feat(debug): Add Tunables Tab (#3431)
Added a Tunables tab similar to Globals/Locals that can be used to read and write tunables by inputting their unsigned/signed or hexadecimal hash, as well as their pre-joaat name. It also displays the offset of the given tunable. Removed the animation players in the Debug tab as the one in the Self tab makes them redundant.
This commit is contained in:
parent
449ebf4c42
commit
6d77b81343
@ -52,7 +52,7 @@ namespace big
|
||||
|
||||
if (SCRIPT::HAS_SCRIPT_WITH_NAME_HASH_LOADED("tuneables_processing"_J) && SCRIPT::HAS_SCRIPT_WITH_NAME_HASH_LOADED("tunables_registration"_J))
|
||||
{
|
||||
m_num_tunables = gta_util::find_script_program("tunables_registration"_J)->m_global_count - 0x40000;
|
||||
m_num_tunables = gta_util::find_script_program("tunables_registration"_J)->m_global_count - TUNABLE_BASE_ADDRESS;
|
||||
|
||||
uint64_t args[] = {6, 27}; // TODO: check args
|
||||
|
||||
@ -65,7 +65,7 @@ namespace big
|
||||
}
|
||||
|
||||
m_tunables_backup = std::make_unique<std::uint64_t[]>(m_num_tunables);
|
||||
memcpy(m_tunables_backup.get(), script_global(0x40000).as<void*>(), m_num_tunables * 8);
|
||||
memcpy(m_tunables_backup.get(), script_global(TUNABLE_BASE_ADDRESS).as<void*>(), m_num_tunables * 8);
|
||||
|
||||
SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED("tuneables_processing"_J);
|
||||
SCRIPT::SET_SCRIPT_WITH_NAME_HASH_AS_NO_LONGER_NEEDED("tunables_registration"_J);
|
||||
@ -78,13 +78,13 @@ namespace big
|
||||
{
|
||||
for (int i = 0; i < m_num_tunables; i++)
|
||||
{
|
||||
auto value = *script_global(0x40000).at(i).as<int*>();
|
||||
auto value = *script_global(TUNABLE_BASE_ADDRESS).at(i).as<int*>();
|
||||
if (auto it = m_junk_values.find(value); it != m_junk_values.end())
|
||||
{
|
||||
m_tunables.emplace(it->second, 0x40000 + i);
|
||||
m_tunables.emplace(it->second, TUNABLE_BASE_ADDRESS + i);
|
||||
}
|
||||
}
|
||||
memcpy(script_global(0x40000).as<void*>(), m_tunables_backup.get(), m_num_tunables * 8);
|
||||
memcpy(script_global(TUNABLE_BASE_ADDRESS).as<void*>(), m_tunables_backup.get(), m_num_tunables * 8);
|
||||
|
||||
if (m_tunables.size() == 0)
|
||||
{
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
constexpr int TUNABLE_BASE_ADDRESS = 0x40000; // This never changes
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct tunable_save_struct
|
||||
{
|
||||
@ -69,6 +71,21 @@ namespace big
|
||||
}
|
||||
}
|
||||
|
||||
inline int get_tunable_offset(rage::joaat_t hash)
|
||||
{
|
||||
if (auto it = m_tunables.find(hash); it != m_tunables.end())
|
||||
{
|
||||
auto offset = it->second;
|
||||
|
||||
if (offset > TUNABLE_BASE_ADDRESS)
|
||||
return (offset - TUNABLE_BASE_ADDRESS) - 1;
|
||||
|
||||
return (TUNABLE_BASE_ADDRESS - offset) - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m_current_junk_val = 0x1000000;
|
||||
std::unordered_map<int, rage::joaat_t> m_junk_values{};
|
||||
|
||||
|
@ -92,7 +92,7 @@ namespace big::animations
|
||||
if (!std::filesystem::exists(g_file_manager.get_project_file("animDictsCompact.json").get_path()))
|
||||
{
|
||||
LOG(WARNING) << "Animations file is not in directory. https://raw.githubusercontent.com/DurtyFree/gta-v-data-dumps/master/animDictsCompact.json";
|
||||
g_notification_service.push_warning("Animations", "Please download the appropriate animations json and put it in the mod directory.");
|
||||
g_notification_service.push_warning("Animations", "JSON_ANIMATIONS_WARNING"_T.data());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -14,17 +14,12 @@ namespace big
|
||||
ImGui::BeginTabBar("debug_tabbar");
|
||||
misc();
|
||||
logs();
|
||||
tunables();
|
||||
globals();
|
||||
locals();
|
||||
script_events();
|
||||
scripts();
|
||||
threads();
|
||||
if (ImGui::BeginTabItem("GUI_TAB_ANIMATIONS"_T.data()))
|
||||
{
|
||||
animations();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
namespace big::debug
|
||||
{
|
||||
extern void misc();
|
||||
extern void logs();
|
||||
extern void tunables();
|
||||
extern void globals();
|
||||
extern void locals();
|
||||
extern void logs();
|
||||
extern void misc();
|
||||
extern void script_events();
|
||||
extern void scripts();
|
||||
extern void threads();
|
||||
extern void animations(std::string* dict = nullptr, std::string* anim = nullptr); // Can be used to retrieve animations
|
||||
|
||||
extern void main();
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
#include "gui/components/components.hpp"
|
||||
#include "util/animations.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "view_debug.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void debug::animations(std::string* dict, std::string* anim)
|
||||
{
|
||||
static std::string current_dict, current_anim;
|
||||
static std::vector<std::string> selected_dict_anim_list{};
|
||||
|
||||
if(dict && anim)
|
||||
{
|
||||
*dict = current_dict;
|
||||
*anim = current_anim;
|
||||
}
|
||||
|
||||
static auto reload_anim_list = []() -> void {
|
||||
selected_dict_anim_list.clear();
|
||||
auto range = animations::all_anims.equal_range(current_dict);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
selected_dict_anim_list.push_back(it->second);
|
||||
}
|
||||
};
|
||||
|
||||
if (animations::has_anim_list_been_populated())
|
||||
{
|
||||
ImGui::Text("VIEW_DEBUG_ANIMATIONS_ANIMATIONS_IN_MEMORY"_T.data(), animations::anim_dict_count(), animations::total_anim_count());
|
||||
}
|
||||
|
||||
components::button("VIEW_DEBUG_ANIMATIONS_FETCH_ALL_ANIMS"_T, [] {
|
||||
animations::fetch_all_anims();
|
||||
});
|
||||
|
||||
ImGui::SetNextItemWidth(400);
|
||||
components::input_text_with_hint("##dictionaryfilter", "DICT"_T, current_dict);
|
||||
|
||||
if (animations::has_anim_list_been_populated() && ImGui::BeginListBox("##dictionaries", ImVec2(400, 200)))
|
||||
{
|
||||
for (auto& entry : animations::all_dicts)
|
||||
{
|
||||
std::string entry_lowercase = entry;
|
||||
std::string search_lowercase = current_dict;
|
||||
std::transform(entry.begin(), entry.end(), entry.begin(), ::tolower);
|
||||
std::transform(current_dict.begin(), current_dict.end(), current_dict.begin(), ::tolower);
|
||||
if (entry.find(search_lowercase) != std::string::npos && ImGui::Selectable(entry.data()))
|
||||
{
|
||||
current_dict = entry;
|
||||
reload_anim_list();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (selected_dict_anim_list.size() > 0 && ImGui::BeginListBox("##animations", ImVec2(400, 200)))
|
||||
{
|
||||
for (auto& entry : selected_dict_anim_list)
|
||||
{
|
||||
if (ImGui::Selectable(entry.data(), entry == current_anim))
|
||||
{
|
||||
current_anim = entry;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
components::button("VIEW_DEBUG_ANIMATIONS_PLAY"_T, [] {
|
||||
TASK::CLEAR_PED_TASKS_IMMEDIATELY(self::ped);
|
||||
ped::ped_play_animation(self::ped, current_dict, current_anim, 4.f, -4.f, -1, 0, 0, false);
|
||||
});
|
||||
ImGui::SameLine();
|
||||
components::button("VIEW_DEBUG_ANIMATIONS_STOP"_T, [] {
|
||||
TASK::CLEAR_PED_TASKS(self::ped);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
#include "gta/joaat.hpp"
|
||||
#include "gui/components/components.hpp"
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "util/pathfind.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "util/system.hpp"
|
||||
#include "view_debug.hpp"
|
||||
#include "network/CNetworkPlayerMgr.hpp"
|
||||
@ -154,21 +152,6 @@ namespace big
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("ANIMATION_PLAYER"_T.data()))
|
||||
{
|
||||
static char dict[100], anim[100];
|
||||
|
||||
ImGui::PushItemWidth(200);
|
||||
components::input_text_with_hint("##dictionary", "DICT"_T, dict, IM_ARRAYSIZE(dict));
|
||||
components::input_text_with_hint("##animation", "ANIMATION"_T, anim, IM_ARRAYSIZE(anim));
|
||||
if (ImGui::Button("PLAY_ANIMATION"_T.data()))
|
||||
g_fiber_pool->queue_job([=] {
|
||||
ped::ped_play_animation(self::ped, dict, anim);
|
||||
});
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
273
src/views/debug/view_debug_tunables.cpp
Normal file
273
src/views/debug/view_debug_tunables.cpp
Normal file
@ -0,0 +1,273 @@
|
||||
#include "gui/components/components.hpp"
|
||||
#include "services/tunables/tunables_service.hpp"
|
||||
#include "widgets/imgui_bitfield.hpp"
|
||||
#include "view_debug.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum TunableValueType : int
|
||||
{
|
||||
INT,
|
||||
BOOLEAN,
|
||||
BITSET,
|
||||
FLOAT
|
||||
};
|
||||
|
||||
struct tunable_debug
|
||||
{
|
||||
std::string tunable_name{};
|
||||
TunableValueType tunable_value_type = TunableValueType::INT;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(tunable_debug, tunable_name, tunable_value_type)
|
||||
};
|
||||
|
||||
nlohmann::json get_tunables_json()
|
||||
{
|
||||
nlohmann::json tunables{};
|
||||
auto file = g_file_manager.get_project_file("./tunables.json");
|
||||
if (file.exists())
|
||||
{
|
||||
std::ifstream iffstream_file(file.get_path());
|
||||
iffstream_file >> tunables;
|
||||
}
|
||||
return tunables;
|
||||
}
|
||||
|
||||
void load_tunable_menu(const std::string& selected_tunable, tunable_debug& tunable_obj)
|
||||
{
|
||||
if (!selected_tunable.empty())
|
||||
{
|
||||
auto tunables = get_tunables_json();
|
||||
if (tunables[selected_tunable].is_null())
|
||||
return;
|
||||
tunable_obj = tunables[selected_tunable].get<tunable_debug>();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_numeric(const std::string& str)
|
||||
{
|
||||
if (str[0] == '-')
|
||||
return str.size() > 1 && std::all_of(str.begin() + 1, str.end(), ::isdigit);
|
||||
|
||||
return std::all_of(str.begin(), str.end(), ::isdigit);
|
||||
}
|
||||
|
||||
bool is_hexadecimal(const std::string& str)
|
||||
{
|
||||
if (str.size() > 2 && str[0] == '0' && str[1] == 'x')
|
||||
{
|
||||
return std::all_of(str.begin() + 2, str.end(), [](unsigned char c) {
|
||||
return std::isxdigit(c);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t parse_tunable(const std::string& tunable)
|
||||
{
|
||||
if (tunable.empty())
|
||||
return 0;
|
||||
|
||||
std::string str = tunable;
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
try
|
||||
{
|
||||
if (is_numeric(str))
|
||||
{
|
||||
return std::stoll(str);
|
||||
}
|
||||
else if (is_hexadecimal(str))
|
||||
{
|
||||
return std::stoll(str, nullptr, 16);
|
||||
}
|
||||
|
||||
return rage::joaat(str);
|
||||
}
|
||||
catch (const std::out_of_range&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t* get_tunable_ptr(tunable_debug& tunable_test)
|
||||
{
|
||||
auto retn_val = g_tunables_service->get_tunable<int64_t*>(parse_tunable(tunable_test.tunable_name));
|
||||
if ((size_t)retn_val < UINT32_MAX)
|
||||
return nullptr;
|
||||
return retn_val;
|
||||
}
|
||||
|
||||
std::string get_tunable_display(tunable_debug& tunable_test)
|
||||
{
|
||||
auto ptr = get_tunable_ptr(tunable_test);
|
||||
if (ptr != nullptr)
|
||||
{
|
||||
switch (tunable_test.tunable_value_type)
|
||||
{
|
||||
case TunableValueType::INT:
|
||||
{
|
||||
return std::to_string(*(PINT)ptr);
|
||||
}
|
||||
case TunableValueType::BOOLEAN:
|
||||
{
|
||||
return (*ptr == TRUE) ? "TRUE" : "FALSE";
|
||||
}
|
||||
case TunableValueType::BITSET:
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << "0x" << std::hex << std::uppercase << (DWORD)*ptr;
|
||||
return o.str();
|
||||
}
|
||||
case TunableValueType::FLOAT:
|
||||
{
|
||||
return std::to_string(*(PFLOAT)ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "VIEW_DEBUG_TUNABLE_INVALID_TUNABLE_READ"_T.data();
|
||||
}
|
||||
|
||||
std::map<std::string, tunable_debug> list_tunables()
|
||||
{
|
||||
auto json = get_tunables_json();
|
||||
std::map<std::string, tunable_debug> return_value;
|
||||
for (auto& item : json.items())
|
||||
return_value[item.key()] = item.value();
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void save_tunable(char* tunable_name, tunable_debug& tunable_obj)
|
||||
{
|
||||
std::string tunable_name_string = tunable_name;
|
||||
if (!tunable_name_string.empty())
|
||||
{
|
||||
auto json = get_tunables_json();
|
||||
json[tunable_name_string] = tunable_obj;
|
||||
|
||||
auto file_path = g_file_manager.get_project_file("./tunables.json").get_path();
|
||||
std::ofstream file(file_path, std::ios::out | std::ios::trunc);
|
||||
file << json.dump(4);
|
||||
file.close();
|
||||
ZeroMemory(tunable_name, sizeof(tunable_name));
|
||||
}
|
||||
}
|
||||
|
||||
void delete_tunable(std::string name)
|
||||
{
|
||||
auto locations = get_tunables_json();
|
||||
if (locations[name].is_null())
|
||||
return;
|
||||
locations.erase(name);
|
||||
auto file_path = g_file_manager.get_project_file("./tunables.json").get_path();
|
||||
std::ofstream file(file_path, std::ios::out | std::ios::trunc);
|
||||
file << locations.dump(4);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void debug::tunables()
|
||||
{
|
||||
if (ImGui::BeginTabItem("DEBUG_TAB_TUNABLES"_T.data()))
|
||||
{
|
||||
static tunable_debug tunable_test{};
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
components::input_text("VIEW_DEBUG_TUNABLE"_T.data(), tunable_test.tunable_name);
|
||||
if (auto ptr = get_tunable_ptr(tunable_test))
|
||||
{
|
||||
auto tunable_offset = g_tunables_service->get_tunable_offset(parse_tunable(tunable_test.tunable_name));
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
ImGui::InputScalar("VIEW_DEBUG_TUNABLE_OFFSET"_T.data(), ImGuiDataType_S32, &tunable_offset, 0, 0, 0, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
switch (tunable_test.tunable_value_type)
|
||||
{
|
||||
case TunableValueType::INT:
|
||||
{
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
ImGui::InputScalar("VIEW_DEBUG_GLOBAL_VALUE"_T.data(), ImGuiDataType_S32, ptr);
|
||||
break;
|
||||
}
|
||||
case TunableValueType::BOOLEAN:
|
||||
{
|
||||
bool is_tunable_enabled = (*ptr == TRUE);
|
||||
if (ImGui::Checkbox("VIEW_DEBUG_GLOBAL_VALUE"_T.data(), &is_tunable_enabled))
|
||||
{
|
||||
*ptr = is_tunable_enabled;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TunableValueType::BITSET:
|
||||
{
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
ImGui::Bitfield("VIEW_DEBUG_GLOBAL_VALUE"_T.data(), (PINT)ptr);
|
||||
break;
|
||||
}
|
||||
case TunableValueType::FLOAT:
|
||||
{
|
||||
ImGui::SetNextItemWidth(300.f);
|
||||
ImGui::InputScalar("VIEW_DEBUG_GLOBAL_VALUE"_T.data(), ImGuiDataType_Float, ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("VIEW_DEBUG_TUNABLE_INVALID_TUNABLE_READ"_T.data());
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(150.f);
|
||||
ImGui::Combo("VIEW_DEBUG_GLOBAL_TYPE"_T.data(), (int*)&tunable_test.tunable_value_type, "INT\0BOOLEAN\0BITSET\0FLOAT\0");
|
||||
|
||||
auto tunables = list_tunables();
|
||||
static std::string selected_tunable;
|
||||
ImGui::Text("VIEW_DEBUG_TUNABLE_SAVED_TUNABLES"_T.data());
|
||||
if (ImGui::BeginListBox("##savedtunables", ImVec2(200, 250)))
|
||||
{
|
||||
for (auto pair : tunables)
|
||||
{
|
||||
if (ImGui::Selectable(pair.first.c_str(), selected_tunable == pair.first))
|
||||
selected_tunable = std::string(pair.first);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::BeginListBox("##tunablevalues", ImVec2(250, 250)))
|
||||
{
|
||||
for (auto pair : tunables)
|
||||
{
|
||||
ImGui::Selectable(get_tunable_display(pair.second).c_str(), false, ImGuiSelectableFlags_Disabled);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
static char tunable_name_to_save[50]{};
|
||||
ImGui::SetNextItemWidth(200.f);
|
||||
components::input_text("##tunableName", tunable_name_to_save, IM_ARRAYSIZE(tunable_name_to_save));
|
||||
if (ImGui::Button("VIEW_DEBUG_TUNABLE_SAVE_TUNABLE"_T.data()))
|
||||
{
|
||||
save_tunable(tunable_name_to_save, tunable_test);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("VIEW_DEBUG_TUNABLE_LOAD_TUNABLE"_T.data()))
|
||||
{
|
||||
load_tunable_menu(selected_tunable, tunable_test);
|
||||
}
|
||||
|
||||
if (ImGui::Button("VIEW_DEBUG_TUNABLE_DELETE_TUNABLE"_T.data()))
|
||||
{
|
||||
if (!selected_tunable.empty())
|
||||
{
|
||||
delete_tunable(selected_tunable);
|
||||
selected_tunable.clear();
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("VIEW_DEBUG_GLOBAL_CLEAR"_T.data()))
|
||||
{
|
||||
tunable_test.tunable_name = "";
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,84 @@
|
||||
#include "services/ped_animations/ped_animations_service.hpp"
|
||||
#include "util/animations.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "views/debug/view_debug.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void json_animations(std::string* dict, std::string* anim)
|
||||
{
|
||||
static std::string current_dict, current_anim;
|
||||
static std::vector<std::string> selected_dict_anim_list{};
|
||||
|
||||
if (dict && anim)
|
||||
{
|
||||
*dict = current_dict;
|
||||
*anim = current_anim;
|
||||
}
|
||||
|
||||
static auto reload_anim_list = []() -> void {
|
||||
selected_dict_anim_list.clear();
|
||||
auto range = animations::all_anims.equal_range(current_dict);
|
||||
for (auto it = range.first; it != range.second; ++it)
|
||||
{
|
||||
selected_dict_anim_list.push_back(it->second);
|
||||
}
|
||||
};
|
||||
|
||||
if (animations::has_anim_list_been_populated())
|
||||
{
|
||||
ImGui::Text("VIEW_DEBUG_ANIMATIONS_ANIMATIONS_IN_MEMORY"_T.data(), animations::anim_dict_count(), animations::total_anim_count());
|
||||
}
|
||||
|
||||
components::button("VIEW_DEBUG_ANIMATIONS_FETCH_ALL_ANIMS"_T, [] {
|
||||
animations::fetch_all_anims();
|
||||
});
|
||||
|
||||
ImGui::SetNextItemWidth(400);
|
||||
components::input_text_with_hint("##dictionaryfilter", "DICT"_T, current_dict);
|
||||
|
||||
if (animations::has_anim_list_been_populated() && ImGui::BeginListBox("##dictionaries", ImVec2(400, 200)))
|
||||
{
|
||||
for (auto& entry : animations::all_dicts)
|
||||
{
|
||||
std::string entry_lowercase = entry;
|
||||
std::string search_lowercase = current_dict;
|
||||
std::transform(entry.begin(), entry.end(), entry.begin(), ::tolower);
|
||||
std::transform(current_dict.begin(), current_dict.end(), current_dict.begin(), ::tolower);
|
||||
if (entry.find(search_lowercase) != std::string::npos && ImGui::Selectable(entry.data()))
|
||||
{
|
||||
current_dict = entry;
|
||||
reload_anim_list();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (selected_dict_anim_list.size() > 0 && ImGui::BeginListBox("##animations", ImVec2(400, 200)))
|
||||
{
|
||||
for (auto& entry : selected_dict_anim_list)
|
||||
{
|
||||
if (ImGui::Selectable(entry.data(), entry == current_anim))
|
||||
{
|
||||
current_anim = entry;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
components::button("VIEW_DEBUG_ANIMATIONS_PLAY"_T, [] {
|
||||
TASK::CLEAR_PED_TASKS_IMMEDIATELY(self::ped);
|
||||
ped::ped_play_animation(self::ped, current_dict, current_anim, 4.f, -4.f, -1, 0, 0, false);
|
||||
});
|
||||
ImGui::SameLine();
|
||||
components::button("VIEW_DEBUG_ANIMATIONS_STOP"_T, [] {
|
||||
TASK::CLEAR_PED_TASKS(self::ped);
|
||||
});
|
||||
}
|
||||
|
||||
void view::animations()
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
@ -42,7 +116,7 @@ namespace big
|
||||
components::options_modal(
|
||||
"VIEW_SELF_ANIMATIONS_DEBUG_ANIMATIONS"_T.data(),
|
||||
[] {
|
||||
debug::animations(&g_ped_animation_service.current_animation.dict, &g_ped_animation_service.current_animation.anim);
|
||||
json_animations(&g_ped_animation_service.current_animation.dict, &g_ped_animation_service.current_animation.anim);
|
||||
},
|
||||
true,
|
||||
"VIEW_SELF_ANIMATIONS_LIST_FROM_DEBUG"_T.data());
|
||||
|
Reference in New Issue
Block a user