diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp index 02e9efa0..f1536e1f 100644 --- a/src/backend/backend.cpp +++ b/src/backend/backend.cpp @@ -54,6 +54,7 @@ namespace big looped::self_police(); looped::self_hud(); looped::self_dance_mode(); + looped::self_persist_outfit(); script::get_current()->yield(); } diff --git a/src/backend/looped/looped.hpp b/src/backend/looped/looped.hpp index 74a0b098..6d3e35ae 100644 --- a/src/backend/looped/looped.hpp +++ b/src/backend/looped/looped.hpp @@ -27,6 +27,7 @@ namespace big static void self_police(); static void self_hud(); static void self_dance_mode(); + static void self_persist_outfit(); static void session_pop_multiplier_areas(); static void session_force_thunder(); diff --git a/src/backend/looped/self/persist_outfit.cpp b/src/backend/looped/self/persist_outfit.cpp new file mode 100644 index 00000000..e3b1e677 --- /dev/null +++ b/src/backend/looped/self/persist_outfit.cpp @@ -0,0 +1,37 @@ +#include "backend/looped/looped.hpp" +#include "services/outfit/outfit_service.hpp" +#include "natives.hpp" +#include "file_manager.hpp" +#include "logger/logger.hpp" + +namespace big +{ + void looped::self_persist_outfit() + { + if (g.self.persist_outfit.empty()) + return; //Off + + if (g_local_player == nullptr || g_local_player->m_player_info == nullptr || g_local_player->m_player_info->m_game_state == eGameState::InMPCutscene || STREAMING::IS_PLAYER_SWITCH_IN_PROGRESS() || DLC::GET_IS_LOADING_SCREEN_ACTIVE()) + return; //Dead or Loading + + if (PED::GET_PED_DRAWABLE_VARIATION(self::ped, 5) == 0 && PED::GET_PED_DRAWABLE_VARIATION(self::ped, 4) >= 14 && PED::GET_PED_DRAWABLE_VARIATION(self::ped, 4) <= 18) + return; //Showering + + if (g.self.persist_outfits_mis && NETWORK::NETWORK_IS_ACTIVITY_SESSION()) + return; //Missioning it up + + static nlohmann::json outfit{}; + static std::string persisting_outfit = ""; + + if (persisting_outfit != g.self.persist_outfit) + { + persisting_outfit = g.self.persist_outfit; + folder saved_outfit_path = g_file_manager.get_project_folder("saved_outfits"); + std::ifstream i(saved_outfit_path.get_file(persisting_outfit).get_path()); + outfit.clear(); + i >> outfit; + } + + outfit_service::apply_outfit(outfit, false); + } +} \ No newline at end of file diff --git a/src/core/settings.hpp b/src/core/settings.hpp index c6d04b88..ac16c407 100644 --- a/src/core/settings.hpp +++ b/src/core/settings.hpp @@ -299,41 +299,43 @@ namespace big NLOHMANN_DEFINE_TYPE_INTRUSIVE(ipls, select) } ipls{}; - bool clean_player = false; - bool force_wanted_level = false; - bool free_cam = false; - bool invisibility = false; - bool local_visibility = true; - bool never_wanted = false; - bool no_ragdoll = false; - bool noclip = false; - bool off_radar = false; - bool ghost_org = false; - bool super_run = false; - bool no_collision = false; - bool unlimited_oxygen = false; - bool no_water_collision = false; - int wanted_level = 0; - bool god_mode = false; - bool part_water = false; - bool proof_bullet = false; - bool proof_fire = false; - bool proof_collision = false; - bool proof_melee = false; - bool proof_explosion = false; - bool proof_steam = false; - bool proof_drown = false; - bool proof_water = false; - uint32_t proof_mask = 0; - bool mobile_radio = false; - bool fast_respawn = false; - bool auto_tp = false; - bool super_jump = false; - bool beast_jump = false; - bool healthregen = false; - float healthregenrate = 1.0f; - bool superman = false; - bool custom_weapon_stop = true; + bool clean_player = false; + bool force_wanted_level = false; + bool free_cam = false; + bool invisibility = false; + bool local_visibility = true; + bool never_wanted = false; + bool no_ragdoll = false; + bool noclip = false; + bool off_radar = false; + bool ghost_org = false; + bool super_run = false; + bool no_collision = false; + bool unlimited_oxygen = false; + bool no_water_collision = false; + int wanted_level = 0; + bool god_mode = false; + bool part_water = false; + bool proof_bullet = false; + bool proof_fire = false; + bool proof_collision = false; + bool proof_melee = false; + bool proof_explosion = false; + bool proof_steam = false; + bool proof_drown = false; + bool proof_water = false; + uint32_t proof_mask = 0; + bool mobile_radio = false; + bool fast_respawn = false; + bool auto_tp = false; + bool super_jump = false; + bool beast_jump = false; + bool healthregen = false; + float healthregenrate = 1.0f; + bool superman = false; + bool custom_weapon_stop = true; + std::string persist_outfit = ""; + bool persist_outfits_mis = false; struct hud { bool color_override = false; @@ -353,7 +355,7 @@ namespace big // do not save below entries bool dance_mode = false; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(self, ipls, ptfx_effects, clean_player, force_wanted_level, free_cam, invisibility, local_visibility, never_wanted, no_ragdoll, noclip, off_radar, super_run, no_collision, unlimited_oxygen, no_water_collision, wanted_level, god_mode, part_water, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_drown, proof_water, proof_mask, mobile_radio, fast_respawn, auto_tp, super_jump, beast_jump, healthregen, healthregenrate, hud, superman, custom_weapon_stop) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(self, ipls, ptfx_effects, clean_player, force_wanted_level, free_cam, invisibility, local_visibility, never_wanted, no_ragdoll, noclip, off_radar, super_run, no_collision, unlimited_oxygen, no_water_collision, wanted_level, god_mode, part_water, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_drown, proof_water, proof_mask, mobile_radio, fast_respawn, auto_tp, super_jump, beast_jump, healthregen, healthregenrate, hud, superman, custom_weapon_stop, persist_outfit) } self{}; struct session diff --git a/src/services/custom_teleport/custom_teleport_service.hpp b/src/services/custom_teleport/custom_teleport_service.hpp index 9b3258cd..c7462c0f 100644 --- a/src/services/custom_teleport/custom_teleport_service.hpp +++ b/src/services/custom_teleport/custom_teleport_service.hpp @@ -7,9 +7,10 @@ namespace big { std::string name; float x, y, z; + float yaw = 0.0f, pitch = 0.0f, roll = 0.0f; }; - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(telelocation, name, x, y, z); + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(telelocation, name, x, y, z, yaw, pitch, roll); class custom_teleport_service { diff --git a/src/services/outfit/outfit_service.cpp b/src/services/outfit/outfit_service.cpp new file mode 100644 index 00000000..4d9d84fe --- /dev/null +++ b/src/services/outfit/outfit_service.cpp @@ -0,0 +1,115 @@ +#include "util/outfit.hpp" +#include "outfit_service.hpp" +#include "natives.hpp" + +namespace big +{ + void outfit_service::apply_outfit(nlohmann::json j, bool set_parachute) + { + bool was_components_set = false; + for (auto& item : j["components"].items()) + { + std::stringstream ss(item.key()); + int id = 0; + ss >> id; + + if (!set_parachute && id == 5) + continue; + + int drawable_id = item.value()["drawable_id"]; + int texture_id = item.value()["texture_id"]; + if (PED::GET_PED_DRAWABLE_VARIATION(self::ped, id) != drawable_id || PED::GET_PED_TEXTURE_VARIATION(self::ped, id) != texture_id) + { + was_components_set = true; + PED::SET_PED_COMPONENT_VARIATION(self::ped, id, drawable_id, texture_id, PED::GET_PED_PALETTE_VARIATION(self::ped, id)); + if (PED::GET_PED_DRAWABLE_VARIATION(self::ped, id) != drawable_id || PED::GET_PED_TEXTURE_VARIATION(self::ped, id) != texture_id) //Run it again Tony to make sure it actually changed. + was_components_set = false; + } + } + for (auto& item : j["props"].items()) + { + std::stringstream ss(item.key()); + int id = 0; + ss >> id; + int drawable_id = item.value()["drawable_id"]; + int texture_id = item.value()["texture_id"]; + if (drawable_id == -1 && PED::GET_PED_PROP_INDEX(self::ped, id, 1) != -1) + PED::CLEAR_PED_PROP(self::ped, id, 1); + else if (PED::GET_PED_PROP_INDEX(self::ped, id, 1) != drawable_id || PED::GET_PED_PROP_TEXTURE_INDEX(self::ped, id) != texture_id) + { + was_components_set = true; + PED::SET_PED_PROP_INDEX(self::ped, id, drawable_id, texture_id, NETWORK::NETWORK_IS_GAME_IN_PROGRESS(), 1); + if (id == 0) + { + //Prevent player ped from taking helmet off. + PED::SET_PED_HELMET_PROP_INDEX(self::ped, drawable_id, 0); + PED::SET_PED_HELMET_TEXTURE_INDEX(self::ped, texture_id); + PED::SET_PED_CONFIG_FLAG(self::ped, 34, true); + if (!PED::IS_PED_ON_ANY_BIKE(self::ped)) + PED::SET_PED_CONFIG_FLAG(self::ped, 36, true); + } + } + } + if (j.contains("blend_data") && was_components_set) + { + head_blend_data blend_data = j["blend_data"]; + PED::SET_PED_HEAD_BLEND_DATA(self::ped, blend_data.shape_first_id, blend_data.shape_second_id, + blend_data.shape_third_id, blend_data.skin_first_id, blend_data.skin_second_id, blend_data.skin_third_id, + blend_data.shape_mix, blend_data.skin_mix, blend_data.third_mix, blend_data.is_parent); + } + } + void outfit_service::save_outfit(std::string filename) + { + outfit::components_t components; + outfit::props_t props; + + for (auto& item : components.items) + { + item.drawable_id = PED::GET_PED_DRAWABLE_VARIATION(self::ped, item.id); + item.drawable_id_max = PED::GET_NUMBER_OF_PED_DRAWABLE_VARIATIONS(self::ped, item.id) - 1; + + item.texture_id = PED::GET_PED_TEXTURE_VARIATION(self::ped, item.id); + item.texture_id_max = PED::GET_NUMBER_OF_PED_TEXTURE_VARIATIONS(self::ped, item.id, item.drawable_id) - 1; + } + + for (auto& item : props.items) + { + item.drawable_id = PED::GET_PED_PROP_INDEX(self::ped, item.id, 1); + item.drawable_id_max = PED::GET_NUMBER_OF_PED_PROP_DRAWABLE_VARIATIONS(self::ped, item.id) - 1; + + item.texture_id = PED::GET_PED_PROP_TEXTURE_INDEX(self::ped, item.id); + item.texture_id_max = PED::GET_NUMBER_OF_PED_PROP_TEXTURE_VARIATIONS(self::ped, item.id, item.drawable_id) - 1; + } + + nlohmann::json j; + nlohmann::json j_components; + nlohmann::json j_props; + + for (auto& item : components.items) + { + nlohmann::json tmp; + tmp["drawable_id"] = item.drawable_id; + tmp["texture_id"] = item.texture_id; + j_components[std::to_string(item.id)] = tmp; + } + + for (auto& item : props.items) + { + nlohmann::json tmp; + tmp["drawable_id"] = item.drawable_id; + tmp["texture_id"] = item.texture_id; + j_props[std::to_string(item.id)] = tmp; + } + + head_blend_data blend_data{}; + PED::GET_PED_HEAD_BLEND_DATA(self::ped, (Any*)&blend_data); + + j["components"] = j_components; + j["props"] = j_props; + j["blend_data"] = blend_data; + + static folder saved_outfit_path = g_file_manager.get_project_folder("saved_outfits"); + std::ofstream o(saved_outfit_path.get_file(filename).get_path()); + o << std::setw(4) << j << std::endl; + } +} \ No newline at end of file diff --git a/src/services/outfit/outfit_service.hpp b/src/services/outfit/outfit_service.hpp new file mode 100644 index 00000000..a73fcfc6 --- /dev/null +++ b/src/services/outfit/outfit_service.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace big +{ + + class outfit_service + { + public: + struct head_blend_data + { + public: + alignas(8) int shape_first_id = -1; + alignas(8) int shape_second_id = -1; + alignas(8) int shape_third_id = -1; + alignas(8) int skin_first_id = -1; + alignas(8) int skin_second_id = -1; + alignas(8) int skin_third_id = -1; + alignas(8) float shape_mix = FLT_MAX; + alignas(8) float skin_mix = FLT_MAX; + alignas(8) float third_mix = FLT_MAX; + alignas(8) BOOL is_parent = FALSE; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(head_blend_data, shape_first_id, shape_second_id, shape_third_id, skin_first_id, skin_second_id, skin_third_id, shape_mix, skin_mix, third_mix, is_parent); + }; + static_assert(sizeof(head_blend_data) == 0x50, "head_blend_data is not sized properly."); + + static void apply_outfit(nlohmann::json, bool); + static void save_outfit(std::string); + }; +} \ No newline at end of file diff --git a/src/util/teleport.hpp b/src/util/teleport.hpp index e8dcebc3..fe3215ce 100644 --- a/src/util/teleport.hpp +++ b/src/util/teleport.hpp @@ -8,7 +8,7 @@ namespace big::teleport { - inline bool teleport_player_to_coords(player_ptr player, Vector3 coords) + inline bool teleport_player_to_coords(player_ptr player, Vector3 coords, Vector3 euler = {0, 0, 0}) { Entity ent; @@ -17,7 +17,9 @@ namespace big::teleport else ent = PLAYER::PLAYER_PED_ID(); - if (ent == self::ped || ent == self::veh) + bool is_local_player = (ent == self::ped || ent == self::veh); + + if (is_local_player) PED::SET_PED_COORDS_KEEP_VEHICLE(ent, coords.x, coords.y, coords.z); if (ENTITY::IS_ENTITY_DEAD(ent, true)) @@ -31,9 +33,22 @@ namespace big::teleport ent = PED::GET_VEHICLE_PED_IS_IN(ent, false); if (entity::take_control_of(ent)) - ENTITY::SET_ENTITY_COORDS(ent, coords.x, coords.y, coords.z, 0, 0, 0, 0); + { + ENTITY::SET_ENTITY_COORDS_NO_OFFSET(ent, coords.x, coords.y, coords.z, TRUE, TRUE, TRUE); + if (euler.x + euler.y + euler.z != 0.0f) + { + ENTITY::SET_ENTITY_HEADING(ent, euler.x); + if (is_local_player) + { + CAM::SET_GAMEPLAY_CAM_RELATIVE_PITCH(euler.y, 1.f); + CAM::SET_GAMEPLAY_CAM_RELATIVE_HEADING(euler.z); + } + } + } else + { g_notification_service->push_warning("TELEPORT"_T.data(), "TELEPORT_FAILED_TO_TAKE_CONTROL"_T.data()); + } return true; } @@ -49,6 +64,13 @@ namespace big::teleport g.m_remote_player_teleports.emplace(g_pointers->m_gta.m_handle_to_ptr(hnd)->m_net_object->m_object_id, remote_tp); + if (is_local_player) + { + ENTITY::SET_ENTITY_HEADING(ent, euler.x); + CAM::SET_GAMEPLAY_CAM_RELATIVE_PITCH(euler.y, 1.f); + CAM::SET_GAMEPLAY_CAM_RELATIVE_HEADING(euler.z); + } + if ((player->is_valid() && PED::IS_PED_IN_ANY_VEHICLE(PLAYER::GET_PLAYER_PED_SCRIPT_INDEX(player->id()), false)) || PLAYER::IS_REMOTE_PLAYER_IN_NON_CLONED_VEHICLE(player->id())) g_pointers->m_gta.m_clear_ped_tasks_network(player->get_ped(), true); diff --git a/src/views/self/view_custom_teleport.cpp b/src/views/self/view_custom_teleport.cpp index a86e9d69..ddea06dc 100644 --- a/src/views/self/view_custom_teleport.cpp +++ b/src/views/self/view_custom_teleport.cpp @@ -77,8 +77,21 @@ namespace big if (g_custom_teleport_service.get_saved_location_by_name(new_location_name)) g_notification_service->push_warning("Custom Teleport", std::format("Location with the name {} already exists", new_location_name)); else - g_custom_teleport_service.save_new_location(category, - {new_location_name, self::pos.x, self::pos.y, self::pos.z}); + { + 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::Separator(); @@ -130,7 +143,7 @@ namespace big 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}); + teleport::teleport_player_to_coords(g_player_service->get_self(), {l.x, l.y, l.z}, {l.yaw, l.pitch, l.roll}); }); } } diff --git a/src/views/self/view_outfit_editor.cpp b/src/views/self/view_outfit_editor.cpp index 6189ea1f..70f9216d 100644 --- a/src/views/self/view_outfit_editor.cpp +++ b/src/views/self/view_outfit_editor.cpp @@ -3,6 +3,7 @@ #include "util/outfit.hpp" #include "util/ped.hpp" #include "views/view.hpp" +#include "services/outfit/outfit_service.hpp" namespace big { @@ -85,7 +86,18 @@ namespace big if (drawable_id == -1) PED::CLEAR_PED_PROP(self::ped, id, 1); else + { PED::SET_PED_PROP_INDEX(self::ped, id, drawable_id, texture_id, TRUE, 1); + if (id == 0) + { + //Prevent ped from taking helmet off. + PED::SET_PED_HELMET_PROP_INDEX(self::ped, drawable_id, 0); + PED::SET_PED_HELMET_TEXTURE_INDEX(self::ped, texture_id); + PED::SET_PED_CONFIG_FLAG(self::ped, 34, true); + if (!PED::IS_PED_ON_ANY_BIKE(self::ped)) + PED::SET_PED_CONFIG_FLAG(self::ped, 36, true); + } + } } }); @@ -173,36 +185,12 @@ namespace big ImGui::SameLine(); components::button("OUTFIT_SAVE_CURRENT"_T, [] { - nlohmann::json j; - nlohmann::json j_components; - nlohmann::json j_props; - - for (auto& item : components.items) - { - nlohmann::json tmp; - tmp["drawable_id"] = item.drawable_id; - tmp["texture_id"] = item.texture_id; - j_components[std::to_string(item.id)] = tmp; - } - - for (auto& item : props.items) - { - nlohmann::json tmp; - tmp["drawable_id"] = item.drawable_id; - tmp["texture_id"] = item.texture_id; - j_props[std::to_string(item.id)] = tmp; - } - - j["components"] = j_components; - j["props"] = j_props; - size_t index = 0; std::string str = outfit_name; while (saved_outfit_path.get_file(str + ".json").exists()) str = std::format("{}({})", outfit_name, ++index); - std::ofstream o(saved_outfit_path.get_file(str + ".json").get_path()); - o << std::setw(4) << j << std::endl; + outfit_service::save_outfit(str + ".json"); }); ImGui::SameLine(); @@ -213,28 +201,7 @@ namespace big nlohmann::json j; i >> j; - for (auto& item : j["components"].items()) - { - std::stringstream ss(item.key()); - int id = 0; - ss >> id; - int drawable_id = item.value()["drawable_id"]; - int texture_id = item.value()["texture_id"]; - PED::SET_PED_COMPONENT_VARIATION(self::ped, id, drawable_id, texture_id, PED::GET_PED_PALETTE_VARIATION(self::ped, id)); - } - - for (auto& item : j["props"].items()) - { - std::stringstream ss(item.key()); - int id = 0; - ss >> id; - int drawable_id = item.value()["drawable_id"]; - int texture_id = item.value()["texture_id"]; - if (drawable_id == -1) - PED::CLEAR_PED_PROP(self::ped, id, 1); - else - PED::SET_PED_PROP_INDEX(self::ped, id, drawable_id, texture_id, TRUE, 1); - } + outfit_service::apply_outfit(j, true); } }); ImGui::SameLine(); @@ -255,5 +222,20 @@ namespace big selected_index = i; ImGui::EndListBox(); } + ImGui::SameLine(); + ImGui::BeginGroup(); + if (ImGui::Button("Set Persist Outfit")) + { + g.self.persist_outfit = saved_outfits[selected_index]; + } + ImGui::SameLine(); + if (ImGui::Button("Clear Persist Outfit")) + { + g.self.persist_outfit.clear(); + } + ImGui::SameLine(); + ImGui::Checkbox("Disable During Missions?", &g.self.persist_outfits_mis); + ImGui::Text(std::format("Current Persisted Outfit: {}", g.self.persist_outfit).c_str()); + ImGui::EndGroup(); } } \ No newline at end of file