From 80af2d7c6a550d63c8456b96e489f574b507e525 Mon Sep 17 00:00:00 2001 From: gir489 <100792176+gir489returns@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:11:49 -0400 Subject: [PATCH] Refactor how blips work (#3473) --- cmake/gtav-classes.cmake | 28 +- src/core/settings.hpp | 2 +- src/gta/blip.hpp | 60 ---- src/gta/enums.hpp | 6 +- src/util/blip.cpp | 84 +++++ src/util/blip.hpp | 82 +---- src/util/teleport.hpp | 2 +- src/views/self/view_custom_teleport.cpp | 422 ++++++++++++------------ 8 files changed, 334 insertions(+), 352 deletions(-) delete mode 100644 src/gta/blip.hpp create mode 100644 src/util/blip.cpp diff --git a/cmake/gtav-classes.cmake b/cmake/gtav-classes.cmake index 8ce2cfef..a3633ba2 100644 --- a/cmake/gtav-classes.cmake +++ b/cmake/gtav-classes.cmake @@ -1,14 +1,14 @@ -include(FetchContent) - -FetchContent_Declare( - gtav_classes - GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git - GIT_TAG 06c58d9c11a9a22336947fbe430d5f4951ff34d7 - GIT_PROGRESS TRUE - CONFIGURE_COMMAND "" - BUILD_COMMAND "" -) -message("GTAV-Classes") -if(NOT gtav_classes_POPULATED) - FetchContent_Populate(gtav_classes) -endif() +include(FetchContent) + +FetchContent_Declare( + gtav_classes + GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git + GIT_TAG a91475c198c59eff04de26929e83d86521299dda + GIT_PROGRESS TRUE + CONFIGURE_COMMAND "" + BUILD_COMMAND "" +) +message("GTAV-Classes") +if(NOT gtav_classes_POPULATED) + FetchContent_Populate(gtav_classes) +endif() diff --git a/src/core/settings.hpp b/src/core/settings.hpp index 3936ce50..fdc1dac1 100644 --- a/src/core/settings.hpp +++ b/src/core/settings.hpp @@ -909,7 +909,7 @@ namespace big float fov = 60.f; float distance = 200.f; int32_t selected_bone = (int32_t)ePedBoneType::HEAD; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(aimbot, enable, only_on_ped_type, only_on_player, only_on_enemy, fov, distance, selected_bone) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(aimbot, enable, only_on_ped_type, only_on_player, only_on_enemy, fov, distance, selected_bone, use_weapon_range) } aimbot{}; struct flying_axe diff --git a/src/gta/blip.hpp b/src/gta/blip.hpp deleted file mode 100644 index 06f3567c..00000000 --- a/src/gta/blip.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "natives.hpp" - -namespace rage -{ -#pragma pack(push, 1) - class Blip_t - { - public: - int32_t m_id; //0x0000 - uint16_t m_blip_array_index;//0x0004 - char pad_0006[4]; //0x0006 - bool m_active; //0x000A - uint8_t N00000197; //0x000B - int32_t m_entity_id; //0x000C - float m_x; //0x0010 - float m_y; //0x0014 - float m_z; //0x0018 - char pad_001C[4]; //0x001C - uint32_t m_display_bits; //0x0020 - uint32_t m_render_bits; //0x0024 - char* m_message; //0x0028 - char pad_0030[8]; //0x0030 - Hash m_description; //0x0038 - char pad_003C[4]; //0x003C - int32_t m_icon; //0x0040 - int16_t m_flash_interval; //0x0044 - int16_t m_flash_timer; //0x0046 - uint32_t m_color; //0x0048 - uint32_t m_secondary_color; //0x004C - float m_scale_x; //0x0050 - float m_scale_y; //0x0054 - float m_rotation; //0x0058 - uint8_t m_mission_bits; //0x005C - uint8_t m_priority; //0x005D - uint8_t m_display_id; //0x005E - uint8_t m_alpha; //0x005F - int8_t m_category; //0x0060 - int8_t m_show_number; //0x0061 - char pad_0062[14]; //0x0062 - }; //Size: 0x0070 - static_assert(sizeof(Blip_t) == 0x70, "Blip_t is not sized properly."); -#pragma pack(pop) - - class BlipEntry - { - public: - Blip_t* m_pBlip;//0x0000 - - };//Size=0x0008 - - class BlipList - { - public: - BlipEntry m_Blips[1500];//0x0000 - char _0x2EE0[56]; - - };//Size=0x2F18 -} \ No newline at end of file diff --git a/src/gta/enums.hpp b/src/gta/enums.hpp index 5109360a..6750db2d 100644 --- a/src/gta/enums.hpp +++ b/src/gta/enums.hpp @@ -1044,8 +1044,12 @@ enum class BlipColors WaypointColor = 0x54 }; -enum BlipDisplayBits +enum BlipDisplayBits : uint32_t { + BlipIsFlashing = (1 << 2), + BlipIsGPSRoute = (1 << 4), + BlipShowHeightMarker = (1 << 5), + BlipsIsDirectional = (1 << 11), BlipShowCheckmark = (1 << 16), BlipShowDollarSign = (1 << 17), BlipShowHeadingIndicator = (1 << 18), diff --git a/src/util/blip.cpp b/src/util/blip.cpp new file mode 100644 index 00000000..686f91d3 --- /dev/null +++ b/src/util/blip.cpp @@ -0,0 +1,84 @@ +#pragma once +#include "blip.hpp" + +namespace big::blip +{ + bool get_blip_location(Vector3& location, int sprite, int color) + { + Blip blip; + for (blip = HUD::GET_CLOSEST_BLIP_INFO_ID(sprite); HUD::DOES_BLIP_EXIST(blip) && color != -1 && HUD::GET_BLIP_COLOUR(blip) != color; blip = HUD::GET_NEXT_BLIP_INFO_ID(sprite)) + ; + + if (!HUD::DOES_BLIP_EXIST(blip) || (color != -1 && HUD::GET_BLIP_COLOUR(blip) != color)) + return false; + + location = HUD::GET_BLIP_COORDS(blip); + + return true; + } + + bool get_objective_location_iteration(Vector3& location, const std::unordered_set sprites, const std::unordered_set blip_colors = {}) + { + for (int i = 0; i < 1500; i++) + { + auto blip = g_pointers->m_gta.m_blip_list->m_Blips[i].m_pBlip; + if (blip != nullptr + && ((sprites.contains((BlipIcons)blip->m_icon) + && (blip_colors.empty() || blip_colors.contains((BlipColors)blip->m_color))) + || (blip->m_display_bits & BlipIsGPSRoute))) + { + location.x = blip->m_position.x; + location.y = blip->m_position.y; + location.z = blip->m_position.z; + return true; + } + } + location = {}; + return false; + } + + bool get_blip_location_from_offset(Vector3& location, int sprite) + { + Blip blip = HUD::GET_CLOSEST_BLIP_INFO_ID(sprite); + if (HUD::DOES_BLIP_EXIST(blip)) + { + location = HUD::GET_BLIP_COORDS(blip); + return true; + } + return false; + } + + bool get_objective_location(Vector3& location) + { + if (get_objective_location_iteration(location, {BlipIcons::Circle}, {BlipColors::YellowMission, BlipColors::YellowMission2, BlipColors::Mission})) + return true; + if (get_objective_location_iteration(location, {BlipIcons::RaceFinish}, {BlipColors::None})) + return true; + if (get_objective_location_iteration(location, {BlipIcons::Circle}, {BlipColors::Green, BlipColors::Blue})) + return true; + if (get_objective_location_iteration(location, {BlipIcons::CrateDrop})) + return true; + + auto blip_icons = {0, 1, 2, 143, 144, 145, 146, 280, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 535, 536, 537, 538, 539, 540, 541, 542}; + for (const auto& icon : blip_icons) + { + if (get_blip_location_from_offset(location, icon)) + return true; + } + + return false; + } + + rage::CBlip* get_selected_blip() + { + for (int i = 0; i < 1500; i++) + { + auto blip = g_pointers->m_gta.m_blip_list->m_Blips[i].m_pBlip; + if (blip && (blip->m_display_bits & BlipIsSelected)) + { + return blip; + } + } + return nullptr; + } +} \ No newline at end of file diff --git a/src/util/blip.hpp b/src/util/blip.hpp index 18f30b39..e26b4bac 100644 --- a/src/util/blip.hpp +++ b/src/util/blip.hpp @@ -1,65 +1,19 @@ -#pragma once -#include "gta/enums.hpp" -#include "natives.hpp" -#include "script.hpp" -#include "pointers.hpp" -#include "ui/blip_t.hpp" -#include "ui/CBlipList.hpp" - -namespace big::blip -{ - inline bool get_blip_location(Vector3& location, int sprite, int color = -1) - { - Blip blip; - for (blip = HUD::GET_FIRST_BLIP_INFO_ID(sprite); HUD::DOES_BLIP_EXIST(blip) && color != -1 && HUD::GET_BLIP_COLOUR(blip) != color; blip = HUD::GET_NEXT_BLIP_INFO_ID(sprite)) - ; - - if (!HUD::DOES_BLIP_EXIST(blip) || (color != -1 && HUD::GET_BLIP_COLOUR(blip) != color)) - return false; - - location = HUD::GET_BLIP_COORDS(blip); - - return true; - } - - inline bool get_objective_location(Vector3& location) - { - if (get_blip_location(location, (int)BlipIcons::Circle, (int)BlipColors::YellowMission)) - return true; - if (get_blip_location(location, (int)BlipIcons::Circle, (int)BlipColors::YellowMission2)) - return true; - if (get_blip_location(location, (int)BlipIcons::Circle, (int)BlipColors::Mission)) - return true; - if (get_blip_location(location, (int)BlipIcons::RaceFinish, (int)BlipColors::None)) - return true; - if (get_blip_location(location, (int)BlipIcons::Circle, (int)BlipColors::Green)) - return true; - if (get_blip_location(location, (int)BlipIcons::Circle, (int)BlipColors::Blue)) - return true; - if (get_blip_location(location, (int)BlipIcons::CrateDrop)) - return true; - - static const int blips[] = {1, 57, 128, 129, 130, 143, 144, 145, 146, 271, 286, 287, 288}; - for (const auto& blip : blips) - { - if (get_blip_location(location, blip, 5)) - return true; - } - - return false; - } - - inline rage::Blip_t* get_selected_blip() - { - for (int i = 0; i < 1500; i++) - { - - auto blip = g_pointers->m_gta.m_blip_list->m_Blips[i].m_pBlip; - if (blip && (blip->m_display_bits & BlipDisplayBits::BlipIsSelected)) - { - return blip; - } - } - return nullptr; - } +#pragma once +#include "gta/enums.hpp" +#include "natives.hpp" +#include "pointers.hpp" +#include "script.hpp" +#include "ui/CBlipList.hpp" + +namespace big::blip +{ + bool get_blip_location(Vector3& location, int sprite, int color = -1); + + bool get_objective_location_iteration(Vector3& location, const std::unordered_set sprites, const std::unordered_set blip_colors); + + bool get_blip_location_from_offset(Vector3& location, int sprite); + + bool get_objective_location(Vector3& location); + + rage::CBlip* get_selected_blip(); } \ No newline at end of file diff --git a/src/util/teleport.hpp b/src/util/teleport.hpp index 2838a9b1..4bfd37d5 100644 --- a/src/util/teleport.hpp +++ b/src/util/teleport.hpp @@ -236,7 +236,7 @@ namespace big::teleport { entity = self::veh; } - ENTITY::SET_ENTITY_COORDS_NO_OFFSET(entity, blip->m_x, blip->m_y, blip->m_z, FALSE, FALSE, TRUE); + ENTITY::SET_ENTITY_COORDS_NO_OFFSET(entity, blip->m_position.x, blip->m_position.y, blip->m_position.z, FALSE, FALSE, TRUE); ENTITY::SET_ENTITY_HEADING(entity, blip->m_rotation); return false; diff --git a/src/views/self/view_custom_teleport.cpp b/src/views/self/view_custom_teleport.cpp index 9fb88866..7deb9c04 100644 --- a/src/views/self/view_custom_teleport.cpp +++ b/src/views/self/view_custom_teleport.cpp @@ -1,211 +1,211 @@ -#include "services/custom_teleport/custom_teleport_service.hpp" -#include "util/math.hpp" -#include "util/teleport.hpp" -#include "util/blip.hpp" -#include "views/view.hpp" - -namespace big -{ - telelocation get_location_player_is_closest_to() - { - if (!g_local_player || !g_local_player->m_navigation || g_custom_teleport_service.all_saved_locations.empty()) - return {}; - - Vector3 transformed_vector = Vector3(g_local_player->m_navigation->get_position()->x, - g_local_player->m_navigation->get_position()->y, - g_local_player->m_navigation->get_position()->z); - - float distance = 500; - telelocation closest_location{}; - //saved_locations_filtered_list can be used to get a joint list of all categories when the filter is empty. - for (auto& loc : g_custom_teleport_service.saved_locations_filtered_list()) - { - float new_distance = math::distance_between_vectors(transformed_vector, {loc.x, loc.y, loc.z}); - - if (new_distance < distance) - closest_location = loc, distance = new_distance; - } - - return closest_location; - } - - float get_distance_to_telelocation(telelocation t) - { - return math::distance_between_vectors(Vector3(t.x,t.y,t.z), Vector3(g_local_player->m_navigation->get_position()->x, - g_local_player->m_navigation->get_position()->y, - g_local_player->m_navigation->get_position()->z)); - } - - void view::custom_teleport() - { - ImGui::BeginGroup(); - static std::string new_location_name{}; - static std::string category = "Default"; - static telelocation deletion_telelocation; - static std::string filter{}; - - if (!std::string(deletion_telelocation.name).empty()) - ImGui::OpenPopup("##deletelocation"); - - if (ImGui::BeginPopupModal("##deletelocation", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) - { - ImGui::Text("VIEW_SELF_ANIMATIONS_ARE_YOU_SURE_DELETE"_T.data(), deletion_telelocation.name); - - ImGui::Spacing(); - - if (ImGui::Button("YES"_T.data())) - { - g_custom_teleport_service.delete_saved_location(category, deletion_telelocation.name); - deletion_telelocation.name = ""; - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("NO"_T.data())) - { - deletion_telelocation.name = ""; - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - - ImGui::PushItemWidth(300); - components::input_text_with_hint("VIEW_SELF_ANIMATIONS_CATEGORY"_T, "VIEW_SELF_ANIMATIONS_CATEGORY_DESC"_T, category); - components::input_text_with_hint("VIEW_SELF_CUSTOM_TELEPORT_LOCATION"_T, "VIEW_SELF_CUSTOM_TELEPORT_LOCATION_DESC"_T, new_location_name); - ImGui::PopItemWidth(); - - components::button("VIEW_SELF_CUSTOM_TELEPORT_SAVE_CURRENT_LOCATION"_T, [] { - if (new_location_name.empty()) - { - g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "VIEW_SELF_CUSTOM_TELEPORT_INVALID_NAME"_T.data()); - } - else if (g_custom_teleport_service.get_saved_location_by_name(new_location_name)) - { - g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), std::vformat("VIEW_SELF_CUSTOM_TELEPORT_LOCATION_ALREADY_EXISTS"_T, std::make_format_args(new_location_name))); - } - else - { - telelocation teleport_location; - Entity teleport_entity = self::ped; - if (self::veh != 0) - teleport_entity = self::veh; - auto coords = ENTITY::GET_ENTITY_COORDS(teleport_entity, TRUE); - teleport_location.name = new_location_name; - teleport_location.x = coords.x; - teleport_location.y = coords.y; - teleport_location.z = coords.z; - teleport_location.yaw = ENTITY::GET_ENTITY_HEADING(teleport_entity); - teleport_location.pitch = CAM::GET_GAMEPLAY_CAM_RELATIVE_PITCH(); - teleport_location.roll = CAM::GET_GAMEPLAY_CAM_RELATIVE_HEADING(); - g_custom_teleport_service.save_new_location(category, teleport_location); - } - }); - ImGui::SameLine(); - components::button("VIEW_SELF_CUSTOM_TELEPORT_SAVE_BLIP"_T, [] { - if (new_location_name.empty()) - { - g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "VIEW_SELF_CUSTOM_TELEPORT_INVALID_NAME"_T.data()); - } - else if (g_custom_teleport_service.get_saved_location_by_name(new_location_name)) - { - g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), std::vformat("VIEW_SELF_CUSTOM_TELEPORT_LOCATION_ALREADY_EXISTS"_T, std::make_format_args(new_location_name))); - } - else if (!*g_pointers->m_gta.m_is_session_started) - { - g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "TELEPORT_NOT_ONLINE"_T.data()); - return; - } - else - { - telelocation teleport_location; - auto blip = blip::get_selected_blip(); - if (blip == nullptr) - { - g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "VIEW_SELF_CUSTOM_TELEPORT_INVALID_BLIP"_T.data()); - return; - } - teleport_location.name = new_location_name; - teleport_location.x = blip->m_x; - teleport_location.y = blip->m_y; - teleport_location.z = blip->m_z; - teleport_location.yaw = blip->m_rotation; - teleport_location.pitch = 0.0f; - teleport_location.roll = 0.0f; - g_custom_teleport_service.save_new_location(category, teleport_location); - } - }); - - ImGui::Separator(); - - components::small_text("VIEW_SELF_CUSTOM_TELEPORT_DOUBLE_CLICK_TO_TELEPORT"_T); - components::small_text("VIEW_SELF_ANIMATIONS_DOUBLE_SHIFT_CLICK_TO_DELETE"_T); - - ImGui::Spacing(); - components::input_text_with_hint("##filter", "SEARCH"_T, filter); - - ImGui::BeginGroup(); - components::small_text("VIEW_SELF_ANIMATIONS_CATEGORIES"_T); - if (ImGui::BeginListBox("##categories", {250, static_cast(*g_pointers->m_gta.m_resolution_y * 0.5)})) - { - for (auto& l : g_custom_teleport_service.all_saved_locations | std::ranges::views::keys) - { - if (ImGui::Selectable(l.data(), l == category)) - { - category = l; - } - } - ImGui::EndListBox(); - } - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::BeginGroup(); - components::small_text("VIEW_SELF_CUSTOM_TELEPORT_LOCATIONS"_T); - if (ImGui::BeginListBox("##telelocations", {250, static_cast(*g_pointers->m_gta.m_resolution_y * 0.5)})) - { - if (g_custom_teleport_service.all_saved_locations.find(category) - != g_custom_teleport_service.all_saved_locations.end()) - { - std::vector current_list{}; - - if (!filter.empty()) - current_list = g_custom_teleport_service.saved_locations_filtered_list(filter); - else - current_list = g_custom_teleport_service.all_saved_locations.at(category); - - for (const auto& l : current_list) - { - if (ImGui::Selectable(l.name.data(), l.name == get_location_player_is_closest_to().name, ImGuiSelectableFlags_AllowDoubleClick)) - { - if (GetAsyncKeyState(VK_SHIFT) & 0x8000) - { - deletion_telelocation = l; - } - else - { - if (ImGui::IsMouseDoubleClicked(0)) - { - g_fiber_pool->queue_job([l] { - teleport::teleport_player_to_coords(g_player_service->get_self(), {l.x, l.y, l.z}, {l.yaw, l.pitch, l.roll}); - }); - } - } - } - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - if (l.name.length() > 27) - ImGui::Text(l.name.data()); - ImGui::Text(std::format("{}: {}", "VIEW_SELF_CUSTOM_TELEPORT_DISTANCE"_T, get_distance_to_telelocation(l)).c_str()); - ImGui::EndTooltip(); - } - } - } - - ImGui::EndListBox(); - } - - ImGui::EndGroup(); - - ImGui::EndGroup(); - } -} +#include "services/custom_teleport/custom_teleport_service.hpp" +#include "util/math.hpp" +#include "util/teleport.hpp" +#include "util/blip.hpp" +#include "views/view.hpp" + +namespace big +{ + telelocation get_location_player_is_closest_to() + { + if (!g_local_player || !g_local_player->m_navigation || g_custom_teleport_service.all_saved_locations.empty()) + return {}; + + Vector3 transformed_vector = Vector3(g_local_player->m_navigation->get_position()->x, + g_local_player->m_navigation->get_position()->y, + g_local_player->m_navigation->get_position()->z); + + float distance = 500; + telelocation closest_location{}; + //saved_locations_filtered_list can be used to get a joint list of all categories when the filter is empty. + for (auto& loc : g_custom_teleport_service.saved_locations_filtered_list()) + { + float new_distance = math::distance_between_vectors(transformed_vector, {loc.x, loc.y, loc.z}); + + if (new_distance < distance) + closest_location = loc, distance = new_distance; + } + + return closest_location; + } + + float get_distance_to_telelocation(telelocation t) + { + return math::distance_between_vectors(Vector3(t.x,t.y,t.z), Vector3(g_local_player->m_navigation->get_position()->x, + g_local_player->m_navigation->get_position()->y, + g_local_player->m_navigation->get_position()->z)); + } + + void view::custom_teleport() + { + ImGui::BeginGroup(); + static std::string new_location_name{}; + static std::string category = "Default"; + static telelocation deletion_telelocation; + static std::string filter{}; + + if (!std::string(deletion_telelocation.name).empty()) + ImGui::OpenPopup("##deletelocation"); + + if (ImGui::BeginPopupModal("##deletelocation", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) + { + ImGui::Text("VIEW_SELF_ANIMATIONS_ARE_YOU_SURE_DELETE"_T.data(), deletion_telelocation.name); + + ImGui::Spacing(); + + if (ImGui::Button("YES"_T.data())) + { + g_custom_teleport_service.delete_saved_location(category, deletion_telelocation.name); + deletion_telelocation.name = ""; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("NO"_T.data())) + { + deletion_telelocation.name = ""; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + ImGui::PushItemWidth(300); + components::input_text_with_hint("VIEW_SELF_ANIMATIONS_CATEGORY"_T, "VIEW_SELF_ANIMATIONS_CATEGORY_DESC"_T, category); + components::input_text_with_hint("VIEW_SELF_CUSTOM_TELEPORT_LOCATION"_T, "VIEW_SELF_CUSTOM_TELEPORT_LOCATION_DESC"_T, new_location_name); + ImGui::PopItemWidth(); + + components::button("VIEW_SELF_CUSTOM_TELEPORT_SAVE_CURRENT_LOCATION"_T, [] { + if (new_location_name.empty()) + { + g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "VIEW_SELF_CUSTOM_TELEPORT_INVALID_NAME"_T.data()); + } + else if (g_custom_teleport_service.get_saved_location_by_name(new_location_name)) + { + g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), std::vformat("VIEW_SELF_CUSTOM_TELEPORT_LOCATION_ALREADY_EXISTS"_T, std::make_format_args(new_location_name))); + } + else + { + telelocation teleport_location; + Entity teleport_entity = self::ped; + if (self::veh != 0) + teleport_entity = self::veh; + auto coords = ENTITY::GET_ENTITY_COORDS(teleport_entity, TRUE); + teleport_location.name = new_location_name; + teleport_location.x = coords.x; + teleport_location.y = coords.y; + teleport_location.z = coords.z; + teleport_location.yaw = ENTITY::GET_ENTITY_HEADING(teleport_entity); + teleport_location.pitch = CAM::GET_GAMEPLAY_CAM_RELATIVE_PITCH(); + teleport_location.roll = CAM::GET_GAMEPLAY_CAM_RELATIVE_HEADING(); + g_custom_teleport_service.save_new_location(category, teleport_location); + } + }); + ImGui::SameLine(); + components::button("VIEW_SELF_CUSTOM_TELEPORT_SAVE_BLIP"_T, [] { + if (new_location_name.empty()) + { + g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "VIEW_SELF_CUSTOM_TELEPORT_INVALID_NAME"_T.data()); + } + else if (g_custom_teleport_service.get_saved_location_by_name(new_location_name)) + { + g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), std::vformat("VIEW_SELF_CUSTOM_TELEPORT_LOCATION_ALREADY_EXISTS"_T, std::make_format_args(new_location_name))); + } + else if (!*g_pointers->m_gta.m_is_session_started) + { + g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "TELEPORT_NOT_ONLINE"_T.data()); + return; + } + else + { + telelocation teleport_location; + auto blip = blip::get_selected_blip(); + if (blip == nullptr) + { + g_notification_service.push_warning("GUI_TAB_CUSTOM_TELEPORT"_T.data(), "VIEW_SELF_CUSTOM_TELEPORT_INVALID_BLIP"_T.data()); + return; + } + teleport_location.name = new_location_name; + teleport_location.x = blip->m_position.x; + teleport_location.y = blip->m_position.y; + teleport_location.z = blip->m_position.z; + teleport_location.yaw = blip->m_rotation; + teleport_location.pitch = 0.0f; + teleport_location.roll = 0.0f; + g_custom_teleport_service.save_new_location(category, teleport_location); + } + }); + + ImGui::Separator(); + + components::small_text("VIEW_SELF_CUSTOM_TELEPORT_DOUBLE_CLICK_TO_TELEPORT"_T); + components::small_text("VIEW_SELF_ANIMATIONS_DOUBLE_SHIFT_CLICK_TO_DELETE"_T); + + ImGui::Spacing(); + components::input_text_with_hint("##filter", "SEARCH"_T, filter); + + ImGui::BeginGroup(); + components::small_text("VIEW_SELF_ANIMATIONS_CATEGORIES"_T); + if (ImGui::BeginListBox("##categories", {250, static_cast(*g_pointers->m_gta.m_resolution_y * 0.5)})) + { + for (auto& l : g_custom_teleport_service.all_saved_locations | std::ranges::views::keys) + { + if (ImGui::Selectable(l.data(), l == category)) + { + category = l; + } + } + ImGui::EndListBox(); + } + ImGui::EndGroup(); + ImGui::SameLine(); + ImGui::BeginGroup(); + components::small_text("VIEW_SELF_CUSTOM_TELEPORT_LOCATIONS"_T); + if (ImGui::BeginListBox("##telelocations", {250, static_cast(*g_pointers->m_gta.m_resolution_y * 0.5)})) + { + if (g_custom_teleport_service.all_saved_locations.find(category) + != g_custom_teleport_service.all_saved_locations.end()) + { + std::vector current_list{}; + + if (!filter.empty()) + current_list = g_custom_teleport_service.saved_locations_filtered_list(filter); + else + current_list = g_custom_teleport_service.all_saved_locations.at(category); + + for (const auto& l : current_list) + { + if (ImGui::Selectable(l.name.data(), l.name == get_location_player_is_closest_to().name, ImGuiSelectableFlags_AllowDoubleClick)) + { + if (GetAsyncKeyState(VK_SHIFT) & 0x8000) + { + deletion_telelocation = l; + } + else + { + if (ImGui::IsMouseDoubleClicked(0)) + { + g_fiber_pool->queue_job([l] { + teleport::teleport_player_to_coords(g_player_service->get_self(), {l.x, l.y, l.z}, {l.yaw, l.pitch, l.roll}); + }); + } + } + } + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + if (l.name.length() > 27) + ImGui::Text(l.name.data()); + ImGui::Text(std::format("{}: {}", "VIEW_SELF_CUSTOM_TELEPORT_DISTANCE"_T, get_distance_to_telelocation(l)).c_str()); + ImGui::EndTooltip(); + } + } + } + + ImGui::EndListBox(); + } + + ImGui::EndGroup(); + + ImGui::EndGroup(); + } +}