refactor!: Replace premake5 with CMake. (#551)
Co-authored-by: tupoy-ya <tupoy-ya@users.noreply.github.com>
This commit is contained in:
349
src/services/context_menu/context_menu_service.cpp
Normal file
349
src/services/context_menu/context_menu_service.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
#include "context_menu_service.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "gta/replay.hpp"
|
||||
#include "gui.hpp"
|
||||
#include "util/misc.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
context_menu_service::context_menu_service()
|
||||
{
|
||||
g_context_menu_service = this;
|
||||
load_shared();
|
||||
}
|
||||
|
||||
context_menu_service::~context_menu_service()
|
||||
{
|
||||
g_context_menu_service = nullptr;
|
||||
}
|
||||
|
||||
void context_menu_service::fill_model_bounding_box_screen_space()
|
||||
{
|
||||
Vector3 forward, right, up, pos;
|
||||
ENTITY::GET_ENTITY_MATRIX(m_handle, &forward, &right, &up, &pos);
|
||||
|
||||
const auto hash = ENTITY::GET_ENTITY_MODEL(m_handle);
|
||||
Vector3 min, max;
|
||||
MISC::GET_MODEL_DIMENSIONS(hash, &min, &max);
|
||||
const auto dimensions = (max - min) * 0.5f;
|
||||
|
||||
const auto& position = m_pointer->m_position;
|
||||
|
||||
rage::fvector3 front_upper_right, back_lower_left;
|
||||
front_upper_right.x = position.x + dimensions.y * forward.x + dimensions.x * right.x + dimensions.z * up.x;
|
||||
front_upper_right.y = position.y + dimensions.y * forward.y + dimensions.x * right.y + dimensions.z * up.y;
|
||||
front_upper_right.z = position.z + dimensions.y * forward.z + dimensions.x * right.z + dimensions.z * up.z;
|
||||
|
||||
back_lower_left.x = position.x - dimensions.y * forward.x - dimensions.x * right.x - dimensions.z * up.x;
|
||||
back_lower_left.y = position.y - dimensions.y * forward.y - dimensions.x * right.y - dimensions.z * up.y;
|
||||
back_lower_left.z = position.z - dimensions.y * forward.z - dimensions.x * right.z - dimensions.z * up.z;
|
||||
|
||||
rage::fvector3 edge1 = back_lower_left;
|
||||
rage::fvector3 edge2, edge3, edge4;
|
||||
|
||||
rage::fvector3 edge5 = front_upper_right;
|
||||
rage::fvector3 edge6, edge7, edge8;
|
||||
|
||||
edge2.x = edge1.x + 2 * dimensions.y * forward.x;
|
||||
edge2.y = edge1.y + 2 * dimensions.y * forward.y;
|
||||
edge2.z = edge1.z + 2 * dimensions.y * forward.z;
|
||||
|
||||
edge3.x = edge2.x + 2 * dimensions.z * up.x;
|
||||
edge3.y = edge2.y + 2 * dimensions.z * up.y;
|
||||
edge3.z = edge2.z + 2 * dimensions.z * up.z;
|
||||
|
||||
edge4.x = edge1.x + 2 * dimensions.z * up.x;
|
||||
edge4.y = edge1.y + 2 * dimensions.z * up.y;
|
||||
edge4.z = edge1.z + 2 * dimensions.z * up.z;
|
||||
|
||||
edge6.x = edge5.x - 2 * dimensions.y * forward.x;
|
||||
edge6.y = edge5.y - 2 * dimensions.y * forward.y;
|
||||
edge6.z = edge5.z - 2 * dimensions.y * forward.z;
|
||||
|
||||
edge7.x = edge6.x - 2 * dimensions.z * up.x;
|
||||
edge7.y = edge6.y - 2 * dimensions.z * up.y;
|
||||
edge7.z = edge6.z - 2 * dimensions.z * up.z;
|
||||
|
||||
edge8.x = edge5.x - 2 * dimensions.z * up.x;
|
||||
edge8.y = edge5.y - 2 * dimensions.z * up.y;
|
||||
edge8.z = edge5.z - 2 * dimensions.z * up.z;
|
||||
|
||||
auto any_fail = false;
|
||||
static auto imgui_world_to_screen = [&any_fail](rage::fvector3& world_input, ImVec2& screen_result)
|
||||
{
|
||||
if (any_fail)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto success = GRAPHICS::GET_SCREEN_COORD_FROM_WORLD_COORD(world_input.x, world_input.y, world_input.z, &screen_result.x, &screen_result.y);
|
||||
if (success)
|
||||
{
|
||||
screen_result.x = static_cast<float>(*g_pointers->m_resolution_x) * screen_result.x;
|
||||
screen_result.y = static_cast<float>(*g_pointers->m_resolution_y) * screen_result.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
any_fail = true;
|
||||
}
|
||||
};
|
||||
|
||||
auto& box = m_model_bounding_box_screen_space;
|
||||
imgui_world_to_screen(edge1, box.edge1);
|
||||
imgui_world_to_screen(edge2, box.edge2);
|
||||
imgui_world_to_screen(edge3, box.edge3);
|
||||
imgui_world_to_screen(edge4, box.edge4);
|
||||
|
||||
imgui_world_to_screen(edge5, box.edge5);
|
||||
imgui_world_to_screen(edge6, box.edge6);
|
||||
imgui_world_to_screen(edge7, box.edge7);
|
||||
imgui_world_to_screen(edge8, box.edge8);
|
||||
|
||||
if (any_fail)
|
||||
{
|
||||
box = {};
|
||||
}
|
||||
}
|
||||
|
||||
double context_menu_service::distance_to_middle_of_screen(const rage::fvector2& screen_pos)
|
||||
{
|
||||
double cumulative_distance{};
|
||||
|
||||
if (screen_pos.x > 0.5)
|
||||
cumulative_distance += screen_pos.x - 0.5;
|
||||
else
|
||||
cumulative_distance += 0.5 - screen_pos.x;
|
||||
|
||||
if (screen_pos.y > 0.5)
|
||||
cumulative_distance += screen_pos.y - 0.5;
|
||||
else
|
||||
cumulative_distance += 0.5 - screen_pos.y;
|
||||
|
||||
return cumulative_distance;
|
||||
}
|
||||
|
||||
s_context_menu* context_menu_service::get_context_menu()
|
||||
{
|
||||
if (m_pointer && m_pointer->m_model_info)
|
||||
{
|
||||
switch (m_pointer->m_model_info->m_model_type)
|
||||
{
|
||||
case eModelType::Object:
|
||||
{
|
||||
if (!misc::has_bits_set(&g->context_menu.allowed_entity_types, static_cast<uint8_t>(ContextEntityType::OBJECT)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
return &options.at(ContextEntityType::OBJECT);
|
||||
}
|
||||
case eModelType::Ped:
|
||||
{
|
||||
if (const auto ped = reinterpret_cast<CPed*>(m_pointer); ped)
|
||||
{
|
||||
if (ped->m_ped_task_flag & static_cast<uint8_t>(ePedTask::TASK_DRIVING) &&
|
||||
ped->m_vehicle)
|
||||
{
|
||||
if (!misc::has_bits_set(&g->context_menu.allowed_entity_types, static_cast<uint8_t>(ContextEntityType::VEHICLE)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
m_pointer = ped->m_vehicle;
|
||||
return &options.at(ContextEntityType::VEHICLE);
|
||||
}
|
||||
if (ped->m_player_info)
|
||||
{
|
||||
if (!misc::has_bits_set(&g->context_menu.allowed_entity_types, static_cast<uint8_t>(ContextEntityType::PLAYER)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
return &options.at(ContextEntityType::PLAYER);
|
||||
}
|
||||
}
|
||||
|
||||
if (!misc::has_bits_set(&g->context_menu.allowed_entity_types, static_cast<uint8_t>(ContextEntityType::PED)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
return &options.at(ContextEntityType::PED);
|
||||
}
|
||||
case eModelType::Vehicle:
|
||||
{
|
||||
if (!misc::has_bits_set(&g->context_menu.allowed_entity_types, static_cast<uint8_t>(ContextEntityType::VEHICLE)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
return &options.at(ContextEntityType::VEHICLE);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void context_menu_service::get_entity_closest_to_screen_center()
|
||||
{
|
||||
if (const auto replay = *g_pointers->m_replay_interface; replay)
|
||||
{
|
||||
const auto veh_interface = replay->m_vehicle_interface;
|
||||
const auto ped_interface = replay->m_ped_interface;
|
||||
const auto obj_interface = replay->m_object_interface;
|
||||
|
||||
if (veh_interface && ped_interface && obj_interface)
|
||||
{
|
||||
const auto veh_interface_size = veh_interface->m_max_vehicles;
|
||||
const auto ped_interface_size = ped_interface->m_max_peds;
|
||||
const auto obj_interface_size = obj_interface->m_max_objects;
|
||||
const auto all_entities = std::make_unique<rage::CEntityHandle[]>(veh_interface_size + ped_interface_size + obj_interface_size);
|
||||
|
||||
const auto ptr = all_entities.get();
|
||||
std::uint32_t offset = 0;
|
||||
std::copy(ped_interface->m_ped_list->m_peds, ped_interface->m_ped_list->m_peds + ped_interface_size, ptr);
|
||||
offset += ped_interface_size;
|
||||
|
||||
std::copy(veh_interface->m_vehicle_list->m_vehicles, veh_interface->m_vehicle_list->m_vehicles + veh_interface_size, ptr + offset);
|
||||
offset += veh_interface_size;
|
||||
|
||||
std::copy(obj_interface->m_object_list->m_objects, obj_interface->m_object_list->m_objects + obj_interface_size, ptr + offset);
|
||||
offset += obj_interface_size;
|
||||
|
||||
double distance = 1;
|
||||
bool got_an_entity = false;
|
||||
rage::fvector2 screen_pos{};
|
||||
for (std::uint32_t i = 0; i < offset; i++)
|
||||
{
|
||||
if (!all_entities[i].m_entity_ptr)
|
||||
continue;
|
||||
|
||||
const auto temp_pointer = all_entities[i].m_entity_ptr;
|
||||
const auto temp_handle = g_pointers->m_ptr_to_handle(temp_pointer);
|
||||
if (!temp_pointer->m_navigation)
|
||||
continue;
|
||||
|
||||
const auto pos = temp_pointer->m_navigation->m_position;
|
||||
HUD::GET_HUD_SCREEN_POSITION_FROM_WORLD_POSITION(pos.x, pos.y, pos.z, &screen_pos.x, &screen_pos.y);
|
||||
if (distance_to_middle_of_screen(screen_pos) < distance &&
|
||||
ENTITY::HAS_ENTITY_CLEAR_LOS_TO_ENTITY(PLAYER::PLAYER_PED_ID(), temp_handle, 17) &&
|
||||
temp_handle != PLAYER::PLAYER_PED_ID())
|
||||
{
|
||||
m_handle = temp_handle;
|
||||
m_pointer = temp_pointer;
|
||||
distance = distance_to_middle_of_screen(screen_pos);
|
||||
got_an_entity = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (got_an_entity)
|
||||
{
|
||||
fill_model_bounding_box_screen_space();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void context_menu_service::load_shared()
|
||||
{
|
||||
for (auto& [type, menu] : options)
|
||||
{
|
||||
if (type == ContextEntityType::SHARED)
|
||||
continue;
|
||||
menu.options.insert(menu.options.end(), options.at(ContextEntityType::SHARED).options.begin(), options.at(ContextEntityType::SHARED).options.end());
|
||||
|
||||
std::uint32_t max_size = 0;
|
||||
for (auto& [name, _] : menu.options)
|
||||
{
|
||||
max_size = static_cast<int>(max_size < name.length() ? name.length() : max_size);
|
||||
}
|
||||
|
||||
menu.menu_size = { (10.f * static_cast<float>(max_size)) + 10.f , 2 * (10.f * static_cast<float>(menu.options.size())) + 10.f };
|
||||
}
|
||||
}
|
||||
|
||||
static const ControllerInputs controls[] =
|
||||
{
|
||||
ControllerInputs::INPUT_NEXT_WEAPON,
|
||||
ControllerInputs::INPUT_PREV_WEAPON,
|
||||
ControllerInputs::INPUT_VEH_NEXT_RADIO,
|
||||
ControllerInputs::INPUT_VEH_SELECT_NEXT_WEAPON,
|
||||
ControllerInputs::INPUT_SELECT_NEXT_WEAPON,
|
||||
ControllerInputs::INPUT_SELECT_PREV_WEAPON,
|
||||
ControllerInputs::INPUT_WEAPON_WHEEL_NEXT,
|
||||
ControllerInputs::INPUT_WEAPON_WHEEL_PREV,
|
||||
ControllerInputs::INPUT_ATTACK,
|
||||
ControllerInputs::INPUT_SPECIAL_ABILITY,
|
||||
ControllerInputs::INPUT_VEH_MOUSE_CONTROL_OVERRIDE,
|
||||
};
|
||||
|
||||
void context_menu_service::disable_control_action_loop()
|
||||
{
|
||||
if (g_context_menu_service->enabled)
|
||||
{
|
||||
for (const auto& control : controls)
|
||||
PAD::DISABLE_CONTROL_ACTION(0, static_cast<int>(control), true);
|
||||
}
|
||||
}
|
||||
|
||||
void context_menu_service::context_menu()
|
||||
{
|
||||
while (g_running)
|
||||
{
|
||||
if (!g->context_menu.enabled)
|
||||
{
|
||||
g_context_menu_service->enabled = false;
|
||||
|
||||
script::get_current()->yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_gui.m_opened)
|
||||
{
|
||||
script::get_current()->yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PAD::IS_DISABLED_CONTROL_JUST_RELEASED(0, (int)ControllerInputs::INPUT_VEH_DUCK))
|
||||
{
|
||||
g_context_menu_service->enabled = !g_context_menu_service->enabled;
|
||||
}
|
||||
|
||||
if (g_context_menu_service->enabled)
|
||||
{
|
||||
HUD::SHOW_HUD_COMPONENT_THIS_FRAME(14 /*RETICLE*/);
|
||||
|
||||
g_context_menu_service->get_entity_closest_to_screen_center();
|
||||
|
||||
const auto cm = g_context_menu_service->get_context_menu();
|
||||
if (cm == nullptr)
|
||||
{
|
||||
script::get_current()->yield();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PAD::IS_DISABLED_CONTROL_JUST_PRESSED(0, (int)ControllerInputs::INPUT_WEAPON_WHEEL_NEXT))
|
||||
cm->current_option = cm->options.size() <= cm->current_option + 1 ? 0 : cm->current_option + 1;
|
||||
if (PAD::IS_DISABLED_CONTROL_JUST_PRESSED(0, (int)ControllerInputs::INPUT_WEAPON_WHEEL_PREV))
|
||||
cm->current_option = 0 > cm->current_option - 1 ? static_cast<int>(cm->options.size()) - 1 : cm->current_option - 1;
|
||||
|
||||
if (PAD::IS_DISABLED_CONTROL_JUST_PRESSED(0, (int)ControllerInputs::INPUT_ATTACK) ||
|
||||
PAD::IS_DISABLED_CONTROL_JUST_PRESSED(0, (int)ControllerInputs::INPUT_SPECIAL_ABILITY))
|
||||
{
|
||||
if (!g_context_menu_service->m_pointer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cm->options.at(cm->current_option).command();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script::get_current()->yield();
|
||||
}
|
||||
}
|
||||
}
|
119
src/services/context_menu/context_menu_service.hpp
Normal file
119
src/services/context_menu/context_menu_service.hpp
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
#include "natives.hpp"
|
||||
#include "util/entity.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "util/teleport.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
struct context_option
|
||||
{
|
||||
std::string name;
|
||||
std::function<void()> command;
|
||||
};
|
||||
|
||||
struct s_context_menu
|
||||
{
|
||||
ContextEntityType type;
|
||||
int current_option = 0;
|
||||
ImVec2 menu_size = {};
|
||||
std::vector<context_option> options;
|
||||
};
|
||||
|
||||
struct model_bounding_box_screen_space
|
||||
{
|
||||
ImVec2 edge1, edge2, edge3, edge4;
|
||||
ImVec2 edge5, edge6, edge7, edge8;
|
||||
};
|
||||
|
||||
class context_menu_service final
|
||||
{
|
||||
private:
|
||||
void fill_model_bounding_box_screen_space();
|
||||
static double distance_to_middle_of_screen(const rage::fvector2& screen_pos);
|
||||
|
||||
public:
|
||||
context_menu_service();
|
||||
~context_menu_service();
|
||||
|
||||
context_menu_service(const context_menu_service&) = delete;
|
||||
context_menu_service(context_menu_service&&) noexcept = delete;
|
||||
context_menu_service& operator=(const context_menu_service&) = delete;
|
||||
context_menu_service& operator=(context_menu_service&&) noexcept = delete;
|
||||
|
||||
bool enabled = false;
|
||||
s_context_menu* get_context_menu();
|
||||
void get_entity_closest_to_screen_center();
|
||||
void load_shared();
|
||||
|
||||
static void disable_control_action_loop();
|
||||
static void context_menu();
|
||||
|
||||
Entity m_handle;
|
||||
rage::fwEntity* m_pointer;
|
||||
model_bounding_box_screen_space m_model_bounding_box_screen_space;
|
||||
|
||||
s_context_menu vehicle_menu{
|
||||
ContextEntityType::VEHICLE,
|
||||
0,{}, {
|
||||
{"KILL ENGINE", [this] {
|
||||
if (entity::take_control_of(m_handle))
|
||||
{
|
||||
VEHICLE::SET_VEHICLE_ENGINE_HEALTH(m_handle, 0.f);
|
||||
VEHICLE::SET_VEHICLE_ENGINE_ON(m_handle, false, true, false);
|
||||
}
|
||||
}},
|
||||
{"DELETE", [this] {
|
||||
if (entity::take_control_of(m_handle))
|
||||
{
|
||||
entity::delete_entity(m_handle);
|
||||
}
|
||||
}},
|
||||
{ "TP INTO", [this] {
|
||||
teleport::into_vehicle(m_handle);
|
||||
}}
|
||||
} };
|
||||
|
||||
s_context_menu ped_menu{
|
||||
ContextEntityType::PED,
|
||||
0,{}, {}};
|
||||
|
||||
s_context_menu object_menu{
|
||||
ContextEntityType::OBJECT,
|
||||
0,{}, {}};
|
||||
|
||||
s_context_menu player_menu{
|
||||
ContextEntityType::PLAYER,
|
||||
0,{}, {
|
||||
{"STEAL IDENTITY", [this]
|
||||
{
|
||||
ped::steal_identity(m_handle);
|
||||
}}
|
||||
} };
|
||||
|
||||
s_context_menu shared_menu{
|
||||
ContextEntityType::SHARED,
|
||||
0,
|
||||
{}, {
|
||||
{"EXPLODE", [this] {
|
||||
rage::fvector3 pos = m_pointer->m_navigation->m_position;
|
||||
FIRE::ADD_EXPLOSION(pos.x, pos.y, pos.z, 1, 1000, 1, 0, 1, 0);
|
||||
}},
|
||||
{"TP TO", [this] {
|
||||
rage::fvector3 pos = m_pointer->m_navigation->m_position;
|
||||
teleport::to_coords({ pos.x, pos.y, pos.z });
|
||||
}},
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<ContextEntityType, s_context_menu> options = {
|
||||
{ContextEntityType::VEHICLE, vehicle_menu},
|
||||
{ContextEntityType::PLAYER, player_menu},
|
||||
{ContextEntityType::PED, ped_menu},
|
||||
{ContextEntityType::SHARED, shared_menu},
|
||||
{ContextEntityType::OBJECT, object_menu}
|
||||
};
|
||||
};
|
||||
|
||||
inline context_menu_service* g_context_menu_service{};
|
||||
}
|
12
src/services/custom_text/custom_text_callbacks.cpp
Normal file
12
src/services/custom_text/custom_text_callbacks.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "custom_text_callbacks.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
const char* respawn_label_callback(const char* label)
|
||||
{
|
||||
if (g->self.god_mode)
|
||||
return "~r~Dying with god mode, how?";
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
4
src/services/custom_text/custom_text_callbacks.hpp
Normal file
4
src/services/custom_text/custom_text_callbacks.hpp
Normal file
@ -0,0 +1,4 @@
|
||||
namespace big
|
||||
{
|
||||
extern const char* respawn_label_callback(const char* label);
|
||||
}
|
51
src/services/custom_text/custom_text_service.cpp
Normal file
51
src/services/custom_text/custom_text_service.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "custom_text_service.hpp"
|
||||
#include "custom_text_callbacks.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
custom_text_service::custom_text_service()
|
||||
{
|
||||
add_callback_for_labels({ RAGE_JOAAT("RESPAWN_W"), RAGE_JOAAT("RESPAWN_W_MP") }, respawn_label_callback);
|
||||
add_label_overwrite(RAGE_JOAAT("GC_OTR_TMR"), "HIDING FROM CLOWNS");
|
||||
add_label_overwrite(RAGE_JOAAT("TICK_LEFTCHEAT"), "~a~~HUD_COLOUR_WHITE~ has been swatted by Rockstar.");
|
||||
|
||||
g_custom_text_service = this;
|
||||
}
|
||||
|
||||
custom_text_service::~custom_text_service()
|
||||
{
|
||||
g_custom_text_service = nullptr;
|
||||
}
|
||||
|
||||
bool custom_text_service::add_callback_for_label(rage::joaat_t hash, custom_label_callback&& cb)
|
||||
{
|
||||
return m_callbacks.insert({ hash, cb }).second;
|
||||
}
|
||||
|
||||
bool custom_text_service::add_callback_for_labels(std::list<rage::joaat_t> hashes, custom_label_callback&& cb)
|
||||
{
|
||||
bool result = true;
|
||||
for (const auto& hash : hashes)
|
||||
result = m_callbacks.insert({ hash, cb }).second;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool custom_text_service::add_label_overwrite(rage::joaat_t hash, const std::string_view overwrite)
|
||||
{
|
||||
const auto size = std::strlen(overwrite.data()) + 1;
|
||||
auto buffer = std::make_unique<char[]>(size);
|
||||
memcpy(buffer.get(), overwrite.data(), size);
|
||||
|
||||
return m_label_overwrites.insert({ hash, std::move(buffer) }).second;
|
||||
}
|
||||
|
||||
const char* custom_text_service::get_text(const char* label) const
|
||||
{
|
||||
const auto hash = rage::joaat(label);
|
||||
if (const auto& it = m_callbacks.find(hash); it != m_callbacks.end())
|
||||
return it->second(label);
|
||||
if (const auto& it = m_label_overwrites.find(hash); it != m_label_overwrites.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
33
src/services/custom_text/custom_text_service.hpp
Normal file
33
src/services/custom_text/custom_text_service.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "gta/joaat.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
using custom_label_callback = std::function<const char* (const char*)>;
|
||||
class custom_text_service final
|
||||
{
|
||||
std::map<rage::joaat_t, custom_label_callback> m_callbacks;
|
||||
std::map<rage::joaat_t, std::unique_ptr<char[]>> m_label_overwrites;
|
||||
|
||||
public:
|
||||
custom_text_service();
|
||||
~custom_text_service();
|
||||
|
||||
custom_text_service(const custom_text_service&) = delete;
|
||||
custom_text_service(custom_text_service&&) noexcept = delete;
|
||||
custom_text_service& operator=(const custom_text_service&) = delete;
|
||||
custom_text_service& operator=(custom_text_service&&) noexcept = delete;
|
||||
|
||||
bool add_callback_for_label(rage::joaat_t hash, custom_label_callback&& cb);
|
||||
bool add_callback_for_labels(std::list<rage::joaat_t> hashes, custom_label_callback&& cb);
|
||||
bool add_label_overwrite(rage::joaat_t hash, std::string_view overwrite);
|
||||
|
||||
/**
|
||||
* \brief Get the custom text for a label.
|
||||
* \return nullptr if no custom text exists
|
||||
*/
|
||||
[[nodiscard]] const char* get_text(const char* label) const;
|
||||
};
|
||||
|
||||
inline custom_text_service* g_custom_text_service;
|
||||
}
|
28
src/services/friends/friends_service.cpp
Normal file
28
src/services/friends/friends_service.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "friends_service.hpp"
|
||||
#include "pointers.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
friends_service::friends_service()
|
||||
{
|
||||
g_friends_service = this;
|
||||
}
|
||||
|
||||
friends_service::~friends_service()
|
||||
{
|
||||
g_friends_service = nullptr;
|
||||
}
|
||||
|
||||
bool friends_service::is_friend(CNetGamePlayer* net_player)
|
||||
{
|
||||
if (net_player == nullptr)
|
||||
return false;
|
||||
|
||||
const auto rockstar_id = net_player->get_net_data()->m_gamer_handle_2.m_rockstar_id;
|
||||
for (std::uint32_t i = 0; i < g_pointers->m_friend_registry->m_friend_count; i++)
|
||||
if (rockstar_id == g_pointers->m_friend_registry->get(i)->m_rockstar_id)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
20
src/services/friends/friends_service.hpp
Normal file
20
src/services/friends/friends_service.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
class friends_service final
|
||||
{
|
||||
public:
|
||||
friends_service();
|
||||
~friends_service();
|
||||
|
||||
friends_service(const friends_service&) = delete;
|
||||
friends_service(friends_service&&) noexcept = delete;
|
||||
friends_service& operator=(const friends_service&) = delete;
|
||||
friends_service& operator=(friends_service&&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] static bool is_friend(CNetGamePlayer* net_player);
|
||||
};
|
||||
|
||||
inline friends_service* g_friends_service{};
|
||||
}
|
86
src/services/globals/globals_service.cpp
Normal file
86
src/services/globals/globals_service.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "globals_service.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
globals_service::globals_service()
|
||||
{
|
||||
g_globals_service = this;
|
||||
}
|
||||
|
||||
globals_service::~globals_service()
|
||||
{
|
||||
g_globals_service = nullptr;
|
||||
|
||||
m_running = false;
|
||||
|
||||
this->save();
|
||||
}
|
||||
|
||||
void globals_service::build(nlohmann::json& data)
|
||||
{
|
||||
m_globals.clear();
|
||||
|
||||
for (auto& offset : data)
|
||||
m_globals.push_back(global(offset));
|
||||
|
||||
for (auto& global : m_globals)
|
||||
global.build_cache();
|
||||
}
|
||||
|
||||
bool globals_service::load()
|
||||
{
|
||||
std::string path = std::getenv("appdata");
|
||||
path += this->file_location;
|
||||
|
||||
std::ifstream file(path);
|
||||
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
nlohmann::json j;
|
||||
j << file;
|
||||
|
||||
this->build(j);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG(WARNING) << "Failure to parse globals.json, aborting...";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void globals_service::loop()
|
||||
{
|
||||
while (m_running)
|
||||
for (auto& global : m_globals)
|
||||
if (global.m_freeze) global.write();
|
||||
}
|
||||
|
||||
void globals_service::save()
|
||||
{
|
||||
nlohmann::json j = nlohmann::json::array();
|
||||
for (auto& global : m_globals)
|
||||
j.push_back(global.to_json());
|
||||
|
||||
std::string path = std::getenv("appdata");
|
||||
path += this->file_location;
|
||||
std::ofstream file(path, std::ios::out | std::ios::trunc);
|
||||
|
||||
try
|
||||
{
|
||||
file << j.dump(4);
|
||||
|
||||
file.close();
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG(WARNING) << "Failed to write to globals.json";
|
||||
}
|
||||
}
|
||||
}
|
155
src/services/globals/globals_service.hpp
Normal file
155
src/services/globals/globals_service.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
#include "script_global.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
struct global_offset
|
||||
{
|
||||
global_offset(nlohmann::json data)
|
||||
{
|
||||
m_offset = data["offset"];
|
||||
|
||||
if (data.contains("size"))
|
||||
m_size = data["size"];
|
||||
}
|
||||
|
||||
global_offset(int offset, int size = 0)
|
||||
{
|
||||
m_offset = offset;
|
||||
|
||||
if (size)
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
script_global apply(script_global internal_cache)
|
||||
{
|
||||
return m_size ? internal_cache.at(m_offset, m_size) : internal_cache.at(m_offset);
|
||||
}
|
||||
|
||||
nlohmann::json to_json()
|
||||
{
|
||||
nlohmann::json j;
|
||||
|
||||
j["offset"] = m_offset;
|
||||
if (m_size)
|
||||
j["size"] = m_size;
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_offset = 0;
|
||||
int m_size = 0;
|
||||
};
|
||||
|
||||
struct global
|
||||
{
|
||||
int m_base_address;
|
||||
bool m_freeze = false;
|
||||
std::string m_name;
|
||||
std::vector<global_offset> m_offsets;
|
||||
int m_value;
|
||||
|
||||
global(nlohmann::json data)
|
||||
{
|
||||
m_internal_id = ++m_instance_count;
|
||||
|
||||
m_base_address = data["base_address"];
|
||||
m_freeze = data["freeze"];
|
||||
m_name = data["name"];
|
||||
m_value = data["value"];
|
||||
|
||||
for (auto& offset : data["offsets"])
|
||||
m_offsets.push_back(global_offset(offset));
|
||||
}
|
||||
|
||||
global(const char* name, const int base_address, const bool freeze, const int(*offsets)[2], int offset_count)
|
||||
{
|
||||
m_internal_id = ++m_instance_count;
|
||||
|
||||
m_base_address = base_address;
|
||||
m_freeze = freeze;
|
||||
m_name = std::string(name);
|
||||
m_value = 0;
|
||||
|
||||
for (int i = 0; i < offset_count; i++)
|
||||
m_offsets.push_back(global_offset(offsets[i][0], offsets[i][1]));
|
||||
}
|
||||
|
||||
void build_cache()
|
||||
{
|
||||
script_global internal_cache(m_base_address);
|
||||
|
||||
for (auto& offset : m_offsets)
|
||||
internal_cache = offset.apply(internal_cache);
|
||||
|
||||
m_internal_addr = internal_cache.as<int*>();
|
||||
}
|
||||
|
||||
int* get()
|
||||
{
|
||||
if (m_freeze)
|
||||
return &m_value;
|
||||
return m_internal_addr;
|
||||
}
|
||||
|
||||
int get_id()
|
||||
{
|
||||
return m_internal_id;
|
||||
}
|
||||
|
||||
void set(int value)
|
||||
{
|
||||
m_value = value;
|
||||
if (!m_freeze)
|
||||
this->write();
|
||||
}
|
||||
|
||||
nlohmann::json to_json()
|
||||
{
|
||||
nlohmann::json j;
|
||||
|
||||
j["base_address"] = m_base_address;
|
||||
j["freeze"] = m_freeze;
|
||||
j["name"] = m_name;
|
||||
j["value"] = m_value;
|
||||
|
||||
j["offsets"] = nlohmann::json::array();
|
||||
for (auto& offset : m_offsets)
|
||||
j["offsets"].push_back(offset.to_json());
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
void write()
|
||||
{
|
||||
*m_internal_addr = m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static int m_instance_count;
|
||||
|
||||
int m_internal_id;
|
||||
int* m_internal_addr;
|
||||
};
|
||||
|
||||
class globals_service
|
||||
{
|
||||
const char* file_location = "\\BigBaseV2\\globals.json";
|
||||
|
||||
public:
|
||||
globals_service();
|
||||
~globals_service();
|
||||
|
||||
bool load();
|
||||
void loop();
|
||||
void save();
|
||||
|
||||
std::vector<global> m_globals;
|
||||
bool m_running = false;;
|
||||
private:
|
||||
void build(nlohmann::json& data);
|
||||
};
|
||||
|
||||
inline globals_service* g_globals_service{};
|
||||
}
|
84
src/services/gta_data/cache_file.cpp
Normal file
84
src/services/gta_data/cache_file.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include "cache_file.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
cache_file::cache_file(file cache_file, std::uint32_t cache_version) :
|
||||
m_cache_file(cache_file),
|
||||
m_data(nullptr),
|
||||
m_cache_version(cache_version),
|
||||
m_cache_header()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cache_file::free()
|
||||
{
|
||||
m_data.reset();
|
||||
}
|
||||
|
||||
bool cache_file::load()
|
||||
{
|
||||
if (!m_cache_file.exists())
|
||||
return false;
|
||||
if (m_data)
|
||||
return true;
|
||||
|
||||
auto file = std::ifstream(m_cache_file.get_path(), std::ios::binary);
|
||||
|
||||
file.read(reinterpret_cast<char*>(&m_cache_header), sizeof(m_cache_header));
|
||||
|
||||
m_data = std::make_unique<std::uint8_t[]>(m_cache_header.m_data_size);
|
||||
file.read(reinterpret_cast<char*>(m_data.get()), m_cache_header.m_data_size);
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cache_file::write() const
|
||||
{
|
||||
if (!m_data)
|
||||
return false;
|
||||
|
||||
auto file = std::ofstream(m_cache_file.get_path(), std::ios::binary);
|
||||
|
||||
file.write(reinterpret_cast<const char*>(&m_cache_header), sizeof(m_cache_header));
|
||||
file.write(reinterpret_cast<const char*>(m_data.get()), m_cache_header.m_data_size);
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint8_t* cache_file::data() const
|
||||
{
|
||||
return m_data.get();
|
||||
}
|
||||
|
||||
std::uint64_t cache_file::data_size() const
|
||||
{
|
||||
return m_cache_header.m_data_size;
|
||||
}
|
||||
|
||||
bool cache_file::up_to_date(std::uint32_t game_version, float online_version) const
|
||||
{
|
||||
if (!m_data)
|
||||
return false;
|
||||
|
||||
return
|
||||
m_cache_version == m_cache_header.m_cache_version &&
|
||||
game_version == m_cache_header.m_game_version &&
|
||||
online_version == m_cache_header.m_online_version;
|
||||
}
|
||||
|
||||
void cache_file::set_data(cache_data&& data, std::uint64_t data_size)
|
||||
{
|
||||
m_data.swap(data);
|
||||
m_cache_header.m_data_size = data_size;
|
||||
}
|
||||
|
||||
void cache_file::set_header_version(std::uint32_t game_version, float online_version)
|
||||
{
|
||||
m_cache_header.m_cache_version = m_cache_version;
|
||||
m_cache_header.m_game_version = game_version;
|
||||
m_cache_header.m_online_version = online_version;
|
||||
}
|
||||
}
|
71
src/services/gta_data/cache_file.hpp
Normal file
71
src/services/gta_data/cache_file.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "file_manager/file.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class cache_header final
|
||||
{
|
||||
public:
|
||||
std::uint32_t m_cache_version;
|
||||
std::uint32_t m_game_version;
|
||||
float m_online_version;
|
||||
std::uint64_t m_data_size;
|
||||
};
|
||||
|
||||
using cache_data = std::unique_ptr<std::uint8_t[]>;
|
||||
class cache_file final
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="cache_file">FileMgr file object</param>
|
||||
/// <param name="cache_version">Internal version, use this to invalidate the cache when changing the structure of the data</param>
|
||||
cache_file(file cache_file, std::uint32_t cache_version);
|
||||
|
||||
/// <summary>
|
||||
/// Frees any memory used to hold the cached data.
|
||||
/// </summary>
|
||||
void free();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load the cache from disk
|
||||
/// </summary>
|
||||
/// <returns>True after successfully loading the data, false if the file didn't exist.</returns>
|
||||
bool load();
|
||||
|
||||
/// <summary>
|
||||
/// Writes the cache to disk
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool write() const;
|
||||
|
||||
std::uint8_t* data() const;
|
||||
std::uint64_t data_size() const;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the cache file is up to date with the expected versions
|
||||
/// </summary>
|
||||
/// <param name="game_version">Current Game version</param>
|
||||
/// <param name="online_version">Current Online version</param>
|
||||
/// <returns>True if cache is up to date, false otherwise.</returns>
|
||||
bool up_to_date(std::uint32_t game_version, float online_version) const;
|
||||
|
||||
|
||||
void set_data(cache_data&& data, std::uint64_t data_size);
|
||||
/// <summary>
|
||||
/// Sets the version information of the cache header.
|
||||
/// </summary>
|
||||
/// <param name="game_version">Game Build</param>
|
||||
/// <param name="online_version">Online Version</param>
|
||||
void set_header_version(std::uint32_t game_version, float online_version);
|
||||
|
||||
private:
|
||||
file m_cache_file;
|
||||
|
||||
std::uint32_t m_cache_version;
|
||||
|
||||
cache_header m_cache_header;
|
||||
cache_data m_data;
|
||||
};
|
||||
}
|
429
src/services/gta_data/gta_data_service.cpp
Normal file
429
src/services/gta_data/gta_data_service.cpp
Normal file
@ -0,0 +1,429 @@
|
||||
#include "gta_data_service.hpp"
|
||||
#include "file_manager.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "pugixml.hpp"
|
||||
#include "script.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "util/session.hpp"
|
||||
#include "yim_fipackfile.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
bool add_if_not_exists(string_vec& vec, std::string str)
|
||||
{
|
||||
if (std::find(vec.begin(), vec.end(), str) != vec.end())
|
||||
return true;
|
||||
|
||||
vec.emplace_back(std::move(str));
|
||||
return false;
|
||||
}
|
||||
|
||||
gta_data_service::gta_data_service() :
|
||||
m_peds_cache(g_file_manager->get_project_file("./cache/peds.bin"), 1),
|
||||
m_vehicles_cache(g_file_manager->get_project_file("./cache/vehicles.bin"), 1),
|
||||
m_weapons_cache(g_file_manager->get_project_file("./cache/weapons.bin"), 1),
|
||||
m_update_state(eGtaDataUpdateState::IDLE)
|
||||
{
|
||||
if (!is_cache_up_to_date())
|
||||
m_update_state = eGtaDataUpdateState::NEEDS_UPDATE;
|
||||
else
|
||||
load_data();
|
||||
|
||||
g_gta_data_service = this;
|
||||
}
|
||||
|
||||
gta_data_service::~gta_data_service()
|
||||
{
|
||||
g_gta_data_service = nullptr;
|
||||
}
|
||||
|
||||
bool gta_data_service::cache_needs_update() const
|
||||
{
|
||||
return m_update_state == eGtaDataUpdateState::NEEDS_UPDATE;
|
||||
}
|
||||
|
||||
eGtaDataUpdateState gta_data_service::state() const
|
||||
{
|
||||
return m_update_state;
|
||||
}
|
||||
|
||||
void gta_data_service::update_in_online()
|
||||
{
|
||||
m_update_state = eGtaDataUpdateState::WAITING_FOR_ONLINE;
|
||||
|
||||
g_fiber_pool->queue_job([this]
|
||||
{
|
||||
session::join_type(eSessionType::SOLO);
|
||||
|
||||
while (!*g_pointers->m_is_session_started)
|
||||
{
|
||||
script::get_current()->yield(100ms);
|
||||
}
|
||||
|
||||
rebuild_cache();
|
||||
});
|
||||
}
|
||||
|
||||
void gta_data_service::update_now()
|
||||
{
|
||||
g_fiber_pool->queue_job([this]
|
||||
{
|
||||
rebuild_cache();
|
||||
});
|
||||
}
|
||||
|
||||
// innefficient getters, don't care to fix right now
|
||||
const ped_item& gta_data_service::ped_by_hash(std::uint32_t hash)
|
||||
{
|
||||
for (const auto& [name, ped] : m_peds)
|
||||
if (rage::joaat(name) == hash)
|
||||
return ped;
|
||||
return gta_data_service::empty_ped;
|
||||
}
|
||||
|
||||
const vehicle_item& gta_data_service::vehicle_by_hash(std::uint32_t hash)
|
||||
{
|
||||
for (const auto& [name, veh] : m_vehicles)
|
||||
if (rage::joaat(name) == hash)
|
||||
return veh;
|
||||
return gta_data_service::empty_vehicle;
|
||||
}
|
||||
|
||||
const weapon_item& gta_data_service::weapon_by_hash(std::uint32_t hash)
|
||||
{
|
||||
for (const auto& [name, weapon] : m_weapons)
|
||||
if (rage::joaat(name) == hash)
|
||||
return weapon;
|
||||
return gta_data_service::empty_weapon;
|
||||
}
|
||||
|
||||
string_vec& gta_data_service::ped_types()
|
||||
{
|
||||
return m_ped_types;
|
||||
}
|
||||
|
||||
string_vec& gta_data_service::vehicle_classes()
|
||||
{
|
||||
return m_vehicle_classes;
|
||||
}
|
||||
|
||||
string_vec& gta_data_service::weapon_types()
|
||||
{
|
||||
return m_weapon_types;
|
||||
}
|
||||
|
||||
bool gta_data_service::is_cache_up_to_date()
|
||||
{
|
||||
m_peds_cache.load();
|
||||
m_vehicles_cache.load();
|
||||
m_weapons_cache.load();
|
||||
|
||||
const auto game_version = std::strtoul(g_pointers->m_game_version, nullptr, 10);
|
||||
const auto online_version = std::strtof(g_pointers->m_online_version, nullptr);
|
||||
|
||||
return
|
||||
m_peds_cache.up_to_date(game_version, online_version) &&
|
||||
m_vehicles_cache.up_to_date(game_version, online_version) &&
|
||||
m_weapons_cache.up_to_date(game_version, online_version);
|
||||
}
|
||||
|
||||
void gta_data_service::load_data()
|
||||
{
|
||||
LOG(G3LOG_DEBUG) << "Loading data from cache.";
|
||||
|
||||
load_peds();
|
||||
load_vehicles();
|
||||
load_weapons();
|
||||
|
||||
LOG(G3LOG_DEBUG) << "Loaded all data from cache.";
|
||||
}
|
||||
|
||||
void gta_data_service::load_peds()
|
||||
{
|
||||
const auto ped_count = m_peds_cache.data_size() / sizeof(ped_item);
|
||||
LOG(INFO) << "Loading " << ped_count << " peds from cache.";
|
||||
|
||||
auto cached_peds = reinterpret_cast<const ped_item*>(m_peds_cache.data());
|
||||
for (size_t i = 0; i < ped_count; i++)
|
||||
{
|
||||
const auto ped = cached_peds[i];
|
||||
|
||||
add_if_not_exists(m_ped_types, ped.m_ped_type);
|
||||
m_peds.insert({ ped.m_name, ped });
|
||||
}
|
||||
|
||||
std::sort(m_ped_types.begin(), m_ped_types.end());
|
||||
m_peds_cache.free();
|
||||
}
|
||||
|
||||
void gta_data_service::load_vehicles()
|
||||
{
|
||||
const auto vehicle_count = m_vehicles_cache.data_size() / sizeof(vehicle_item);
|
||||
LOG(INFO) << "Loading " << vehicle_count << " vehicles from cache.";
|
||||
|
||||
auto cached_vehicles = reinterpret_cast<const vehicle_item*>(m_vehicles_cache.data());
|
||||
for (size_t i = 0; i < vehicle_count; i++)
|
||||
{
|
||||
const auto vehicle = cached_vehicles[i];
|
||||
|
||||
add_if_not_exists(m_vehicle_classes, vehicle.m_vehicle_class);
|
||||
m_vehicles.insert({ vehicle.m_name, vehicle });
|
||||
}
|
||||
|
||||
std::sort(m_vehicle_classes.begin(), m_vehicle_classes.end());
|
||||
m_vehicles_cache.free();
|
||||
}
|
||||
|
||||
void gta_data_service::load_weapons()
|
||||
{
|
||||
const auto weapon_count = m_weapons_cache.data_size() / sizeof(weapon_item);
|
||||
LOG(INFO) << "Loading " << weapon_count << " weapons from cache.";
|
||||
|
||||
auto cached_weapons = reinterpret_cast<const weapon_item*>(m_weapons_cache.data());
|
||||
for (size_t i = 0; i < weapon_count; i++)
|
||||
{
|
||||
const auto weapon = cached_weapons[i];
|
||||
|
||||
add_if_not_exists(m_weapon_types, weapon.m_weapon_type);
|
||||
m_weapons.insert({ weapon.m_name, weapon });
|
||||
}
|
||||
|
||||
std::sort(m_weapon_types.begin(), m_weapon_types.end());
|
||||
m_weapons_cache.free();
|
||||
}
|
||||
|
||||
void gta_data_service::rebuild_cache()
|
||||
{
|
||||
m_update_state = eGtaDataUpdateState::UPDATING;
|
||||
|
||||
using hash_array = std::vector<std::uint32_t>;
|
||||
hash_array mapped_peds;
|
||||
hash_array mapped_vehicles;
|
||||
hash_array mapped_weapons;
|
||||
|
||||
std::vector<ped_item> peds;
|
||||
std::vector<vehicle_item> vehicles;
|
||||
std::vector<weapon_item> weapons;
|
||||
|
||||
constexpr auto exists = [](const hash_array& arr, std::uint32_t val) -> bool
|
||||
{
|
||||
return std::find(arr.begin(), arr.end(), val) != arr.end();
|
||||
};
|
||||
|
||||
LOG(INFO) << "Rebuilding cache started...";
|
||||
yim_fipackfile::for_each_fipackfile([&](yim_fipackfile& rpf_wrapper)
|
||||
{
|
||||
const auto files = rpf_wrapper.get_file_paths();
|
||||
for (const auto& file : files)
|
||||
{
|
||||
if (file.filename() == "setup2.xml")
|
||||
{
|
||||
std::string dlc_name;
|
||||
rpf_wrapper.read_xml_file(file, [&dlc_name](pugi::xml_document& doc)
|
||||
{
|
||||
const auto item = doc.select_node("/SSetupData/nameHash");
|
||||
dlc_name = item.node().text().as_string();
|
||||
});
|
||||
|
||||
if (dlc_name == "mpG9EC")
|
||||
{
|
||||
LOG(G3LOG_DEBUG) << "Bad DLC, skipping...";
|
||||
|
||||
return std::size_t(0);
|
||||
}
|
||||
}
|
||||
else if (file.filename() == "vehicles.meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(file, [&exists, &vehicles, &mapped_vehicles](pugi::xml_document& doc)
|
||||
{
|
||||
const auto& items = doc.select_nodes("/CVehicleModelInfo__InitDataList/InitDatas/Item");
|
||||
for (const auto& item_node : items)
|
||||
{
|
||||
const auto item = item_node.node();
|
||||
|
||||
const auto name = item.child("modelName").text().as_string();
|
||||
const auto hash = rage::joaat(name);
|
||||
|
||||
if (exists(mapped_vehicles, hash))
|
||||
continue;
|
||||
mapped_vehicles.emplace_back(hash);
|
||||
|
||||
auto veh = vehicle_item{};
|
||||
std::strncpy(veh.m_name, name, sizeof(veh.m_name));
|
||||
|
||||
const auto manufacturer_display = item.child("vehicleMakeName").text().as_string();
|
||||
std::strncpy(
|
||||
veh.m_display_manufacturer,
|
||||
HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(manufacturer_display),
|
||||
sizeof(veh.m_display_manufacturer));
|
||||
|
||||
const auto game_name = item.child("gameName").text().as_string();
|
||||
std::strncpy(
|
||||
veh.m_display_name,
|
||||
HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(game_name),
|
||||
sizeof(veh.m_display_name));
|
||||
|
||||
const auto vehicle_class = item.child("vehicleClass").text().as_string();
|
||||
constexpr auto enum_prefix_len = 3;
|
||||
if (std::strlen(vehicle_class) > enum_prefix_len)
|
||||
std::strncpy(veh.m_vehicle_class, vehicle_class + enum_prefix_len, sizeof(veh.m_vehicle_class));
|
||||
|
||||
veh.m_hash = hash;
|
||||
|
||||
vehicles.emplace_back(std::move(veh));
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (const auto file_str = file.string(); file_str.find("weapon") != std::string::npos && file.extension() == ".meta")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(file, [&exists, &weapons, &mapped_weapons](pugi::xml_document& doc)
|
||||
{
|
||||
const auto& items = doc.select_nodes("/CWeaponInfoBlob/Infos/Item/Infos/Item[@type='CWeaponInfo']");
|
||||
for (const auto& item_node : items)
|
||||
{
|
||||
const auto item = item_node.node();
|
||||
const auto name = item.child("Name").text().as_string();
|
||||
const auto hash = rage::joaat(name);
|
||||
|
||||
if (exists(mapped_weapons, hash))
|
||||
continue;
|
||||
mapped_weapons.emplace_back(hash);
|
||||
|
||||
const auto human_name_hash = item.child("HumanNameHash").text().as_string();
|
||||
if (std::strcmp(human_name_hash, "WT_INVALID") == 0)
|
||||
continue;
|
||||
|
||||
auto weapon = weapon_item{};
|
||||
|
||||
std::strncpy(weapon.m_name, name, sizeof(weapon.m_name));
|
||||
|
||||
const auto display_name = HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(human_name_hash);
|
||||
std::strncpy(weapon.m_display_name, display_name, sizeof(weapon.m_name));
|
||||
|
||||
auto weapon_flags = std::string(
|
||||
item.child("WeaponFlags").text().as_string()
|
||||
);
|
||||
|
||||
bool is_gun = false;
|
||||
bool is_rechargable = false;
|
||||
|
||||
std::size_t pos;
|
||||
while ((pos = weapon_flags.find(' ')) != std::string::npos) {
|
||||
const auto flag = weapon_flags.substr(0, pos);
|
||||
if (flag == "Thrown")
|
||||
{
|
||||
weapon.m_throwable = true;
|
||||
}
|
||||
else if (flag == "Gun")
|
||||
{
|
||||
is_gun = true;
|
||||
}
|
||||
else if (flag == "DisplayRechargeTimeHUD")
|
||||
{
|
||||
is_rechargable = true;
|
||||
}
|
||||
|
||||
weapon_flags.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
const auto category = item.child("Group").text().as_string();
|
||||
if (std::strlen(category) > 6)
|
||||
{
|
||||
std::strncpy(weapon.m_weapon_type, category + 6, sizeof(weapon.m_weapon_type));
|
||||
}
|
||||
|
||||
if (is_gun || !std::strcmp(weapon.m_weapon_type, "MELEE") || !std::strcmp(weapon.m_weapon_type, "UNARMED"))
|
||||
{
|
||||
const std::string reward_prefix = "REWARD_";
|
||||
weapon.m_reward_hash = rage::joaat(reward_prefix + name);
|
||||
|
||||
if (is_gun && !is_rechargable)
|
||||
{
|
||||
std::string weapon_id = name + 7;
|
||||
weapon.m_reward_ammo_hash = rage::joaat(reward_prefix + "AMMO_" + weapon_id);
|
||||
}
|
||||
}
|
||||
|
||||
weapon.m_hash = hash;
|
||||
|
||||
weapons.emplace_back(std::move(weapon));
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (file.filename() == "peds.meta" || file.filename() == "peds.ymt")
|
||||
{
|
||||
rpf_wrapper.read_xml_file(file, [&exists, &peds, &mapped_peds](pugi::xml_document& doc)
|
||||
{
|
||||
const auto& items = doc.select_nodes("/CPedModelInfo__InitDataList/InitDatas/Item");
|
||||
for (const auto& item_node : items)
|
||||
{
|
||||
const auto& item = item_node.node();
|
||||
const auto name = item.child("Name").text().as_string();
|
||||
const auto hash = rage::joaat(name);
|
||||
|
||||
if (exists(mapped_peds, hash))
|
||||
continue;
|
||||
mapped_peds.emplace_back(hash);
|
||||
|
||||
auto ped = ped_item{};
|
||||
|
||||
std::strncpy(ped.m_name, name, sizeof(ped.m_name));
|
||||
|
||||
const auto ped_type = item.child("Pedtype").text().as_string();
|
||||
std::strncpy(ped.m_ped_type, ped_type, sizeof(ped.m_ped_type));
|
||||
|
||||
ped.m_hash = hash;
|
||||
|
||||
peds.emplace_back(std::move(ped));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return files.size();
|
||||
});
|
||||
|
||||
m_update_state = eGtaDataUpdateState::IDLE;
|
||||
LOG(INFO) << "Cache has been rebuilt.\n\tPeds: " << peds.size() << "\n\tVehicles: " << vehicles.size() << "\n\tWeapons: " << weapons.size();
|
||||
|
||||
LOG(G3LOG_DEBUG) << "Starting cache saving procedure...";
|
||||
g_thread_pool->push([this, peds = std::move(peds), vehicles = std::move(vehicles), weapons = std::move(weapons)]
|
||||
{
|
||||
const auto game_version = std::strtoul(g_pointers->m_game_version, nullptr, 10);
|
||||
const auto online_version = std::strtof(g_pointers->m_online_version, nullptr);
|
||||
|
||||
{
|
||||
const auto data_size = sizeof(ped_item) * peds.size();
|
||||
m_peds_cache.set_data(std::make_unique<std::uint8_t[]>(data_size), data_size);
|
||||
std::memcpy(m_peds_cache.data(), peds.data(), data_size);
|
||||
|
||||
m_peds_cache.set_header_version(game_version, online_version);
|
||||
m_peds_cache.write();
|
||||
}
|
||||
|
||||
{
|
||||
const auto data_size = sizeof(vehicle_item) * vehicles.size();
|
||||
m_vehicles_cache.set_data(std::make_unique<std::uint8_t[]>(data_size), data_size);
|
||||
std::memcpy(m_vehicles_cache.data(), vehicles.data(), data_size);
|
||||
|
||||
m_vehicles_cache.set_header_version(game_version, online_version);
|
||||
m_vehicles_cache.write();
|
||||
}
|
||||
|
||||
{
|
||||
const auto data_size = sizeof(weapon_item) * weapons.size();
|
||||
m_weapons_cache.set_data(std::make_unique<std::uint8_t[]>(data_size), data_size);
|
||||
std::memcpy(m_weapons_cache.data(), weapons.data(), data_size);
|
||||
|
||||
m_weapons_cache.set_header_version(game_version, online_version);
|
||||
m_weapons_cache.write();
|
||||
}
|
||||
|
||||
LOG(INFO) << "Finished writing cache to disk.";
|
||||
|
||||
load_data();
|
||||
});
|
||||
}
|
||||
}
|
82
src/services/gta_data/gta_data_service.hpp
Normal file
82
src/services/gta_data/gta_data_service.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "cache_file.hpp"
|
||||
#include "ped_item.hpp"
|
||||
#include "vehicle_item.hpp"
|
||||
#include "weapon_item.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class eGtaDataUpdateState
|
||||
{
|
||||
IDLE,
|
||||
NEEDS_UPDATE,
|
||||
WAITING_FOR_ONLINE,
|
||||
UPDATING
|
||||
};
|
||||
|
||||
using ped_map = std::map<std::string, ped_item>;
|
||||
using vehicle_map = std::map<std::string, vehicle_item>;
|
||||
using weapon_map = std::map<std::string, weapon_item>;
|
||||
using string_vec = std::vector<std::string>;
|
||||
|
||||
class gta_data_service final
|
||||
{
|
||||
public:
|
||||
gta_data_service();
|
||||
~gta_data_service();
|
||||
|
||||
bool cache_needs_update() const;
|
||||
eGtaDataUpdateState state() const;
|
||||
void update_in_online();
|
||||
void update_now();
|
||||
|
||||
const ped_item& ped_by_hash(std::uint32_t hash);
|
||||
const vehicle_item& vehicle_by_hash(std::uint32_t hash);
|
||||
const weapon_item& weapon_by_hash(std::uint32_t hash);
|
||||
|
||||
string_vec& ped_types();
|
||||
string_vec& vehicle_classes();
|
||||
string_vec& weapon_types();
|
||||
|
||||
ped_map& peds()
|
||||
{ return m_peds; }
|
||||
vehicle_map& vehicles()
|
||||
{ return m_vehicles; }
|
||||
weapon_map& weapons()
|
||||
{ return m_weapons; }
|
||||
|
||||
private:
|
||||
bool is_cache_up_to_date();
|
||||
|
||||
void load_data();
|
||||
void load_peds();
|
||||
void load_vehicles();
|
||||
void load_weapons();
|
||||
|
||||
void rebuild_cache();
|
||||
|
||||
private:
|
||||
cache_file m_peds_cache;
|
||||
cache_file m_vehicles_cache;
|
||||
cache_file m_weapons_cache;
|
||||
|
||||
// std::map is free sorting algo
|
||||
ped_map m_peds;
|
||||
vehicle_map m_vehicles;
|
||||
weapon_map m_weapons;
|
||||
|
||||
string_vec m_ped_types;
|
||||
string_vec m_vehicle_classes;
|
||||
string_vec m_weapon_types;
|
||||
|
||||
eGtaDataUpdateState m_update_state;
|
||||
|
||||
private:
|
||||
static constexpr ped_item empty_ped {};
|
||||
static constexpr vehicle_item empty_vehicle {};
|
||||
static constexpr weapon_item empty_weapon {};
|
||||
|
||||
};
|
||||
|
||||
inline gta_data_service* g_gta_data_service{};
|
||||
}
|
14
src/services/gta_data/ped_item.hpp
Normal file
14
src/services/gta_data/ped_item.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
class ped_item final
|
||||
{
|
||||
public:
|
||||
char m_name[32];
|
||||
char m_ped_type[16];
|
||||
std::uint32_t m_hash;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
16
src/services/gta_data/vehicle_item.hpp
Normal file
16
src/services/gta_data/vehicle_item.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
class vehicle_item final
|
||||
{
|
||||
public:
|
||||
char m_name[16];
|
||||
char m_display_name[32];
|
||||
char m_display_manufacturer[32];
|
||||
char m_vehicle_class[32];
|
||||
std::uint32_t m_hash;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
18
src/services/gta_data/weapon_item.hpp
Normal file
18
src/services/gta_data/weapon_item.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
class weapon_item final
|
||||
{
|
||||
public:
|
||||
char m_name[32];
|
||||
char m_display_name[32];
|
||||
char m_weapon_type[16];
|
||||
std::uint32_t m_hash;
|
||||
std::uint32_t m_reward_hash;
|
||||
std::uint32_t m_reward_ammo_hash;
|
||||
bool m_throwable;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
216
src/services/gta_data/yim_fipackfile.cpp
Normal file
216
src/services/gta_data/yim_fipackfile.cpp
Normal file
@ -0,0 +1,216 @@
|
||||
#include "yim_fipackfile.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "gta/fidevice.hpp"
|
||||
#include "script.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
yim_fipackfile::yim_fipackfile(rage::fiPackfile* rpf, const std::string& mount_name)
|
||||
{
|
||||
this->rpf = rpf;
|
||||
this->mount_name = mount_name;
|
||||
}
|
||||
|
||||
static std::vector<std::string> get_non_dlc_mounted_devices_names()
|
||||
{
|
||||
std::vector<std::string> non_dlc_mounted_devices_names;
|
||||
|
||||
uint16_t mounted_devices_len = *g_pointers->m_fidevices_len;
|
||||
if (mounted_devices_len)
|
||||
{
|
||||
auto devices_arr = *(uint64_t*)g_pointers->m_fidevices;
|
||||
uint8_t** current_device_mount_name_ptr = *(unsigned __int8***)g_pointers->m_fidevices;
|
||||
auto device_i = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
non_dlc_mounted_devices_names.push_back(*(const char**)current_device_mount_name_ptr);
|
||||
|
||||
++device_i;
|
||||
current_device_mount_name_ptr += 4;
|
||||
if (device_i >= mounted_devices_len)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return non_dlc_mounted_devices_names;
|
||||
}
|
||||
|
||||
static int ends_with(const char* str, const char* suffix)
|
||||
{
|
||||
if (!str || !suffix)
|
||||
return 0;
|
||||
size_t lenstr = strlen(str);
|
||||
size_t lensuffix = strlen(suffix);
|
||||
if (lensuffix > lenstr)
|
||||
return 0;
|
||||
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
|
||||
}
|
||||
|
||||
void yim_fipackfile::for_each_fipackfile(std::function<size_t(yim_fipackfile& rpf_wrapper)> cb)
|
||||
{
|
||||
// the idea is to reuse existing mount points as much as possible because
|
||||
// even when mounting / unmounting properly you'll get file errors
|
||||
// and crashes if the rpf file was already mounted
|
||||
|
||||
// iterate the fidevice array which contains devices that are currently mounted
|
||||
// the dlc devices are in another array
|
||||
const auto non_dlc_mounted_devices_names = get_non_dlc_mounted_devices_names();
|
||||
|
||||
// for not hanging the game too much
|
||||
constexpr auto yield_increment = 80;
|
||||
|
||||
auto i = 1;
|
||||
while (g_pointers->m_fipackfile_instances[i])
|
||||
{
|
||||
auto* rpf = g_pointers->m_fipackfile_instances[i];
|
||||
|
||||
// its hard coded in the binary?
|
||||
if (++i >= 3672)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
yim_fipackfile rpf_wrapper = yim_fipackfile(rpf, default_mount_name);
|
||||
|
||||
auto already_mounted = false;
|
||||
for (const auto& non_dlc_mounted_device_name : non_dlc_mounted_devices_names)
|
||||
{
|
||||
auto* non_dlc_mounted_device = rage::fiDevice::GetDevice(non_dlc_mounted_device_name.c_str(), true);
|
||||
|
||||
if (rpf == non_dlc_mounted_device)
|
||||
{
|
||||
rpf_wrapper.mount_name = non_dlc_mounted_device_name;
|
||||
already_mounted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_mounted)
|
||||
{
|
||||
size_t acc = 0;
|
||||
|
||||
rpf_wrapper.mount_name = "memory:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "memory:";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "dlc";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "dlc:";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "dlc:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "dlcpacks:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "common:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "commoncrc:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "update:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "update2:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "platform:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "platformcrc:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
rpf_wrapper.mount_name = "gamecache:/";
|
||||
acc += cb(rpf_wrapper);
|
||||
|
||||
// if we got nothing with those mount points for this rpf, mount it
|
||||
if (!acc)
|
||||
{
|
||||
rpf_wrapper.mount_name = default_mount_name;
|
||||
rpf->Mount(default_mount_name);
|
||||
|
||||
cb(rpf_wrapper);
|
||||
|
||||
g_pointers->m_fipackfile_unmount(default_mount_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cb(rpf_wrapper);
|
||||
}
|
||||
|
||||
if (i % yield_increment == 0)
|
||||
script::get_current()->yield();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> yim_fipackfile::get_file_paths(std::string parent)
|
||||
{
|
||||
std::vector<std::filesystem::path> file_paths;
|
||||
if (parent.empty())
|
||||
parent = mount_name;
|
||||
|
||||
std::vector<std::string> directories;
|
||||
|
||||
rage::fiFindData findData = { 0 };
|
||||
auto handlef = rpf->FindFirst(parent.c_str(), &findData);
|
||||
if (handlef != -1)
|
||||
{
|
||||
do
|
||||
{
|
||||
std::string fn = std::string(parent.c_str()) + std::string("/") + std::string(findData.fileName);
|
||||
|
||||
if (findData.fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
directories.push_back(fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
file_paths.push_back(fn);
|
||||
}
|
||||
} while (rpf->FindNext(handlef, &findData));
|
||||
|
||||
rpf->FindClose(handlef);
|
||||
}
|
||||
|
||||
for (auto& directory : directories)
|
||||
{
|
||||
auto files = get_file_paths(directory);
|
||||
|
||||
file_paths.insert(file_paths.end(), files.begin(), files.end());
|
||||
}
|
||||
|
||||
return file_paths;
|
||||
}
|
||||
|
||||
void yim_fipackfile::read_file(const std::filesystem::path& path, file_contents_callback&& cb)
|
||||
{
|
||||
if (const auto handle = rpf->Open(path.string().c_str(), true); handle != -1)
|
||||
{
|
||||
const auto data_length = rpf->GetFileLength(handle);
|
||||
const auto file_content = std::make_unique<std::uint8_t[]>(data_length);
|
||||
|
||||
rpf->ReadFull(handle, file_content.get(), data_length);
|
||||
|
||||
cb(file_content, data_length);
|
||||
|
||||
rpf->Close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void yim_fipackfile::read_xml_file(const std::filesystem::path& path, std::function<void(pugi::xml_document& doc)> cb)
|
||||
{
|
||||
read_file(path, [&cb](const std::unique_ptr<std::uint8_t[]>& file_content, const int data_size)
|
||||
{
|
||||
if (pugi::xml_document doc; doc.load_buffer(file_content.get(), data_size).status == pugi::xml_parse_status::status_ok)
|
||||
{
|
||||
cb(doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
27
src/services/gta_data/yim_fipackfile.hpp
Normal file
27
src/services/gta_data/yim_fipackfile.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <pugixml.hpp>
|
||||
|
||||
namespace big
|
||||
{
|
||||
using file_contents_callback = std::function<void(const std::unique_ptr<std::uint8_t[]>& file_content, const int data_size)>;
|
||||
class yim_fipackfile
|
||||
{
|
||||
static constexpr auto default_mount_name = "yimM:/";
|
||||
|
||||
rage::fiPackfile* rpf;
|
||||
std::string mount_name;
|
||||
|
||||
public:
|
||||
explicit yim_fipackfile(rage::fiPackfile* rpf, const std::string& mount_name);
|
||||
|
||||
static void for_each_fipackfile(std::function<size_t(yim_fipackfile& rpf_wrapper)> cb);
|
||||
std::vector<std::filesystem::path> get_file_paths(std::string parent = {});
|
||||
|
||||
void read_file(const std::filesystem::path& path, file_contents_callback&& cb);
|
||||
|
||||
void read_xml_file(const std::filesystem::path& path, std::function<void(pugi::xml_document& doc)> cb);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
}
|
78
src/services/gui/gui_service.cpp
Normal file
78
src/services/gui/gui_service.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "gui_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
gui_service::gui_service()
|
||||
{
|
||||
g_gui_service = this;
|
||||
}
|
||||
|
||||
gui_service::~gui_service()
|
||||
{
|
||||
g_gui_service = nullptr;
|
||||
}
|
||||
|
||||
navigation_struct* gui_service::get_selected()
|
||||
{
|
||||
navigation_struct tab_none = { "", nullptr };
|
||||
if (current_tab.empty() || current_tab.at(0) == tabs::NONE)
|
||||
return &tab_none;
|
||||
|
||||
navigation_struct* current_nav = &nav.at(current_tab.at(0));
|
||||
if (current_tab.size() > 1)
|
||||
{
|
||||
for (const tabs& t : current_tab)
|
||||
{
|
||||
if (t == current_tab.at(0)) continue;
|
||||
current_nav = ¤t_nav->sub_nav.at(t);
|
||||
}
|
||||
}
|
||||
|
||||
return current_nav;
|
||||
}
|
||||
|
||||
std::vector<tabs>& gui_service::get_selected_tab()
|
||||
{
|
||||
return current_tab;
|
||||
}
|
||||
|
||||
bool gui_service::has_switched_view()
|
||||
{
|
||||
return switched_view;
|
||||
}
|
||||
|
||||
void gui_service::set_selected(tabs tab)
|
||||
{
|
||||
if (current_tab.empty()) return current_tab.push_back(tab);
|
||||
if (auto it = get_selected()->sub_nav.find(tab); it != get_selected()->sub_nav.end())
|
||||
current_tab.push_back(tab);
|
||||
else
|
||||
{
|
||||
current_tab.pop_back();
|
||||
set_selected(tab);
|
||||
}
|
||||
}
|
||||
|
||||
void gui_service::set_nav_size(int nav_size)
|
||||
{
|
||||
nav_ctr = nav_size;
|
||||
}
|
||||
|
||||
void gui_service::increment_nav_size()
|
||||
{
|
||||
nav_ctr++;
|
||||
}
|
||||
|
||||
|
||||
void gui_service::reset_nav_size()
|
||||
{
|
||||
nav_ctr = 0;
|
||||
}
|
||||
|
||||
std::map<tabs, navigation_struct>& gui_service::get_navigation()
|
||||
{
|
||||
return nav;
|
||||
}
|
||||
|
||||
|
||||
}
|
109
src/services/gui/gui_service.hpp
Normal file
109
src/services/gui/gui_service.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class tabs {
|
||||
NONE,
|
||||
|
||||
SELF,
|
||||
WEAPONS,
|
||||
TELEPORT,
|
||||
MOBILE,
|
||||
|
||||
VEHICLE,
|
||||
HANDLING,
|
||||
HANDLING_SEARCH,
|
||||
HANDLING_SAVED_PROFILE,
|
||||
HANDLING_MY_PROFILES,
|
||||
HANDLING_CURRENT_PROFILE,
|
||||
LSC,
|
||||
SPAWN_VEHICLE,
|
||||
PV,
|
||||
PERSIST_CAR,
|
||||
FUN_VEHICLE,
|
||||
|
||||
WORLD,
|
||||
SPAWN_PED,
|
||||
TIME_AND_WEATHER,
|
||||
|
||||
NETWORK,
|
||||
SESSION,
|
||||
SPOOFING,
|
||||
|
||||
SETTINGS,
|
||||
CONTEXT_MENU_SETTINGS,
|
||||
ESP_SETTINGS,
|
||||
GUI_SETTINGS,
|
||||
NOTIFICATION_SETTINGS,
|
||||
PROTECTION_SETTINGS,
|
||||
DEBUG,
|
||||
|
||||
PLAYER
|
||||
};
|
||||
|
||||
struct navigation_struct
|
||||
{
|
||||
const char name[32] = "";
|
||||
std::function<void()> func = nullptr;
|
||||
std::map<tabs, navigation_struct> sub_nav{};
|
||||
};
|
||||
|
||||
class gui_service final
|
||||
{
|
||||
std::vector<tabs> current_tab{};
|
||||
bool switched_view = true;
|
||||
|
||||
std::map<tabs, navigation_struct> nav = {
|
||||
{tabs::SELF, { "Self",view::self, {
|
||||
{ tabs::WEAPONS, { "Weapons", view::weapons }},
|
||||
{ tabs::MOBILE, {"Mobile", view::mobile}},
|
||||
{ tabs::TELEPORT, {"Teleport", view::teleport}},
|
||||
}}},
|
||||
{tabs::VEHICLE, { "Vehicle", view::vehicle, {
|
||||
{ tabs::HANDLING, {"Handling", view::handling_current_profile, {
|
||||
{ tabs::HANDLING_CURRENT_PROFILE, {"Current Profile", view::handling_current_profile } },
|
||||
{ tabs::HANDLING_SAVED_PROFILE, {"Saved Profiles", view::handling_saved_profiles } },
|
||||
}}},
|
||||
{ tabs::LSC, { "LS Customs", view::lsc }},
|
||||
{ tabs::SPAWN_VEHICLE, { "Spawn Vehicle", view::spawn_vehicle }},
|
||||
{ tabs::PV, { "Personal Vehicle", view::pv }},
|
||||
{ tabs::PERSIST_CAR, { "Persist Car", view::persist_car }},
|
||||
{ tabs::FUN_VEHICLE, { "Fun Features", view::fun_vehicle }},
|
||||
}}},
|
||||
{ tabs::WORLD, { "World", nullptr, {
|
||||
{ tabs::SPAWN_PED, { "Spawn Ped", view::spawn_ped }},
|
||||
{ tabs::TIME_AND_WEATHER, { "Time And Weather", view::time_and_weather }},
|
||||
}}},
|
||||
{tabs::NETWORK, { "Network", nullptr, {
|
||||
{ tabs::SPOOFING, { "Spoofing", view::spoofing }},
|
||||
{ tabs::SESSION, { "Session", view::session }},
|
||||
}}},
|
||||
{tabs::SETTINGS, { "Settings", view::settings, {
|
||||
{ tabs::CONTEXT_MENU_SETTINGS, { "Context Menu", view::context_menu_settings}},
|
||||
{ tabs::ESP_SETTINGS, { "ESP", view::esp_settings}},
|
||||
{ tabs::GUI_SETTINGS, { "GUI", view::gui_settings}},
|
||||
{ tabs::NOTIFICATION_SETTINGS, { "Notifications", view::notification_settings}},
|
||||
{ tabs::PROTECTION_SETTINGS, { "Protection", view::protection_settings}},
|
||||
{ tabs::DEBUG, { "Debug", nullptr }},
|
||||
}}},
|
||||
{tabs::PLAYER, {"", view::view_player}}
|
||||
};
|
||||
public:
|
||||
gui_service();
|
||||
virtual ~gui_service();
|
||||
|
||||
int nav_ctr = 0;
|
||||
|
||||
navigation_struct* get_selected();
|
||||
std::vector<tabs>& get_selected_tab();
|
||||
bool has_switched_view();
|
||||
void set_selected(tabs);
|
||||
void set_nav_size(int);
|
||||
void increment_nav_size();
|
||||
void reset_nav_size();
|
||||
std::map<tabs, navigation_struct>& get_navigation();
|
||||
};
|
||||
|
||||
inline gui_service* g_gui_service{};
|
||||
}
|
121
src/services/mobile/mobile_service.cpp
Normal file
121
src/services/mobile/mobile_service.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
#include "mobile_service.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "script.hpp"
|
||||
#include "util/mobile.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
personal_vehicle::personal_vehicle(int idx, script_global vehicle_idx)
|
||||
: m_id(idx), m_vehicle_idx(vehicle_idx)
|
||||
{
|
||||
m_plate = m_vehicle_idx.at(1).as<char*>();
|
||||
m_hash = *m_vehicle_idx.at(66).as<Hash*>();
|
||||
m_state_bitfield = m_vehicle_idx.at(103).as<int*>();
|
||||
|
||||
m_name = std::format(
|
||||
"{} ({})",
|
||||
HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(VEHICLE::GET_DISPLAY_NAME_FROM_VEHICLE_MODEL(m_hash)),
|
||||
m_plate
|
||||
);
|
||||
}
|
||||
|
||||
std::string personal_vehicle::get_display_name() const
|
||||
{
|
||||
return m_name + "##" + std::to_string(m_id);
|
||||
}
|
||||
|
||||
Hash personal_vehicle::get_hash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
int personal_vehicle::get_id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const char* personal_vehicle::get_plate() const
|
||||
{
|
||||
return m_plate;
|
||||
}
|
||||
|
||||
script_global personal_vehicle::get_vehicle_idx() const
|
||||
{
|
||||
return m_vehicle_idx;
|
||||
}
|
||||
|
||||
void personal_vehicle::summon() const
|
||||
{
|
||||
mobile::mechanic::summon_vehicle_by_index(m_id);
|
||||
}
|
||||
|
||||
mobile_service::mobile_service()
|
||||
{
|
||||
g_mobile_service = this;
|
||||
}
|
||||
|
||||
mobile_service::~mobile_service()
|
||||
{
|
||||
g_mobile_service = nullptr;
|
||||
}
|
||||
|
||||
void mobile_service::refresh_personal_vehicles()
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_last_update) < 10s) return;
|
||||
m_last_update = std::chrono::high_resolution_clock::now();
|
||||
|
||||
g_fiber_pool->queue_job([this] {
|
||||
register_vehicles();
|
||||
});
|
||||
}
|
||||
|
||||
void mobile_service::register_vehicles()
|
||||
{
|
||||
const auto array_size = *mobile::vehicle_global.as<int*>();
|
||||
for (int i = 0; i < array_size; i++)
|
||||
{
|
||||
if (i % 100 == 0)
|
||||
script::get_current()->yield();
|
||||
|
||||
auto veh_idx_global = mobile::vehicle_global.at(i, 142);
|
||||
|
||||
const auto hash = *veh_idx_global.at(66).as<Hash*>();
|
||||
const auto& it = m_pv_lookup.find(i);
|
||||
const auto exists = it != m_pv_lookup.end();
|
||||
|
||||
// double check if model is a vehicle
|
||||
if (STREAMING::IS_MODEL_A_VEHICLE(hash))
|
||||
{
|
||||
auto veh = std::make_unique<personal_vehicle>(i, veh_idx_global);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
// vehicle name is no longer the same, update the vehicle at that index
|
||||
if (veh->get_display_name() != it->second)
|
||||
{
|
||||
m_personal_vehicles.erase(it->second);
|
||||
|
||||
it->second = veh->get_display_name();
|
||||
m_personal_vehicles.emplace(veh->get_display_name(), std::move(veh));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_pv_lookup.emplace(i, veh->get_display_name()); // update lookup table
|
||||
m_personal_vehicles.emplace(veh->get_display_name(), std::move(veh)); // add new vehicle
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// vehicle existed at some point but no longer does
|
||||
if (exists)
|
||||
{
|
||||
m_personal_vehicles.erase(it->second);
|
||||
m_pv_lookup.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
src/services/mobile/mobile_service.hpp
Normal file
51
src/services/mobile/mobile_service.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "script_global.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class personal_vehicle final
|
||||
{
|
||||
Hash m_hash;
|
||||
int m_id;
|
||||
std::string m_name;
|
||||
const char* m_plate;
|
||||
int* m_state_bitfield;
|
||||
script_global m_vehicle_idx;
|
||||
|
||||
public:
|
||||
personal_vehicle(int idx, script_global vehicle_idx);
|
||||
|
||||
[[nodiscard]] std::string get_display_name() const;
|
||||
[[nodiscard]] Hash get_hash() const;
|
||||
[[nodiscard]] int get_id() const;
|
||||
[[nodiscard]] const char* get_plate() const;
|
||||
[[nodiscard]] script_global get_vehicle_idx() const;
|
||||
|
||||
void summon() const;
|
||||
};
|
||||
|
||||
class mobile_service final
|
||||
{
|
||||
std::map<std::string, std::unique_ptr<personal_vehicle>> m_personal_vehicles;
|
||||
std::map<int, std::string> m_pv_lookup;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_last_update;
|
||||
|
||||
public:
|
||||
mobile_service();
|
||||
~mobile_service();
|
||||
|
||||
mobile_service(const mobile_service&) = delete;
|
||||
mobile_service(mobile_service&&) noexcept = delete;
|
||||
mobile_service& operator=(const mobile_service&) = delete;
|
||||
mobile_service& operator=(mobile_service&&) noexcept = delete;
|
||||
|
||||
std::map<std::string, std::unique_ptr<personal_vehicle>>& personal_vehicles()
|
||||
{ return m_personal_vehicles; }
|
||||
void refresh_personal_vehicles();
|
||||
void register_vehicles();
|
||||
|
||||
};
|
||||
|
||||
inline mobile_service* g_mobile_service{};
|
||||
}
|
213
src/services/model_preview/model_preview_service.cpp
Normal file
213
src/services/model_preview/model_preview_service.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include "fiber_pool.hpp"
|
||||
#include "gui.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "util/vehicle.hpp"
|
||||
#include "model_preview_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
model_preview_service::model_preview_service()
|
||||
{
|
||||
g_model_preview_service = this;
|
||||
}
|
||||
|
||||
model_preview_service::~model_preview_service()
|
||||
{
|
||||
g_model_preview_service = nullptr;
|
||||
}
|
||||
|
||||
void model_preview_service::show_ped(Hash hash)
|
||||
{
|
||||
m_ped_clone = 0;
|
||||
m_veh_model_hash = 0;
|
||||
m_veh_owned_mods.clear();
|
||||
|
||||
if (m_ped_model_hash != hash)
|
||||
{
|
||||
m_ped_model_hash = hash;
|
||||
|
||||
if (m_ped_model_hash != 0)
|
||||
{
|
||||
m_new_model = true;
|
||||
|
||||
preview_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void model_preview_service::show_ped(Hash hash, Ped clone)
|
||||
{
|
||||
m_veh_model_hash = 0;
|
||||
m_veh_owned_mods.clear();
|
||||
|
||||
if (m_ped_model_hash != hash || m_ped_clone != clone)
|
||||
{
|
||||
m_ped_model_hash = hash;
|
||||
m_ped_clone = clone;
|
||||
|
||||
if (m_ped_model_hash != 0)
|
||||
{
|
||||
m_new_model = true;
|
||||
|
||||
preview_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void model_preview_service::show_vehicle(Hash hash, bool spawn_max)
|
||||
{
|
||||
m_ped_model_hash = 0;
|
||||
m_ped_clone = 0;
|
||||
m_veh_owned_mods.clear();
|
||||
|
||||
if (m_veh_model_hash != hash || m_veh_spawn_max != spawn_max)
|
||||
{
|
||||
m_veh_model_hash = hash;
|
||||
|
||||
if (m_veh_model_hash != 0)
|
||||
{
|
||||
m_veh_spawn_max = spawn_max;
|
||||
m_new_model = true;
|
||||
|
||||
preview_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void model_preview_service::show_vehicle(const std::map<int, int32_t>& owned_mods, bool spawn_max)
|
||||
{
|
||||
m_ped_model_hash = 0;
|
||||
m_ped_clone = 0;
|
||||
|
||||
if (
|
||||
m_veh_spawn_max != spawn_max ||
|
||||
m_veh_owned_mods.size() != owned_mods.size() ||
|
||||
!std::equal(m_veh_owned_mods.begin(), m_veh_owned_mods.end(), owned_mods.begin())
|
||||
) {
|
||||
m_veh_owned_mods.clear();
|
||||
|
||||
auto hash_item = owned_mods.find(MOD_MODEL_HASH);
|
||||
|
||||
m_veh_model_hash = hash_item->second;
|
||||
|
||||
if (m_veh_model_hash != 0)
|
||||
{
|
||||
m_veh_owned_mods.insert(owned_mods.begin(), owned_mods.end());
|
||||
m_veh_spawn_max = spawn_max;
|
||||
m_new_model = true;
|
||||
|
||||
preview_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void model_preview_service::preview_loop()
|
||||
{
|
||||
if (m_running || m_loop_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
|
||||
g_fiber_pool->queue_job([this] {
|
||||
m_loop_running = true;
|
||||
|
||||
while (
|
||||
g_running && m_running && g_gui.m_opened &&
|
||||
(m_ped_model_hash|| m_veh_model_hash)
|
||||
) {
|
||||
Vector3 location;
|
||||
|
||||
if (m_ped_model_hash)
|
||||
{
|
||||
location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 5.f, -.5f);
|
||||
}
|
||||
else if (m_veh_model_hash)
|
||||
{
|
||||
location = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(self::ped, 0.f, 10.f, .5f);
|
||||
}
|
||||
|
||||
if (m_current_ent == 0)
|
||||
{
|
||||
m_new_model = false;
|
||||
location.z = -10.f;
|
||||
|
||||
if (m_ped_model_hash)
|
||||
{
|
||||
m_current_ent = ped::spawn(ePedType::PED_TYPE_ARMY, m_ped_model_hash, m_ped_clone, location, 0.f, false);
|
||||
|
||||
ENTITY::SET_ENTITY_HEALTH(m_current_ent, 0, 0);
|
||||
script::get_current()->yield(20ms);
|
||||
PED::RESURRECT_PED(m_current_ent);
|
||||
TASK::CLEAR_PED_TASKS(m_current_ent);
|
||||
}
|
||||
else if (m_veh_model_hash)
|
||||
{
|
||||
if (m_veh_owned_mods.empty())
|
||||
{
|
||||
m_current_ent = vehicle::spawn(m_veh_model_hash, location, 0.f, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_current_ent = vehicle::clone_from_owned_mods(m_veh_owned_mods, location, 0.f, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_current_ent)
|
||||
{
|
||||
if (m_veh_model_hash && m_veh_spawn_max)
|
||||
{
|
||||
vehicle::max_vehicle(m_current_ent);
|
||||
}
|
||||
|
||||
ENTITY::FREEZE_ENTITY_POSITION(m_current_ent, true);
|
||||
ENTITY::SET_ENTITY_ALPHA(m_current_ent, 0, false);
|
||||
ENTITY::SET_ENTITY_COLLISION(m_current_ent, false, false);
|
||||
ENTITY::SET_CAN_CLIMB_ON_ENTITY(m_current_ent, false);
|
||||
OBJECT::SET_OBJECT_ALLOW_LOW_LOD_BUOYANCY(m_current_ent, false);
|
||||
}
|
||||
}
|
||||
else if (m_new_model)
|
||||
{
|
||||
ENTITY::DETACH_ENTITY(m_current_ent, 1, 1);
|
||||
ENTITY::DELETE_ENTITY(&m_current_ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (const int alpha = ENTITY::GET_ENTITY_ALPHA(m_current_ent); alpha < 255)
|
||||
{
|
||||
ENTITY::SET_ENTITY_ALPHA(m_current_ent, std::min<int>(255, alpha + 20), false);
|
||||
}
|
||||
|
||||
ENTITY::SET_ENTITY_HEADING(m_current_ent, m_heading);
|
||||
ENTITY::SET_ENTITY_COORDS(m_current_ent, location.x, location.y, location.z, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (m_heading += 0.5f; m_heading > 359)
|
||||
{
|
||||
m_heading = 0;
|
||||
}
|
||||
|
||||
script::get_current()->yield(15ms);
|
||||
}
|
||||
|
||||
ENTITY::DETACH_ENTITY(m_current_ent, 1, 1);
|
||||
ENTITY::DELETE_ENTITY(&m_current_ent);
|
||||
|
||||
m_current_ent = 0;
|
||||
m_ped_model_hash = 0;
|
||||
m_veh_model_hash = 0;
|
||||
m_veh_owned_mods.clear();
|
||||
m_running = false;
|
||||
m_loop_running = false;
|
||||
});
|
||||
}
|
||||
|
||||
void model_preview_service::stop_preview()
|
||||
{
|
||||
m_veh_owned_mods.clear();
|
||||
m_running = false;
|
||||
}
|
||||
}
|
39
src/services/model_preview/model_preview_service.hpp
Normal file
39
src/services/model_preview/model_preview_service.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "file_manager/file.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class model_preview_service
|
||||
{
|
||||
std::condition_variable m_cond;
|
||||
std::mutex m_mutex;
|
||||
|
||||
Entity m_current_ent = 0;
|
||||
|
||||
Hash m_veh_model_hash = 0;
|
||||
std::map<int, int32_t> m_veh_owned_mods;
|
||||
bool m_veh_spawn_max = false;
|
||||
|
||||
Hash m_ped_model_hash = 0;
|
||||
Ped m_ped_clone = 0;
|
||||
|
||||
bool m_new_model = false;
|
||||
float m_heading = 0.f;
|
||||
bool m_loop_running = false;
|
||||
bool m_running = false;
|
||||
public:
|
||||
model_preview_service();
|
||||
~model_preview_service();
|
||||
|
||||
void show_ped(Hash hash);
|
||||
void show_ped(Hash hash, Ped clone);
|
||||
|
||||
void show_vehicle(Hash hash, bool spawn_max);
|
||||
void show_vehicle(const std::map<int, int32_t>& owned_mods, bool spawn_max);
|
||||
|
||||
void preview_loop();
|
||||
void stop_preview();
|
||||
};
|
||||
|
||||
inline model_preview_service* g_model_preview_service{};
|
||||
}
|
58
src/services/notifications/notification_service.cpp
Normal file
58
src/services/notifications/notification_service.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "notification_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
notification_service::notification_service()
|
||||
{
|
||||
g_notification_service = this;
|
||||
}
|
||||
|
||||
notification_service::~notification_service()
|
||||
{
|
||||
g_notification_service = nullptr;
|
||||
}
|
||||
|
||||
void notification_service::push(notification n)
|
||||
{
|
||||
this->notifications.emplace(std::hash<std::string>{}(n.message + n.title), n);
|
||||
}
|
||||
|
||||
void notification_service::push(std::string title, std::string message)
|
||||
{
|
||||
this->push({ NotificationType::INFO, title, message, std::chrono::system_clock::now(), 3000.f , 1.f});
|
||||
}
|
||||
|
||||
void notification_service::push_warning(std::string title, std::string message)
|
||||
{
|
||||
this->push({ NotificationType::WARNING, title, message, std::chrono::system_clock::now(), 3000.f , 1.f });
|
||||
}
|
||||
|
||||
void notification_service::push_error(std::string title, std::string message)
|
||||
{
|
||||
this->push({ NotificationType::DANGER, title, message, std::chrono::system_clock::now(), 3000.f , 1.f });
|
||||
}
|
||||
|
||||
std::vector<notification> notification_service::get()
|
||||
{
|
||||
std::vector<notification> notifications_to_sent;
|
||||
std::vector<std::size_t> to_remove;
|
||||
for (auto& n : this->notifications) {
|
||||
std::chrono::time_point<std::chrono::system_clock> curTime = std::chrono::system_clock::now();
|
||||
const float time_diff = (float)std::chrono::duration_cast<std::chrono::milliseconds>(curTime - n.second.created_on).count();
|
||||
n.second.alpha = 1;
|
||||
if (n.second.destroy_in <= time_diff) {
|
||||
n.second.alpha = 1.f - ((time_diff - n.second.destroy_in) / 600);
|
||||
n.second.alpha = n.second.alpha < 0.f ? 0.f : n.second.alpha;
|
||||
}
|
||||
|
||||
if (n.second.alpha > 0.f)
|
||||
notifications_to_sent.push_back(n.second);
|
||||
else to_remove.push_back(n.first);
|
||||
}
|
||||
for (std::size_t k : to_remove)
|
||||
this->notifications.erase(k);
|
||||
|
||||
return notifications_to_sent;
|
||||
}
|
||||
|
||||
}
|
45
src/services/notifications/notification_service.hpp
Normal file
45
src/services/notifications/notification_service.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
namespace big
|
||||
{
|
||||
enum class NotificationType {
|
||||
INFO,
|
||||
SUCCESS,
|
||||
WARNING,
|
||||
DANGER,
|
||||
};
|
||||
|
||||
struct notification
|
||||
{
|
||||
NotificationType type;
|
||||
const std::string title;
|
||||
const std::string message;
|
||||
const std::chrono::time_point<std::chrono::system_clock> created_on;
|
||||
const float destroy_in;
|
||||
float alpha;
|
||||
};
|
||||
|
||||
class notification_service final
|
||||
{
|
||||
std::unordered_map<std::size_t, notification> notifications;
|
||||
|
||||
public:
|
||||
notification_service();
|
||||
virtual ~notification_service();
|
||||
|
||||
void push(notification);
|
||||
void push(std::string, std::string);
|
||||
void push_warning(std::string, std::string);
|
||||
void push_error(std::string, std::string);
|
||||
std::vector<notification> get();
|
||||
|
||||
std::map<NotificationType, ImVec4> notification_colors = {
|
||||
{NotificationType::INFO, ImVec4(0.80f, 0.80f, 0.83f, 1.00f)},
|
||||
{NotificationType::SUCCESS, ImVec4(0.29f, 0.69f, 0.34f, 1.00f)},
|
||||
{NotificationType::WARNING, ImVec4(0.69f ,0.49f, 0.29f, 1.00f) },
|
||||
{NotificationType::DANGER, ImVec4(0.69f, 0.29f , 0.29f, 1.00f)},
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
inline notification_service* g_notification_service{};
|
||||
}
|
79
src/services/pickups/pickup_service.cpp
Normal file
79
src/services/pickups/pickup_service.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "pickup_service.hpp"
|
||||
#include "gta/joaat.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "script.hpp"
|
||||
#include "services/gta_data/gta_data_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
pickup_service::pickup_service()
|
||||
{
|
||||
g_pickup_service = this;
|
||||
}
|
||||
|
||||
pickup_service::~pickup_service()
|
||||
{
|
||||
g_pickup_service = nullptr;
|
||||
}
|
||||
|
||||
void pickup_service::give_player_ammo(const Player player) const
|
||||
{
|
||||
give_ammo(1 << player);
|
||||
}
|
||||
|
||||
void pickup_service::give_player_armour(const Player player) const
|
||||
{
|
||||
give_armour(1 << player);
|
||||
}
|
||||
|
||||
void pickup_service::give_player_health(const Player player) const
|
||||
{
|
||||
give_health(1 << player);
|
||||
}
|
||||
|
||||
void pickup_service::give_player_weapons(const Player player) const
|
||||
{
|
||||
give_weapons(1 << player);
|
||||
}
|
||||
|
||||
void pickup_service::give_ammo(const int targets) const
|
||||
{
|
||||
for (const auto& [_, weapon] : g_gta_data_service->weapons())
|
||||
{
|
||||
if (weapon.m_reward_ammo_hash != 0 || weapon.m_throwable)
|
||||
{
|
||||
g_pointers->m_give_pickup_rewards(targets, weapon.m_reward_ammo_hash);
|
||||
script::get_current()->yield(20ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pickup_service::give_armour(const int targets) const
|
||||
{
|
||||
g_pointers->m_give_pickup_rewards(targets, RAGE_JOAAT("REWARD_ARMOUR"));
|
||||
script::get_current()->yield(20ms);
|
||||
}
|
||||
|
||||
void pickup_service::give_health(const int targets) const
|
||||
{
|
||||
g_pointers->m_give_pickup_rewards(targets, RAGE_JOAAT("REWARD_HEALTH"));
|
||||
script::get_current()->yield(20ms);
|
||||
g_pointers->m_give_pickup_rewards(targets, RAGE_JOAAT("REWARD_HEALTH"));
|
||||
script::get_current()->yield(20ms);
|
||||
}
|
||||
|
||||
void pickup_service::give_weapons(const int targets) const
|
||||
{
|
||||
for (const auto& [_, weapon] : g_gta_data_service->weapons())
|
||||
{
|
||||
if (weapon.m_reward_hash != 0)
|
||||
{
|
||||
g_pointers->m_give_pickup_rewards(targets, weapon.m_reward_hash);
|
||||
script::get_current()->yield(20ms);
|
||||
}
|
||||
}
|
||||
|
||||
g_pointers->m_give_pickup_rewards(targets, RAGE_JOAAT("REWARD_PARACHUTE"));
|
||||
script::get_current()->yield(20ms);
|
||||
}
|
||||
}
|
28
src/services/pickups/pickup_service.hpp
Normal file
28
src/services/pickups/pickup_service.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
class pickup_service final
|
||||
{
|
||||
public:
|
||||
pickup_service();
|
||||
~pickup_service();
|
||||
|
||||
pickup_service(const pickup_service&) = delete;
|
||||
pickup_service(pickup_service&&) noexcept = delete;
|
||||
pickup_service& operator=(const pickup_service&) = delete;
|
||||
pickup_service& operator=(pickup_service&&) noexcept = delete;
|
||||
|
||||
void give_player_ammo(const Player player) const;
|
||||
void give_player_armour(const Player player) const;
|
||||
void give_player_health(const Player player) const;
|
||||
void give_player_weapons(const Player player) const;
|
||||
|
||||
void give_ammo(const int targets) const;
|
||||
void give_armour(const int targets) const;
|
||||
void give_health(const int targets) const;
|
||||
void give_weapons(const int targets) const;
|
||||
};
|
||||
|
||||
inline pickup_service* g_pickup_service{};
|
||||
}
|
114
src/services/players/player.cpp
Normal file
114
src/services/players/player.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "player.hpp"
|
||||
#include "network/CNetGamePlayer.hpp"
|
||||
#include "services/friends/friends_service.hpp"
|
||||
#include "gta_util.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
player::player(CNetGamePlayer* net_game_player)
|
||||
: m_net_game_player(net_game_player)
|
||||
{
|
||||
m_is_friend = friends_service::is_friend(net_game_player);
|
||||
}
|
||||
|
||||
CVehicle* player::get_current_vehicle() const
|
||||
{
|
||||
if (const auto ped = this->get_ped(); ped != nullptr)
|
||||
if (const auto vehicle = ped->m_vehicle; vehicle != nullptr)
|
||||
return vehicle;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* player::get_name() const
|
||||
{
|
||||
return m_net_game_player == nullptr ? "" : m_net_game_player->get_name();
|
||||
}
|
||||
|
||||
rage::rlGamerInfo* player::get_net_data() const
|
||||
{
|
||||
return m_net_game_player == nullptr ? nullptr : m_net_game_player->get_net_data();
|
||||
}
|
||||
|
||||
CNetGamePlayer* player::get_net_game_player() const
|
||||
{
|
||||
return m_net_game_player;
|
||||
}
|
||||
|
||||
CPed* player::get_ped() const
|
||||
{
|
||||
if (const auto player_info = this->get_player_info(); player_info != nullptr)
|
||||
if (const auto ped = player_info->m_ped; ped != nullptr)
|
||||
return ped;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CPlayerInfo* player::get_player_info() const
|
||||
{
|
||||
if (m_net_game_player != nullptr && m_net_game_player->m_player_info != nullptr)
|
||||
return m_net_game_player->m_player_info;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rage::snPlayer* player::get_session_player()
|
||||
{
|
||||
for (std::uint32_t i = 0; i < gta_util::get_network()->m_game_session_ptr->m_player_count; i++)
|
||||
{
|
||||
if (gta_util::get_network()->m_game_session_ptr->m_players[i]->m_player_data.m_host_token == get_net_data()->m_host_token)
|
||||
{
|
||||
return gta_util::get_network()->m_game_session_ptr->m_players[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (gta_util::get_network()->m_game_session_ptr->m_local_player.m_player_data.m_host_token == get_net_data()->m_host_token)
|
||||
return >a_util::get_network()->m_game_session_ptr->m_local_player;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rage::snPeer* player::get_session_peer()
|
||||
{
|
||||
for (std::uint32_t i = 0; i < gta_util::get_network()->m_game_session_ptr->m_peer_count; i++)
|
||||
{
|
||||
if (gta_util::get_network()->m_game_session_ptr->m_peers[i]->m_peer_data.m_gamer_handle_2.m_rockstar_id == get_net_data()->m_gamer_handle_2.m_rockstar_id)
|
||||
{
|
||||
return gta_util::get_network()->m_game_session_ptr->m_peers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t player::id() const
|
||||
{
|
||||
return m_net_game_player == nullptr ? -1 : m_net_game_player->m_player_id;
|
||||
}
|
||||
|
||||
bool player::is_host() const
|
||||
{
|
||||
return m_net_game_player == nullptr ? false : m_net_game_player->is_host();
|
||||
}
|
||||
|
||||
bool player::is_friend() const
|
||||
{
|
||||
return m_is_friend;
|
||||
}
|
||||
|
||||
|
||||
bool player::is_valid() const
|
||||
{
|
||||
return m_net_game_player == nullptr ? false : m_net_game_player->is_valid();
|
||||
}
|
||||
|
||||
bool player::equals(const CNetGamePlayer* net_game_player) const
|
||||
{
|
||||
return net_game_player == m_net_game_player;
|
||||
}
|
||||
|
||||
std::string player::to_lowercase_identifier() const
|
||||
{
|
||||
std::string lower = this->get_name();
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
return lower;
|
||||
}
|
||||
}
|
54
src/services/players/player.hpp
Normal file
54
src/services/players/player.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "player_service.hpp"
|
||||
#include "vehicle/CVehicle.hpp"
|
||||
#include "network/snSession.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class player final
|
||||
{
|
||||
friend class player_service;
|
||||
|
||||
CNetGamePlayer* m_net_game_player = nullptr;
|
||||
std::string m_identifier;
|
||||
bool m_is_friend;
|
||||
|
||||
public:
|
||||
explicit player(CNetGamePlayer* net_game_player);
|
||||
~player() = default;
|
||||
|
||||
player(const player&) = default;
|
||||
player(player&&) noexcept = default;
|
||||
player& operator=(const player&) = default;
|
||||
player& operator=(player&&) noexcept = default;
|
||||
|
||||
float screen_position_x = -1.f;
|
||||
float screen_position_y = -1.f;
|
||||
|
||||
[[nodiscard]] CVehicle* get_current_vehicle() const;
|
||||
[[nodiscard]] const char* get_name() const;
|
||||
[[nodiscard]] rage::rlGamerInfo* get_net_data() const;
|
||||
[[nodiscard]] CNetGamePlayer* get_net_game_player() const;
|
||||
[[nodiscard]] CPed* get_ped() const;
|
||||
[[nodiscard]] CPlayerInfo* get_player_info() const;
|
||||
[[nodiscard]] class rage::snPlayer* get_session_player();
|
||||
[[nodiscard]] class rage::snPeer* get_session_peer();
|
||||
|
||||
[[nodiscard]] uint8_t id() const;
|
||||
|
||||
[[nodiscard]] bool is_friend() const;
|
||||
[[nodiscard]] bool is_host() const;
|
||||
[[nodiscard]] bool is_valid() const;
|
||||
|
||||
bool never_wanted = false;
|
||||
|
||||
std::chrono::system_clock::time_point m_last_transition_msg_sent{};
|
||||
int m_num_failed_transition_attempts = 0;
|
||||
|
||||
protected:
|
||||
bool equals(const CNetGamePlayer* net_game_player) const;
|
||||
|
||||
[[nodiscard]] std::string to_lowercase_identifier() const;
|
||||
|
||||
};
|
||||
}
|
105
src/services/players/player_service.cpp
Normal file
105
src/services/players/player_service.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "gta_util.hpp"
|
||||
#include "player_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
player_service::player_service()
|
||||
: m_self(), m_selected_player(m_dummy)
|
||||
{
|
||||
g_player_service = this;
|
||||
|
||||
const auto network_player_mgr = gta_util::get_network_player_mgr();
|
||||
if (!network_player_mgr)
|
||||
return;
|
||||
|
||||
m_self = &network_player_mgr->m_local_net_player;
|
||||
|
||||
for (uint16_t i = 0; i < network_player_mgr->m_player_limit; ++i)
|
||||
player_join(network_player_mgr->m_player_list[i]);
|
||||
}
|
||||
|
||||
player_service::~player_service()
|
||||
{
|
||||
g_player_service = nullptr;
|
||||
}
|
||||
|
||||
void player_service::do_cleanup()
|
||||
{
|
||||
m_selected_player = m_dummy;
|
||||
m_players.clear();
|
||||
}
|
||||
|
||||
player_ptr player_service::get_by_name(std::string name)
|
||||
{
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
|
||||
if (const auto it = m_players.find(name); it != m_players.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
player_ptr player_service::get_by_msg_id(uint32_t msg_id) const
|
||||
{
|
||||
for (const auto& [_, player] : m_players)
|
||||
if (player->get_net_game_player()->m_msg_id == msg_id)
|
||||
return player;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
player_ptr player_service::get_by_id(uint32_t id) const
|
||||
{
|
||||
for (const auto& [name, player] : m_players)
|
||||
if (player->id() == id)
|
||||
return player;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
player_ptr player_service::get_by_host_token(uint64_t token) const
|
||||
{
|
||||
for (const auto& [name, player] : m_players)
|
||||
if (player->get_net_data()->m_host_token == token)
|
||||
return player;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
player_ptr player_service::get_selected() const
|
||||
{
|
||||
return m_selected_player;
|
||||
}
|
||||
|
||||
player_ptr player_service::get_self()
|
||||
{
|
||||
if (!m_self_ptr || !m_self_ptr->equals(*m_self))
|
||||
{
|
||||
m_self_ptr = std::make_shared<player>(*m_self);
|
||||
}
|
||||
|
||||
return m_self_ptr;
|
||||
}
|
||||
|
||||
void player_service::player_join(CNetGamePlayer* net_game_player)
|
||||
{
|
||||
if (net_game_player == nullptr || net_game_player == *m_self) return;
|
||||
|
||||
auto plyr = std::make_shared<player>(net_game_player);
|
||||
m_players.emplace(
|
||||
plyr->to_lowercase_identifier(),
|
||||
std::move(plyr)
|
||||
);
|
||||
}
|
||||
|
||||
void player_service::player_leave(CNetGamePlayer* net_game_player)
|
||||
{
|
||||
if (net_game_player == nullptr) return;
|
||||
if (m_selected_player && m_selected_player->equals(net_game_player))
|
||||
m_selected_player = m_dummy;
|
||||
|
||||
auto plyr = std::make_unique<player>(net_game_player);
|
||||
m_players.erase(plyr->to_lowercase_identifier());
|
||||
}
|
||||
|
||||
void player_service::set_selected(player_ptr plyr)
|
||||
{
|
||||
m_selected_player = plyr;
|
||||
}
|
||||
}
|
56
src/services/players/player_service.hpp
Normal file
56
src/services/players/player_service.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "player.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class player;
|
||||
|
||||
using player_ptr = std::shared_ptr<player>;
|
||||
using player_entry = std::pair<std::string, player_ptr>;
|
||||
using players = std::map<std::string, player_ptr>;
|
||||
|
||||
class player_service final
|
||||
{
|
||||
CNetGamePlayer** m_self;
|
||||
|
||||
player_ptr m_self_ptr;
|
||||
|
||||
players m_players;
|
||||
|
||||
player_ptr m_dummy = std::make_shared<player>(nullptr);
|
||||
player_ptr m_selected_player;
|
||||
public:
|
||||
|
||||
player_service();
|
||||
~player_service();
|
||||
|
||||
player_service(const player_service&) = delete;
|
||||
player_service(player_service&&) noexcept = delete;
|
||||
player_service& operator=(const player_service&) = delete;
|
||||
player_service& operator=(player_service&&) noexcept = delete;
|
||||
|
||||
void do_cleanup();
|
||||
|
||||
[[nodiscard]] player_ptr get_self();
|
||||
|
||||
[[nodiscard]] player_ptr get_by_name(std::string name);
|
||||
[[nodiscard]] player_ptr get_by_msg_id(uint32_t msg_id) const;
|
||||
[[nodiscard]] player_ptr get_by_id(uint32_t id) const;
|
||||
[[nodiscard]] player_ptr get_by_host_token(uint64_t token) const;
|
||||
[[nodiscard]] player_ptr get_selected() const;
|
||||
|
||||
void player_join(CNetGamePlayer* net_game_player);
|
||||
void player_leave(CNetGamePlayer* net_game_player);
|
||||
|
||||
players& players()
|
||||
{ return m_players; }
|
||||
|
||||
void iterate(const std::function< void(const player_entry &entry) > func)
|
||||
{ for (const auto &iter : m_players) func(iter); }
|
||||
|
||||
void set_selected(player_ptr plyr);
|
||||
|
||||
};
|
||||
|
||||
inline player_service* g_player_service{};
|
||||
}
|
21
src/services/vehicle/handling_profile.cpp
Normal file
21
src/services/vehicle/handling_profile.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "handling_profile.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
handling_profile::handling_profile(CVehicle* vehicle)
|
||||
{
|
||||
m_gravity = vehicle->m_gravity;
|
||||
m_handling_data = *vehicle->m_handling_data;
|
||||
}
|
||||
|
||||
void handling_profile::apply_to(CVehicle* vehicle, bool restore_hash) const
|
||||
{
|
||||
const auto hash = vehicle->m_handling_data->m_model_hash;
|
||||
|
||||
vehicle->m_gravity = m_gravity;
|
||||
*vehicle->m_handling_data = m_handling_data;
|
||||
|
||||
if (restore_hash)
|
||||
vehicle->m_handling_data->m_model_hash = hash;
|
||||
}
|
||||
}
|
19
src/services/vehicle/handling_profile.hpp
Normal file
19
src/services/vehicle/handling_profile.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "vehicle/CVehicle.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class handling_profile final
|
||||
{
|
||||
public:
|
||||
handling_profile(CVehicle* vehicle);
|
||||
handling_profile() = default;
|
||||
|
||||
void apply_to(CVehicle* vehicle, bool restore_hash = true) const;
|
||||
|
||||
private:
|
||||
float m_gravity;
|
||||
CHandlingData m_handling_data;
|
||||
|
||||
};
|
||||
}
|
108
src/services/vehicle/handling_service.cpp
Normal file
108
src/services/vehicle/handling_service.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "gta_util.hpp"
|
||||
#include "handling_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
handling_service::handling_service() :
|
||||
m_profiles_folder(g_file_manager->get_project_folder("./handling_profiles"))
|
||||
{
|
||||
g_handling_service = this;
|
||||
|
||||
load_files();
|
||||
}
|
||||
|
||||
handling_service::~handling_service()
|
||||
{
|
||||
g_handling_service = nullptr;
|
||||
}
|
||||
|
||||
std::size_t handling_service::load_files()
|
||||
{
|
||||
std::size_t files_loaded{};
|
||||
|
||||
for (const auto& item : std::filesystem::directory_iterator(m_profiles_folder.get_path()))
|
||||
{
|
||||
if (!item.is_regular_file())
|
||||
continue;
|
||||
if (auto file_path = item.path(); file_path.extension() == ".bin")
|
||||
{
|
||||
auto profile = std::make_unique<handling_profile>();
|
||||
auto profile_file = std::ifstream(file_path, std::ios::binary);
|
||||
profile_file.read(reinterpret_cast<char*>(profile.get()), sizeof(handling_profile));
|
||||
profile_file.close();
|
||||
|
||||
m_handling_profiles.emplace(file_path.stem().string(), std::move(profile));
|
||||
|
||||
++files_loaded;
|
||||
}
|
||||
}
|
||||
|
||||
return files_loaded;
|
||||
}
|
||||
|
||||
handling_profiles& handling_service::profiles()
|
||||
{
|
||||
return m_handling_profiles;
|
||||
}
|
||||
|
||||
handling_profile* handling_service::active_profile()
|
||||
{
|
||||
return m_active_profile;
|
||||
}
|
||||
|
||||
void handling_service::apply_profile(handling_profile* profile)
|
||||
{
|
||||
if (const auto vehicle = gta_util::get_local_vehicle(); vehicle)
|
||||
{
|
||||
profile->apply_to(vehicle);
|
||||
m_active_profile = profile;
|
||||
}
|
||||
}
|
||||
|
||||
bool handling_service::save_profile(std::string name)
|
||||
{
|
||||
const auto vehicle = gta_util::get_local_vehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
|
||||
name += ".bin";
|
||||
const auto save = m_profiles_folder.get_file(name);
|
||||
|
||||
auto profile = std::make_unique<handling_profile>(vehicle);
|
||||
|
||||
auto save_file = std::ofstream(save.get_path(), std::ios::binary);
|
||||
save_file.write(reinterpret_cast<const char*>(profile.get()), sizeof(handling_profile));
|
||||
save_file.close();
|
||||
|
||||
m_handling_profiles.emplace(name, std::move(profile));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handling_service::backup_vehicle()
|
||||
{
|
||||
const auto vehicle = gta_util::get_local_vehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
|
||||
const auto hash = vehicle->m_handling_data->m_model_hash;
|
||||
if (const auto& it = m_vehicle_backups.find(hash); it != m_vehicle_backups.end())
|
||||
return false;
|
||||
|
||||
m_vehicle_backups.emplace(hash, std::make_unique<handling_profile>(vehicle));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void handling_service::restore_vehicle() const
|
||||
{
|
||||
const auto vehicle = gta_util::get_local_vehicle();
|
||||
if (!vehicle)
|
||||
return;
|
||||
|
||||
if (const auto& it = m_vehicle_backups.find(vehicle->m_handling_data->m_model_hash); it != m_vehicle_backups.end())
|
||||
{
|
||||
it->second->apply_to(vehicle);
|
||||
}
|
||||
}
|
||||
}
|
40
src/services/vehicle/handling_service.hpp
Normal file
40
src/services/vehicle/handling_service.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "handling_profile.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
using handling_profiles = std::map<std::string, std::unique_ptr<handling_profile>>;
|
||||
class handling_service final
|
||||
{
|
||||
public:
|
||||
handling_service();
|
||||
~handling_service();
|
||||
|
||||
handling_service(const handling_service&) = delete;
|
||||
handling_service(handling_service&&) noexcept = delete;
|
||||
handling_service& operator=(const handling_service&) = delete;
|
||||
handling_service& operator=(handling_service&&) noexcept = delete;
|
||||
|
||||
std::size_t load_files();
|
||||
handling_profiles& profiles();
|
||||
|
||||
handling_profile* active_profile();
|
||||
void apply_profile(handling_profile* profile);
|
||||
bool save_profile(std::string name);
|
||||
|
||||
bool backup_vehicle();
|
||||
void restore_vehicle() const;
|
||||
|
||||
private:
|
||||
const folder m_profiles_folder;
|
||||
|
||||
handling_profile* m_active_profile;
|
||||
handling_profiles m_handling_profiles;
|
||||
|
||||
// contains the handling profiles of a vehicles before they're been modified
|
||||
std::unordered_map<std::uint32_t, std::unique_ptr<handling_profile>> m_vehicle_backups;
|
||||
|
||||
};
|
||||
|
||||
inline handling_service* g_handling_service{};
|
||||
}
|
504
src/services/vehicle/persist_car_service.cpp
Normal file
504
src/services/vehicle/persist_car_service.cpp
Normal file
@ -0,0 +1,504 @@
|
||||
#include "persist_car_service.hpp"
|
||||
#include "util/vehicle.hpp"
|
||||
#include "util/world_model.hpp"
|
||||
#include "util/misc.hpp"
|
||||
#include "vehicle/CVehicle.hpp"
|
||||
#include "base/CObject.hpp"
|
||||
#include "pointers.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void persist_car_service::save_vehicle(Vehicle vehicle, std::string_view file_name)
|
||||
{
|
||||
if (!ENTITY::DOES_ENTITY_EXIST(vehicle) ||
|
||||
!ENTITY::IS_ENTITY_A_VEHICLE(vehicle))
|
||||
{
|
||||
g_notification_service->push_warning("Persist Car", "Tried to save a vehicle which does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file = check_vehicle_folder().get_file(file_name);
|
||||
|
||||
std::ofstream file_stream(file.get_path(), std::ios::out | std::ios::trunc);
|
||||
|
||||
file_stream << get_full_vehicle_json(vehicle).dump(4);
|
||||
|
||||
file_stream.close();
|
||||
}
|
||||
|
||||
Vehicle persist_car_service::load_vehicle(std::string_view file_name)
|
||||
{
|
||||
const auto file = check_vehicle_folder().get_file(file_name);
|
||||
|
||||
std::ifstream file_stream(file.get_path());
|
||||
|
||||
nlohmann::json vehicle_json;
|
||||
|
||||
file_stream >> vehicle_json;
|
||||
file_stream.close();
|
||||
|
||||
return spawn_vehicle_full(vehicle_json, self::ped);
|
||||
}
|
||||
|
||||
std::vector<std::string> persist_car_service::list_files()
|
||||
{
|
||||
std::vector<std::string> file_paths;
|
||||
|
||||
const auto file_path = check_vehicle_folder();
|
||||
for (const auto& directory_entry : std::filesystem::directory_iterator(file_path.get_path()))
|
||||
if (directory_entry.path().extension() == ".json")
|
||||
file_paths.push_back(directory_entry.path().filename().generic_string());
|
||||
|
||||
return file_paths;
|
||||
}
|
||||
|
||||
Vehicle persist_car_service::clone_ped_car(Ped ped, Vehicle vehicle)
|
||||
{
|
||||
return spawn_vehicle_full(get_full_vehicle_json(vehicle), ped);
|
||||
}
|
||||
|
||||
Vehicle persist_car_service::spawn_vehicle_full(nlohmann::json vehicle_json, Ped ped)
|
||||
{
|
||||
const auto vehicle = spawn_vehicle(vehicle_json, ped);
|
||||
|
||||
if (!vehicle_json[tow_key].is_null())
|
||||
{
|
||||
const auto tow = spawn_vehicle(vehicle_json[tow_key], ped);
|
||||
|
||||
auto pos = ENTITY::GET_ENTITY_COORDS(tow, true);
|
||||
pos.x -= 10;
|
||||
|
||||
ENTITY::SET_ENTITY_COORDS_NO_OFFSET(tow, pos.x, pos.y, 0.f, true, true, false);
|
||||
|
||||
VEHICLE::ATTACH_VEHICLE_TO_TOW_TRUCK(vehicle, tow, -1, 0.f, 0.5f, 0.f);
|
||||
VEHICLE::SET_VEHICLE_TOW_TRUCK_ARM_POSITION(vehicle, 1.f);
|
||||
|
||||
const auto rotation = ENTITY::GET_ENTITY_ROTATION(tow, 2);
|
||||
ENTITY::SET_ENTITY_ROTATION(tow, 0, 0, rotation.z, 2, true);
|
||||
}
|
||||
else if (!vehicle_json[trailer_key].is_null())
|
||||
{
|
||||
const auto trailer = spawn_vehicle(vehicle_json[trailer_key], ped);
|
||||
VEHICLE::ATTACH_VEHICLE_TO_TRAILER(vehicle, trailer, 1.0f);
|
||||
|
||||
const auto rotation = ENTITY::GET_ENTITY_ROTATION(trailer, 2);
|
||||
ENTITY::SET_ENTITY_ROTATION(trailer, 0, 0, rotation.z, 2, true);
|
||||
}
|
||||
|
||||
const auto rotation = ENTITY::GET_ENTITY_ROTATION(vehicle, 2);
|
||||
ENTITY::SET_ENTITY_ROTATION(vehicle, rotation.x, 0, rotation.z, 2, true);
|
||||
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
Vehicle persist_car_service::spawn_vehicle(nlohmann::json vehicle_json, Ped ped)
|
||||
{
|
||||
const auto vehicle = spawn_vehicle_json(vehicle_json, ped);
|
||||
|
||||
std::vector<nlohmann::json> model_attachments = vehicle_json[model_attachments_key];
|
||||
for (const auto& j : model_attachments)
|
||||
{
|
||||
const auto attachment = j.get<model_attachment>();
|
||||
const auto object = world_model::spawn(attachment.model_hash);
|
||||
if (object)
|
||||
{
|
||||
ENTITY::ATTACH_ENTITY_TO_ENTITY(
|
||||
object, vehicle,
|
||||
0,
|
||||
attachment.position.x, attachment.position.y, attachment.position.z,
|
||||
attachment.rotation.x, attachment.rotation.y, attachment.rotation.z,
|
||||
false, false, false, false, 0, true, 0);
|
||||
|
||||
ENTITY::SET_ENTITY_VISIBLE(object, attachment.is_visible, 0);
|
||||
ENTITY::SET_ENTITY_COLLISION(object, attachment.has_collision, true);
|
||||
ENTITY::SET_ENTITY_INVINCIBLE(object, attachment.is_invincible);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<nlohmann::json> vehicle_attachments = vehicle_json[vehicle_attachments_key];
|
||||
for (const auto& j : vehicle_attachments)
|
||||
{
|
||||
const auto vehicle_to_attach = spawn_vehicle_json(j[vehicle_key], ped);
|
||||
auto attachment = j[model_attachment_key].get<big::model_attachment>();
|
||||
ENTITY::ATTACH_ENTITY_TO_ENTITY(
|
||||
vehicle_to_attach, vehicle,
|
||||
0,
|
||||
attachment.position.x, attachment.position.y, attachment.position.z,
|
||||
attachment.rotation.x, attachment.rotation.y, attachment.rotation.z,
|
||||
|
||||
false, false, false, false, 0, true, 0);
|
||||
|
||||
VEHICLE::SET_VEHICLE_IS_CONSIDERED_BY_PLAYER(vehicle_to_attach, false);
|
||||
}
|
||||
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
Vehicle persist_car_service::spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped)
|
||||
{
|
||||
const Hash vehicle_hash = vehicle_json[vehicle_model_hash_key];
|
||||
|
||||
const auto pos = ENTITY::GET_ENTITY_COORDS(self::ped, true);
|
||||
|
||||
const auto vehicle = big::vehicle::spawn(vehicle_hash, pos, ENTITY::GET_ENTITY_HEADING(ped));
|
||||
|
||||
VEHICLE::SET_VEHICLE_DIRT_LEVEL(vehicle, 0.0f);
|
||||
VEHICLE::SET_VEHICLE_MOD_KIT(vehicle, 0);
|
||||
VEHICLE::SET_VEHICLE_TYRES_CAN_BURST(vehicle, false);
|
||||
VEHICLE::SET_VEHICLE_COLOURS(vehicle, vehicle_json[primary_color_key], vehicle_json[secondary_color_key]);
|
||||
|
||||
if (!vehicle_json[custom_primary_color_key].is_null())
|
||||
{
|
||||
std::vector<int> primary_custom_color = vehicle_json[custom_primary_color_key];
|
||||
VEHICLE::SET_VEHICLE_CUSTOM_PRIMARY_COLOUR(vehicle, primary_custom_color[0], primary_custom_color[1], primary_custom_color[2]);
|
||||
}
|
||||
|
||||
if (!vehicle_json[is_visible_key].is_null())
|
||||
{
|
||||
bool is_visible = vehicle_json[is_visible_key];
|
||||
ENTITY::SET_ENTITY_VISIBLE(vehicle, is_visible, 0);
|
||||
}
|
||||
|
||||
if (!vehicle_json[has_collision_key].is_null())
|
||||
{
|
||||
bool has_collision = vehicle_json[has_collision_key];
|
||||
ENTITY::SET_ENTITY_COLLISION(vehicle, has_collision, true);
|
||||
}
|
||||
|
||||
if (!vehicle_json[is_invincible_key].is_null())
|
||||
{
|
||||
bool is_invincible = vehicle_json[is_invincible_key];
|
||||
ENTITY::SET_ENTITY_INVINCIBLE(vehicle, is_invincible);
|
||||
}
|
||||
|
||||
if (!vehicle_json[custom_secondary_color_key].is_null())
|
||||
{
|
||||
std::vector<int> secondary_custom_color = vehicle_json[custom_secondary_color_key];
|
||||
VEHICLE::SET_VEHICLE_CUSTOM_SECONDARY_COLOUR(vehicle, secondary_custom_color[0], secondary_custom_color[1], secondary_custom_color[2]);
|
||||
}
|
||||
|
||||
VEHICLE::SET_VEHICLE_WINDOW_TINT(vehicle, vehicle_json[vehicle_window_tint_key]);
|
||||
|
||||
if (!vehicle_json[radio_station_key].is_null())
|
||||
AUDIO::SET_VEH_RADIO_STATION(vehicle, vehicle_json[radio_station_key].get<std::string>().c_str());
|
||||
|
||||
VEHICLE::SET_VEHICLE_NUMBER_PLATE_TEXT(vehicle, vehicle_json[plate_text_key].get<std::string>().c_str());
|
||||
VEHICLE::SET_VEHICLE_NUMBER_PLATE_TEXT_INDEX(vehicle, vehicle_json[plate_text_index_key]);
|
||||
VEHICLE::SET_VEHICLE_EXTRA_COLOURS(vehicle, vehicle_json[pearlescent_color_key], vehicle_json[wheel_color_key]);
|
||||
|
||||
std::map<int, bool> vehicle_extras = vehicle_json[vehicle_extras_key];
|
||||
for (int i = 0; i <= 20; i++)
|
||||
{
|
||||
if (VEHICLE::DOES_EXTRA_EXIST(vehicle, i))
|
||||
VEHICLE::SET_VEHICLE_EXTRA(vehicle, i, vehicle_extras[i]);
|
||||
}
|
||||
|
||||
if (!vehicle_json[vehicle_livery_key].is_null())
|
||||
{
|
||||
VEHICLE::SET_VEHICLE_LIVERY(vehicle, vehicle_json[vehicle_livery_key]);
|
||||
}
|
||||
|
||||
if (VEHICLE::IS_THIS_MODEL_A_CAR(ENTITY::GET_ENTITY_MODEL(vehicle)) || VEHICLE::IS_THIS_MODEL_A_BIKE(ENTITY::GET_ENTITY_MODEL(vehicle)))
|
||||
{
|
||||
VEHICLE::SET_VEHICLE_WHEEL_TYPE(vehicle, vehicle_json[wheel_type_key]);
|
||||
for (int i = MOD_SPOILERS; i < MOD_LIGHTBAR; i++)
|
||||
{
|
||||
const bool has_mod = !vehicle_json[mod_names[i]].is_null();
|
||||
if (has_mod)
|
||||
{
|
||||
if (i == MOD_TYRE_SMOKE)
|
||||
{
|
||||
std::vector<int> tire_smoke_color = vehicle_json[tire_smoke_color_key];
|
||||
VEHICLE::SET_VEHICLE_TYRE_SMOKE_COLOR(vehicle, tire_smoke_color[0], tire_smoke_color[1], tire_smoke_color[2]);
|
||||
VEHICLE::TOGGLE_VEHICLE_MOD(vehicle, MOD_TYRE_SMOKE, true);
|
||||
}
|
||||
else if (vehicle_json[mod_names[i]].is_array())
|
||||
{
|
||||
std::vector<int> mod = vehicle_json[mod_names[i]];
|
||||
VEHICLE::SET_VEHICLE_MOD(vehicle, i, mod[0], mod[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
VEHICLE::TOGGLE_VEHICLE_MOD(vehicle, i, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<bool> neon_lights = vehicle_json[neon_lights_key];
|
||||
for (int i = NEON_LEFT; i <= NEON_BACK; i++)
|
||||
VEHICLE::SET_VEHICLE_NEON_ENABLED(vehicle, i, neon_lights[i]);
|
||||
std::vector<int> neon_color = vehicle_json[neon_color_key];
|
||||
VEHICLE::SET_VEHICLE_NEON_COLOUR(vehicle, neon_color[0], neon_color[1], neon_color[2]);
|
||||
|
||||
if (VEHICLE::IS_VEHICLE_A_CONVERTIBLE(vehicle, 0))
|
||||
{
|
||||
int convertable_state = vehicle_json[convertable_state_key];
|
||||
if (convertable_state == 0 || convertable_state == 3 || convertable_state == 5)
|
||||
VEHICLE::RAISE_CONVERTIBLE_ROOF(vehicle, true);
|
||||
else
|
||||
VEHICLE::LOWER_CONVERTIBLE_ROOF(vehicle, true);
|
||||
}
|
||||
|
||||
VEHICLE::SET_VEHICLE_EXTRA_COLOUR_5(vehicle, vehicle_json[interior_color_key]);
|
||||
|
||||
VEHICLE::SET_VEHICLE_EXTRA_COLOUR_6(vehicle, vehicle_json[dash_color_key]);
|
||||
|
||||
const BOOL have_clan_logo = vehicle_json[clan_logo_key];
|
||||
if (have_clan_logo)
|
||||
vehicle_helper::add_clan_logo_to_vehicle(vehicle, ped);
|
||||
|
||||
VEHICLE::SET_VEHICLE_XENON_LIGHT_COLOR_INDEX(vehicle, vehicle_json[headlight_color_key]);
|
||||
}
|
||||
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
nlohmann::json persist_car_service::get_full_vehicle_json(Vehicle vehicle)
|
||||
{
|
||||
// The car needs to be rotated at (0, 0, 0) for the relative offset calculations to be accurate.
|
||||
ENTITY::SET_ENTITY_ROTATION(vehicle, 0, 0, 0, 0, true);
|
||||
script::get_current()->yield();
|
||||
ENTITY::SET_ENTITY_ROTATION(vehicle, 0, 0, 0, 0, true);
|
||||
|
||||
nlohmann::json vehicle_json = get_vehicle_json(vehicle);
|
||||
|
||||
vehicle_json[model_attachments_key] = get_model_attachments(vehicle);
|
||||
vehicle_json[vehicle_attachments_key] = get_vehicle_attachents(vehicle);
|
||||
|
||||
Vehicle tow = VEHICLE::GET_ENTITY_ATTACHED_TO_TOW_TRUCK(vehicle);
|
||||
if (ENTITY::DOES_ENTITY_EXIST(tow))
|
||||
{
|
||||
vehicle_json[tow_key] = get_vehicle_json(tow);
|
||||
vehicle_json[tow_key][model_attachments_key] = get_model_attachments(tow, true);
|
||||
vehicle_json[tow_key][vehicle_attachments_key] = get_vehicle_attachents(tow);
|
||||
}
|
||||
if (VEHICLE::IS_VEHICLE_ATTACHED_TO_TRAILER(vehicle))
|
||||
{
|
||||
Vehicle trailer;
|
||||
VEHICLE::GET_VEHICLE_TRAILER_VEHICLE(vehicle, &trailer);
|
||||
|
||||
vehicle_json[trailer_key] = get_vehicle_json(trailer);
|
||||
vehicle_json[trailer_key][model_attachments_key] = get_model_attachments(trailer);
|
||||
vehicle_json[trailer_key][vehicle_attachments_key] = get_vehicle_attachents(trailer);
|
||||
}
|
||||
|
||||
return vehicle_json;
|
||||
}
|
||||
|
||||
model_attachment persist_car_service::get_model_attachment(Vehicle vehicle, Object object)
|
||||
{
|
||||
const auto object_location = ENTITY::GET_ENTITY_COORDS(object, 0);
|
||||
const auto location = ENTITY::GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(vehicle, object_location.x, object_location.y, object_location.z);
|
||||
|
||||
const auto object_rotation = ENTITY::GET_ENTITY_ROTATION(object, 0);
|
||||
const auto vehicle_rotation = ENTITY::GET_ENTITY_ROTATION(vehicle, 0);
|
||||
bool has_collision = ENTITY::GET_ENTITY_COLLISION_DISABLED(object);
|
||||
bool is_visible = ENTITY::IS_ENTITY_VISIBLE(object);
|
||||
CObject* cobject = (CObject*)g_pointers->m_get_script_handle(vehicle);
|
||||
bool is_invincible = misc::has_bit_set(&(int&)cobject->m_damage_bits, 8);
|
||||
|
||||
Vector3 rotation;
|
||||
rotation.x = (object_rotation.x - vehicle_rotation.x);
|
||||
rotation.y = (object_rotation.y - vehicle_rotation.y);
|
||||
rotation.z = (object_rotation.z - vehicle_rotation.z);
|
||||
|
||||
model_attachment attachment = { ENTITY::GET_ENTITY_MODEL(object), location, rotation, !has_collision, is_visible, is_invincible };
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
nlohmann::json persist_car_service::get_model_attachments(Vehicle vehicle, bool is_towed_vehicle)
|
||||
{
|
||||
const auto replay_interface = *g_pointers->m_replay_interface;
|
||||
|
||||
std::vector<nlohmann::json> attached_objects;
|
||||
|
||||
const auto object_interface = replay_interface->m_object_interface;
|
||||
for (int i = 0; i < object_interface->m_max_objects; i++)
|
||||
{
|
||||
const auto object_ptr = object_interface->get_object(i);
|
||||
if (!object_ptr)
|
||||
continue;
|
||||
|
||||
const auto object = g_pointers->m_ptr_to_handle(object_ptr);
|
||||
if (!object)
|
||||
break;
|
||||
|
||||
if (!ENTITY::IS_ENTITY_ATTACHED_TO_ENTITY(vehicle, object))
|
||||
continue;
|
||||
|
||||
// Don't save tow hook.
|
||||
if (is_towed_vehicle && ENTITY::GET_ENTITY_MODEL(object) == 0xBC344305)
|
||||
continue;
|
||||
|
||||
attached_objects.push_back(get_model_attachment(vehicle, object));
|
||||
};
|
||||
|
||||
return attached_objects;
|
||||
}
|
||||
|
||||
nlohmann::json persist_car_service::get_vehicle_attachents(Vehicle vehicle)
|
||||
{
|
||||
const auto replay_interface = *g_pointers->m_replay_interface;
|
||||
|
||||
const auto vehicle_interface = replay_interface->m_vehicle_interface;
|
||||
|
||||
std::vector<nlohmann::json> attached_vehicles;
|
||||
|
||||
Vehicle trailer;
|
||||
VEHICLE::GET_VEHICLE_TRAILER_VEHICLE(vehicle, &trailer);
|
||||
|
||||
for (int i = 0; i < vehicle_interface->m_max_vehicles; i++)
|
||||
{
|
||||
const auto vehicle_ptr = vehicle_interface->get_vehicle(i);
|
||||
if (!vehicle_ptr)
|
||||
continue;
|
||||
|
||||
const auto object = g_pointers->m_ptr_to_handle(vehicle_ptr);
|
||||
if (!object)
|
||||
break;
|
||||
|
||||
if (!ENTITY::IS_ENTITY_ATTACHED_TO_ENTITY(vehicle, object))
|
||||
continue;
|
||||
|
||||
if (object == VEHICLE::GET_ENTITY_ATTACHED_TO_TOW_TRUCK(vehicle) ||
|
||||
VEHICLE::IS_VEHICLE_ATTACHED_TO_TOW_TRUCK(object, vehicle))
|
||||
continue;
|
||||
|
||||
if (object == trailer ||
|
||||
VEHICLE::IS_VEHICLE_ATTACHED_TO_TRAILER(object))
|
||||
continue;
|
||||
|
||||
nlohmann::json model_attachment;
|
||||
model_attachment[vehicle_key] = get_vehicle_json(object);
|
||||
model_attachment[model_attachment_key] = get_model_attachment(vehicle, object);
|
||||
|
||||
attached_vehicles.push_back(model_attachment);
|
||||
}
|
||||
|
||||
return attached_vehicles;
|
||||
}
|
||||
|
||||
nlohmann::json persist_car_service::get_vehicle_json(Vehicle vehicle)
|
||||
{
|
||||
nlohmann::json vehicle_json;
|
||||
|
||||
vehicle_json[vehicle_model_hash_key] = ENTITY::GET_ENTITY_MODEL(vehicle);
|
||||
|
||||
int primary_color, secondary_color;
|
||||
VEHICLE::GET_VEHICLE_COLOURS(vehicle, &primary_color, &secondary_color);
|
||||
vehicle_json[primary_color_key] = primary_color;
|
||||
vehicle_json[secondary_color_key] = secondary_color;
|
||||
if (VEHICLE::GET_IS_VEHICLE_PRIMARY_COLOUR_CUSTOM(vehicle))
|
||||
{
|
||||
int custom_primary_color[3]{};
|
||||
VEHICLE::GET_VEHICLE_CUSTOM_PRIMARY_COLOUR(vehicle, &custom_primary_color[0], &custom_primary_color[1], &custom_primary_color[2]);
|
||||
vehicle_json[custom_primary_color_key] = custom_primary_color;
|
||||
}
|
||||
|
||||
if (VEHICLE::GET_IS_VEHICLE_SECONDARY_COLOUR_CUSTOM(vehicle))
|
||||
{
|
||||
int custom_secondary_color[3]{};
|
||||
VEHICLE::GET_VEHICLE_CUSTOM_SECONDARY_COLOUR(vehicle, &custom_secondary_color[0], &custom_secondary_color[1], &custom_secondary_color[2]);
|
||||
vehicle_json[custom_secondary_color_key] = custom_secondary_color;
|
||||
}
|
||||
|
||||
vehicle_json[vehicle_window_tint_key] = VEHICLE::GET_VEHICLE_WINDOW_TINT(vehicle);
|
||||
|
||||
auto radio_station = AUDIO::GET_PLAYER_RADIO_STATION_NAME();
|
||||
|
||||
if (!radio_station)
|
||||
radio_station = "OFF";
|
||||
|
||||
vehicle_json[radio_station_key] = radio_station;
|
||||
|
||||
vehicle_json[plate_text_key] = VEHICLE::GET_VEHICLE_NUMBER_PLATE_TEXT(vehicle);
|
||||
vehicle_json[plate_text_index_key] = VEHICLE::GET_VEHICLE_NUMBER_PLATE_TEXT_INDEX(vehicle);
|
||||
|
||||
int pearlescent_color, wheel_color;
|
||||
VEHICLE::GET_VEHICLE_EXTRA_COLOURS(vehicle, &pearlescent_color, &wheel_color);
|
||||
|
||||
vehicle_json[pearlescent_color_key] = pearlescent_color;
|
||||
bool has_collision = ENTITY::GET_ENTITY_COLLISION_DISABLED(vehicle);
|
||||
bool is_visible = ENTITY::IS_ENTITY_VISIBLE(vehicle);
|
||||
CVehicle* cvehicle = (CVehicle*)g_pointers->m_get_script_handle(vehicle);
|
||||
bool is_invincible = misc::has_bit_set(&(int&)cvehicle->m_damage_bits, 8);
|
||||
vehicle_json[has_collision_key] = !has_collision;
|
||||
vehicle_json[is_visible_key] = is_visible;
|
||||
vehicle_json[is_invincible_key] = is_invincible;
|
||||
vehicle_json[wheel_color_key] = wheel_color;
|
||||
|
||||
std::map<int, bool> vehicle_extras;
|
||||
for (int i = 0; i <= 20; i++)
|
||||
{
|
||||
if (VEHICLE::DOES_EXTRA_EXIST(vehicle, i))
|
||||
vehicle_extras[i] = !VEHICLE::IS_VEHICLE_EXTRA_TURNED_ON(vehicle, i);
|
||||
}
|
||||
|
||||
vehicle_json[vehicle_extras_key] = vehicle_extras;
|
||||
|
||||
if ((VEHICLE::GET_VEHICLE_LIVERY_COUNT(vehicle) > 1) && VEHICLE::GET_VEHICLE_LIVERY(vehicle) >= 0)
|
||||
{
|
||||
vehicle_json[vehicle_livery_key] = VEHICLE::GET_VEHICLE_LIVERY(vehicle);
|
||||
}
|
||||
|
||||
if (VEHICLE::IS_THIS_MODEL_A_CAR(ENTITY::GET_ENTITY_MODEL(vehicle)) ||
|
||||
VEHICLE::IS_THIS_MODEL_A_BIKE(ENTITY::GET_ENTITY_MODEL(vehicle)))
|
||||
{
|
||||
vehicle_json[wheel_type_key] = VEHICLE::GET_VEHICLE_WHEEL_TYPE(vehicle);
|
||||
|
||||
for (int i = MOD_SPOILERS; i <= MOD_LIVERY; i++)
|
||||
{
|
||||
const auto is_mod_on = VEHICLE::IS_TOGGLE_MOD_ON(vehicle, i);
|
||||
if (is_mod_on)
|
||||
{
|
||||
if (i == MOD_TYRE_SMOKE)
|
||||
{
|
||||
int tire_smoke_color[3]{};
|
||||
VEHICLE::GET_VEHICLE_TYRE_SMOKE_COLOR(vehicle, &tire_smoke_color[0], &tire_smoke_color[1], &tire_smoke_color[2]);
|
||||
vehicle_json[tire_smoke_color_key] = tire_smoke_color;
|
||||
}
|
||||
else
|
||||
{
|
||||
vehicle_json[mod_names[i]] = "TOGGLE";
|
||||
}
|
||||
}
|
||||
|
||||
if (VEHICLE::GET_VEHICLE_MOD(vehicle, i) != -1)
|
||||
{
|
||||
int vehicle_mod[2] = { VEHICLE::GET_VEHICLE_MOD(vehicle, i), VEHICLE::GET_VEHICLE_MOD_VARIATION(vehicle, i) };
|
||||
vehicle_json[mod_names[i]] = vehicle_mod;
|
||||
}
|
||||
}
|
||||
|
||||
bool neon_lights[4]{};
|
||||
for (int i = NEON_LEFT; i <= NEON_BACK; i++)
|
||||
neon_lights[i] = VEHICLE::GET_VEHICLE_NEON_ENABLED(vehicle, i);
|
||||
|
||||
int neon_color[3]{};
|
||||
VEHICLE::GET_VEHICLE_NEON_COLOUR(vehicle, &neon_color[0], &neon_color[1], &neon_color[2]);
|
||||
vehicle_json[neon_color_key] = neon_color;
|
||||
vehicle_json[neon_lights_key] = neon_lights;
|
||||
|
||||
if (VEHICLE::IS_VEHICLE_A_CONVERTIBLE(vehicle, 0))
|
||||
vehicle_json[convertable_state_key] = VEHICLE::GET_CONVERTIBLE_ROOF_STATE(vehicle);
|
||||
|
||||
int interior_color, dashboard_color;
|
||||
VEHICLE::GET_VEHICLE_EXTRA_COLOUR_5(vehicle, &interior_color);
|
||||
VEHICLE::GET_VEHICLE_EXTRA_COLOUR_6(vehicle, &dashboard_color);
|
||||
vehicle_json[interior_color_key] = interior_color;
|
||||
vehicle_json[dash_color_key] = dashboard_color;
|
||||
|
||||
vehicle_json[clan_logo_key] = GRAPHICS::DOES_VEHICLE_HAVE_CREW_EMBLEM(vehicle, 0);
|
||||
vehicle_json[headlight_color_key] = VEHICLE::GET_VEHICLE_XENON_LIGHT_COLOR_INDEX(vehicle);
|
||||
}
|
||||
|
||||
return vehicle_json;
|
||||
}
|
||||
|
||||
big::folder persist_car_service::check_vehicle_folder()
|
||||
{
|
||||
const auto folder = g_file_manager->get_project_folder("./saved_json_vehicles");
|
||||
|
||||
return folder;
|
||||
}
|
||||
}
|
82
src/services/vehicle/persist_car_service.hpp
Normal file
82
src/services/vehicle/persist_car_service.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "natives.hpp"
|
||||
#include "core/data/model_attachment.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class persist_car_service
|
||||
{
|
||||
public:
|
||||
static std::vector<std::string> list_files();
|
||||
|
||||
static Vehicle clone_ped_car(Ped ped, Vehicle vehicle);
|
||||
static void save_vehicle(Vehicle vehicle, std::string_view file_name);
|
||||
static Vehicle load_vehicle(std::string_view file_name);
|
||||
|
||||
private:
|
||||
static constexpr auto model_attachment_key = "model_attachment";
|
||||
static constexpr auto model_attachments_key = "model_attachments";
|
||||
|
||||
static constexpr auto vehicle_attachments_key = "vehicle_attachments";
|
||||
static constexpr auto is_invincible_key = "is_invincible";
|
||||
static constexpr auto is_visible_key = "is_visible";
|
||||
static constexpr auto has_collision_key = "has_collision";
|
||||
|
||||
static constexpr auto vehicle_model_hash_key = "vehicle_model_hash";
|
||||
|
||||
static constexpr auto vehicle_key = "vehicle";
|
||||
|
||||
static constexpr auto tow_key = "tow";
|
||||
static constexpr auto trailer_key = "trailer";
|
||||
|
||||
static constexpr auto radio_station_key = "radio_station";
|
||||
|
||||
static constexpr auto plate_text_key = "plate_text";
|
||||
static constexpr auto plate_text_index_key = "plate_text_index";
|
||||
|
||||
static constexpr auto vehicle_extras_key = "vehicle_extras";
|
||||
|
||||
static constexpr auto vehicle_livery_key = "vehicle_livery";
|
||||
|
||||
static constexpr auto wheel_type_key = "wheel_type";
|
||||
static constexpr auto wheel_color_key = "wheel_color";
|
||||
static constexpr auto tire_smoke_color_key = "tire_smoke_color";
|
||||
|
||||
static constexpr auto convertable_state_key = "convertable_state";
|
||||
|
||||
static constexpr auto vehicle_window_tint_key = "vehicle_window_tint";
|
||||
|
||||
static constexpr auto neon_lights_key = "neon_lights";
|
||||
static constexpr auto neon_color_key = "neon_color";
|
||||
|
||||
static constexpr auto primary_color_key = "primary_color";
|
||||
static constexpr auto custom_primary_color_key = "custom_primary_color";
|
||||
|
||||
static constexpr auto secondary_color_key = "secondary_color";
|
||||
static constexpr auto custom_secondary_color_key = "custom_secondary_color";
|
||||
|
||||
static constexpr auto pearlescent_color_key = "pearlescent_color";
|
||||
static constexpr auto headlight_color_key = "headlight_color";
|
||||
static constexpr auto interior_color_key = "interior_color";
|
||||
static constexpr auto dash_color_key = "dash_color";
|
||||
|
||||
static constexpr auto clan_logo_key = "clan_logo";
|
||||
|
||||
|
||||
static Vehicle spawn_vehicle_full(nlohmann::json vehicle_json, Ped ped);
|
||||
static Vehicle spawn_vehicle(nlohmann::json vehicle_json, Ped ped);
|
||||
static Vehicle spawn_vehicle_json(nlohmann::json vehicle_json, Ped ped);
|
||||
|
||||
static nlohmann::json get_full_vehicle_json(Vehicle vehicle);
|
||||
|
||||
static model_attachment get_model_attachment(Vehicle vehicle, Object object);
|
||||
static nlohmann::json get_model_attachments(Vehicle vehicle, bool is_towed_vehicle = false);
|
||||
|
||||
static nlohmann::json get_vehicle_attachents(Vehicle vehicle);
|
||||
|
||||
static nlohmann::json get_vehicle_json(Vehicle vehicle);
|
||||
|
||||
static big::folder check_vehicle_folder();
|
||||
};
|
||||
}
|
272
src/services/vehicle_helper/vehicle_helper.cpp
Normal file
272
src/services/vehicle_helper/vehicle_helper.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
#include "natives.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "script.hpp"
|
||||
#include "vehicle_helper.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void vehicle_helper::add_clan_logo_to_vehicle(Vehicle vehicle, Ped ped)
|
||||
{
|
||||
rage::fvector3 x, y, z;
|
||||
float scale;
|
||||
Hash modelHash = ENTITY::GET_ENTITY_MODEL(vehicle);
|
||||
if (GetVehicleInfoForClanLogo(modelHash, x, y, z, scale))
|
||||
{
|
||||
int alpha = 200;
|
||||
if (modelHash == VEHICLE_WINDSOR || modelHash == VEHICLE_COMET4)
|
||||
alpha = 255;
|
||||
|
||||
GRAPHICS::ADD_VEHICLE_CREW_EMBLEM(vehicle, ped, ENTITY::GET_ENTITY_BONE_INDEX_BY_NAME(vehicle, "chassis_dummy"), x.x, x.y, x.z, y.x, y.y, y.z, z.x, z.y, z.z, scale, 0, alpha);
|
||||
if (y.z >= 0.0f)
|
||||
GRAPHICS::ADD_VEHICLE_CREW_EMBLEM(vehicle, ped, ENTITY::GET_ENTITY_BONE_INDEX_BY_NAME(vehicle, "chassis_dummy"), x.x * -1.0f, x.y, x.z, y.x * -1.0f, y.y, y.z, z.x * -1.0f, z.y * -1.0f, z.z, scale, 1, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
const char* vehicle_helper::get_mod_slot_name(Hash model, Vehicle vehicle, int mod_slot)
|
||||
{
|
||||
switch (mod_slot)
|
||||
{
|
||||
case MOD_HOOD:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_HOD");
|
||||
case MOD_ARMOR:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_ARM");
|
||||
case MOD_BRAKES:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_BRA");
|
||||
case MOD_ENGINE:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_ENG");
|
||||
case MOD_SUSPENSION:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_SUS");
|
||||
case MOD_TRANSMISSION:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_TRN");
|
||||
case MOD_HORNS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_HRN");
|
||||
case MOD_FRONTWHEEL:
|
||||
if (!VEHICLE::IS_THIS_MODEL_A_BIKE(model) && VEHICLE::IS_THIS_MODEL_A_BICYCLE(model))
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_MOD_WHEM");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_WHE0_0");
|
||||
case MOD_REARWHEEL:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_WHE0_1");
|
||||
//Bennys
|
||||
case MOD_PLATEHOLDER:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S0");
|
||||
case MOD_VANITYPLATES:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S1");
|
||||
case MOD_TRIMDESIGN:
|
||||
if (model == VEHICLE_SULTANRS)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S2b");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S2");
|
||||
case MOD_ORNAMENTS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S3");
|
||||
case MOD_DASHBOARD:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S4");
|
||||
case MOD_DIALDESIGN:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S5");
|
||||
case MOD_DOORSPEAKERS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S6");
|
||||
case MOD_SEATS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S7");
|
||||
case MOD_STEERINGWHEELS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S8");
|
||||
case MOD_COLUMNSHIFTERLEVERS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S9");
|
||||
case MOD_PLAQUES:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S10");
|
||||
case MOD_SPEAKERS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S11");
|
||||
case MOD_TRUNK:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S12");
|
||||
case MOD_HYDRO:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S13");
|
||||
case MOD_ENGINEBLOCK:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S14");
|
||||
case MOD_AIRFILTER:
|
||||
if (model == VEHICLE_SULTANRS)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S15b");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S15");
|
||||
case MOD_STRUTS:
|
||||
if (model == VEHICLE_SULTANRS || model == VEHICLE_BANSHEE2)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S16b");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S16");
|
||||
case MOD_ARCHCOVER:
|
||||
if (model == VEHICLE_SULTANRS)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S17b");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S17");
|
||||
case MOD_AERIALS:
|
||||
if (model == VEHICLE_SULTANRS)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S18b");
|
||||
else if (model == VEHICLE_BTYPE3)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S18c");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S18");
|
||||
case MOD_TRIM:
|
||||
if (model == VEHICLE_SULTANRS)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S19b");
|
||||
else if (model == VEHICLE_BTYPE3)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S19c");
|
||||
else if (model == VEHICLE_VIRGO2)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S19d");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S19");
|
||||
case MOD_TANK:
|
||||
if (model == VEHICLE_SLAMVAN3)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S27");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S20");
|
||||
case MOD_WINDOWS:
|
||||
if (model == VEHICLE_BTYPE3)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S21b");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S21");
|
||||
case MOD_DOORS:
|
||||
if (model == VEHICLE_SLAMVAN3)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("SLVAN3_RDOOR");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S22");
|
||||
case MOD_LIVERY:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMM_MOD_S23");
|
||||
default:
|
||||
auto name = VEHICLE::GET_MOD_SLOT_NAME(vehicle, mod_slot);
|
||||
if (name == nullptr)
|
||||
return "";
|
||||
if (strstr(name, "_"))
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(name);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
static const std::map<int, const char*> horn_map = {
|
||||
{-1, "CMOD_HRN_0"}, {0, "CMOD_HRN_TRK"}, {1, "CMOD_HRN_COP"}, {2, "CMOD_HRN_CLO"}, {3, "CMOD_HRN_MUS1"}, {4, "CMOD_HRN_MUS2"}, {5, "CMOD_HRN_MUS3"},
|
||||
{6, "CMOD_HRN_MUS4"}, {7, "CMOD_HRN_MUS5"}, {8, "CMOD_HRN_SAD"}, {9, "HORN_CLAS1"}, {10, "HORN_CLAS2"},
|
||||
{11, "HORN_CLAS3"}, {12, "HORN_CLAS4"}, {13, "HORN_CLAS5"}, {14, "HORN_CLAS6"}, {15, "HORN_CLAS7"},
|
||||
{16, "HORN_CNOTE_C0"}, {17, "HORN_CNOTE_D0"}, {18, "HORN_CNOTE_E0"}, {19, "HORN_CNOTE_F0"}, {20, "HORN_CNOTE_G0"},
|
||||
{21, "HORN_CNOTE_A0"}, {22, "HORN_CNOTE_B0"}, {23, "HORN_CNOTE_C1"}, {24, "HORN_HIPS1"}, {25, "HORN_HIPS2"},
|
||||
{26, "HORN_HIPS3"}, {27, "HORN_HIPS4"}, {28, "HORN_INDI_1"}, {29, "HORN_INDI_2"}, {30, "HORN_INDI_3"},
|
||||
{31, "HORN_INDI_4"}, {32, "HORN_LUXE2"}, {33, "HORN_LUXE1"}, {34, "HORN_LUXE3"}, /*{35, "HORN_LUXE2"},
|
||||
{36, "HORN_LUXE1"}, {37, "HORN_LUXE3"},*/ {38, "HORN_HWEEN1"}, /*{39, "HORN_HWEEN1"},*/ {40, "HORN_HWEEN2"},
|
||||
/*{41, "HORN_HWEEN2"},*/ {42, "HORN_LOWRDER1"}, /*{43, "HORN_LOWRDER1"},*/ {44, "HORN_LOWRDER2"}, /*{45, "HORN_LOWRDER2"},*/
|
||||
{46, "HORN_XM15_1"}, {47, "HORN_XM15_2"}, {48, "HORN_XM15_3"}
|
||||
};
|
||||
|
||||
const char* vehicle_helper::get_mod_name(Hash model, Vehicle vehicle, int mod_slot, int mod, int mod_count)
|
||||
{
|
||||
if (mod_count == 0)
|
||||
return "";
|
||||
if (mod < -1 || mod >= mod_count)
|
||||
return "";
|
||||
if (mod_slot == MOD_HORNS)
|
||||
{
|
||||
if (horn_map.find(mod) != horn_map.end())
|
||||
{
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(horn_map.find(mod)->second);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
if (mod_slot == MOD_FRONTWHEEL || mod_slot == MOD_REARWHEEL)
|
||||
{
|
||||
if (mod == -1)
|
||||
{
|
||||
if (!VEHICLE::IS_THIS_MODEL_A_BIKE(model) && VEHICLE::IS_THIS_MODEL_A_BICYCLE(model))
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_WHE_0");
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_WHE_B_0");
|
||||
}
|
||||
if (mod >= mod_count / 2)
|
||||
//return std::format("{} {}", HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CHROME"), HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(VEHICLE::GET_MOD_TEXT_LABEL(vehicle, mod_slot, mod))).c_str(); //Bug with FMT library? Returns Chrome Chrome...
|
||||
return std::format("Chrome {}", HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(VEHICLE::GET_MOD_TEXT_LABEL(vehicle, mod_slot, mod))).c_str();
|
||||
else
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(VEHICLE::GET_MOD_TEXT_LABEL(vehicle, mod_slot, mod));
|
||||
}
|
||||
|
||||
switch (mod_slot)
|
||||
{
|
||||
case MOD_ARMOR:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(std::format("CMOD_ARM_{}", (mod + 1)).c_str());
|
||||
case MOD_BRAKES:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(std::format("CMOD_BRA_{}", (mod + 1)).c_str());
|
||||
case MOD_ENGINE:
|
||||
if (mod == -1)
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_ARM_0");
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(std::format("CMOD_ENG_{}", (mod + 1)).c_str());
|
||||
case MOD_SUSPENSION:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(std::format("CMOD_SUS_{}", (mod + 1)).c_str());
|
||||
case MOD_TRANSMISSION:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(std::format("CMOD_GBX_{}", (mod + 1)).c_str());
|
||||
}
|
||||
|
||||
|
||||
if (mod > -1)
|
||||
{
|
||||
if (mod_slot == MOD_SIDESKIRT && VEHICLE::GET_NUM_VEHICLE_MODS(vehicle, MOD_SIDESKIRT) < 2)
|
||||
{
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_SKI_1");
|
||||
}
|
||||
auto label = VEHICLE::GET_MOD_TEXT_LABEL(vehicle, mod_slot, mod);
|
||||
if (label == nullptr || strlen(label) == 0)
|
||||
return "MISSING_LABEL";
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(label);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (mod_slot)
|
||||
{
|
||||
case MOD_AIRFILTER:
|
||||
break;
|
||||
case MOD_STRUTS:
|
||||
switch (model)
|
||||
{
|
||||
case VEHICLE_BANSHEE:
|
||||
case VEHICLE_BANSHEE2:
|
||||
case VEHICLE_SULTANRS:
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_COL5_41");
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
return HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION("CMOD_DEF_0");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const std::map<Hash, std::map<int, std::vector<int32_t>>> mod_blacklists = {
|
||||
{ VEHICLE_BANSHEE, {
|
||||
{ MOD_SPOILERS, { 3, 4 } },
|
||||
{ MOD_COLUMNSHIFTERLEVERS, { 0, 1, 2, 3 } },
|
||||
{ MOD_SPEAKERS, { 0 } },
|
||||
{ MOD_LIVERY, { 15, 16 } }
|
||||
} },
|
||||
{ VEHICLE_SENTINEL, {
|
||||
{ MOD_SPOILERS, { 4, 5 } },
|
||||
{ MOD_COLUMNSHIFTERLEVERS, { 0, 1, 2, 3 } },
|
||||
{ MOD_SPEAKERS, { 0 } },
|
||||
{ MOD_LIVERY, { 0, 1 } }
|
||||
} }
|
||||
};
|
||||
|
||||
bool vehicle_helper::check_mod_blacklist(Hash model, int mod_slot, int mod)
|
||||
{
|
||||
if (mod_blacklists.find(model) == mod_blacklists.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto veh_slot_blacklist = mod_blacklists.find(model)->second;
|
||||
if (veh_slot_blacklist.find(mod_slot) == veh_slot_blacklist.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto veh_mod_blacklist = veh_slot_blacklist.find(mod_slot)->second;
|
||||
if (std::find(veh_mod_blacklist.begin(), veh_mod_blacklist.end(), mod) != veh_mod_blacklist.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
15
src/services/vehicle_helper/vehicle_helper.hpp
Normal file
15
src/services/vehicle_helper/vehicle_helper.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "gta\VehicleValues.h"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class vehicle_helper
|
||||
{
|
||||
public:
|
||||
static bool check_mod_blacklist(Hash model, int mod_slot, int mod);
|
||||
static const char* get_mod_slot_name(Hash model, Vehicle vehicle, int mod_slot);
|
||||
static const char* get_mod_name(Hash model, Vehicle vehicle, int mod_slot, int mod, int mod_count);
|
||||
static void add_clan_logo_to_vehicle(Vehicle vehicle, Ped ped);
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user