Ped animations interface (#1668)
This commit is contained in:
parent
f61a89696e
commit
ee364cd684
@ -7,6 +7,7 @@
|
||||
#include "services/context_menu/context_menu_service.hpp"
|
||||
#include "services/custom_teleport/custom_teleport_service.hpp"
|
||||
#include "services/orbital_drone/orbital_drone.hpp"
|
||||
#include "services/ped_animations/ped_animations_service.hpp"
|
||||
#include "services/script_connection/script_connection_service.hpp"
|
||||
#include "services/squad_spawner/squad_spawner.hpp"
|
||||
#include "services/tunables/tunables_service.hpp"
|
||||
@ -27,6 +28,7 @@ namespace big
|
||||
g_squad_spawner_service.fetch_squads();
|
||||
g_xml_vehicles_service->fetch_xml_files();
|
||||
g_custom_teleport_service.fetch_saved_locations();
|
||||
g_ped_animation_service.fetch_saved_animations();
|
||||
|
||||
while (g_running)
|
||||
{
|
||||
@ -59,6 +61,16 @@ namespace big
|
||||
}
|
||||
}
|
||||
|
||||
void backend::ambient_animations_loop()
|
||||
{
|
||||
while (g_running)
|
||||
{
|
||||
g_ped_animation_service.ambient_animations_prompt_tick();
|
||||
|
||||
script::get_current()->yield();
|
||||
}
|
||||
}
|
||||
|
||||
void backend::weapons_loop()
|
||||
{
|
||||
LOG(INFO) << "Starting script: Weapons";
|
||||
|
@ -8,6 +8,7 @@ namespace big
|
||||
public:
|
||||
static void loop();
|
||||
static void self_loop();
|
||||
static void ambient_animations_loop();
|
||||
static void weapons_loop();
|
||||
static void vehicles_loop();
|
||||
static void misc_loop();
|
||||
|
@ -18,6 +18,8 @@ namespace big
|
||||
|
||||
self::pos = ENTITY::GET_ENTITY_COORDS(self::ped, false /*Unused*/);
|
||||
|
||||
self::rot = ENTITY::GET_ENTITY_ROTATION(self::ped, 2);
|
||||
|
||||
if (PED::IS_PED_IN_ANY_VEHICLE(self::ped, 0))
|
||||
{
|
||||
self::veh = PED::GET_VEHICLE_PED_IS_IN(self::ped, false);
|
||||
|
@ -80,6 +80,7 @@ namespace self
|
||||
inline Ped ped;
|
||||
inline Player id;
|
||||
inline Vector3 pos;
|
||||
inline Vector3 rot;
|
||||
inline Vehicle veh;
|
||||
}
|
||||
|
||||
|
@ -305,45 +305,47 @@ namespace big
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(ipls, select)
|
||||
} ipls{};
|
||||
|
||||
bool clean_player = false;
|
||||
bool force_wanted_level = false;
|
||||
bool free_cam = false;
|
||||
bool invisibility = false;
|
||||
bool local_visibility = true;
|
||||
bool never_wanted = false;
|
||||
bool no_ragdoll = false;
|
||||
bool noclip = false;
|
||||
bool clean_player = false;
|
||||
bool force_wanted_level = false;
|
||||
bool free_cam = false;
|
||||
bool invisibility = false;
|
||||
bool local_visibility = true;
|
||||
bool never_wanted = false;
|
||||
bool no_ragdoll = false;
|
||||
bool noclip = false;
|
||||
float noclip_aim_speed_multiplier = 0.25f;
|
||||
float noclip_speed_multiplier = 20.f;
|
||||
bool off_radar = false;
|
||||
bool ghost_org = false;
|
||||
bool super_run = false;
|
||||
bool no_collision = false;
|
||||
bool unlimited_oxygen = false;
|
||||
bool no_water_collision = false;
|
||||
int wanted_level = 0;
|
||||
bool god_mode = false;
|
||||
bool part_water = false;
|
||||
bool proof_bullet = false;
|
||||
bool proof_fire = false;
|
||||
bool proof_collision = false;
|
||||
bool proof_melee = false;
|
||||
bool proof_explosion = false;
|
||||
bool proof_steam = false;
|
||||
bool proof_drown = false;
|
||||
bool proof_water = false;
|
||||
uint32_t proof_mask = 0;
|
||||
bool mobile_radio = false;
|
||||
bool fast_respawn = false;
|
||||
bool auto_tp = false;
|
||||
bool super_jump = false;
|
||||
bool beast_jump = false;
|
||||
bool healthregen = false;
|
||||
float healthregenrate = 1.0f;
|
||||
bool superman = false;
|
||||
bool custom_weapon_stop = true;
|
||||
std::string persist_outfit = "";
|
||||
bool persist_outfits_mis = false;
|
||||
bool off_radar = false;
|
||||
bool ghost_org = false;
|
||||
bool super_run = false;
|
||||
bool no_collision = false;
|
||||
bool unlimited_oxygen = false;
|
||||
bool no_water_collision = false;
|
||||
int wanted_level = 0;
|
||||
bool god_mode = false;
|
||||
bool part_water = false;
|
||||
bool proof_bullet = false;
|
||||
bool proof_fire = false;
|
||||
bool proof_collision = false;
|
||||
bool proof_melee = false;
|
||||
bool proof_explosion = false;
|
||||
bool proof_steam = false;
|
||||
bool proof_drown = false;
|
||||
bool proof_water = false;
|
||||
uint32_t proof_mask = 0;
|
||||
bool mobile_radio = false;
|
||||
bool fast_respawn = false;
|
||||
bool auto_tp = false;
|
||||
bool super_jump = false;
|
||||
bool beast_jump = false;
|
||||
bool healthregen = false;
|
||||
float healthregenrate = 1.0f;
|
||||
bool superman = false;
|
||||
bool custom_weapon_stop = true;
|
||||
bool prompt_ambient_animations = false;
|
||||
std::string persist_outfit = "";
|
||||
bool persist_outfits_mis = false;
|
||||
|
||||
struct hud
|
||||
{
|
||||
bool color_override = false;
|
||||
@ -363,7 +365,7 @@ namespace big
|
||||
// do not save below entries
|
||||
bool dance_mode = false;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(self, ipls, ptfx_effects, clean_player, force_wanted_level, free_cam, invisibility, local_visibility, never_wanted, no_ragdoll, noclip, noclip_aim_speed_multiplier, noclip_speed_multiplier, off_radar, super_run, no_collision, unlimited_oxygen, no_water_collision, wanted_level, god_mode, part_water, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_drown, proof_water, proof_mask, mobile_radio, fast_respawn, auto_tp, super_jump, beast_jump, healthregen, healthregenrate, hud, superman, custom_weapon_stop, persist_outfit, persist_outfits_mis)
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(self, ipls, ptfx_effects, clean_player, force_wanted_level, free_cam, invisibility, local_visibility, never_wanted, no_ragdoll, noclip, noclip_aim_speed_multiplier, noclip_speed_multiplier, off_radar, super_run, no_collision, unlimited_oxygen, no_water_collision, wanted_level, god_mode, part_water, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_drown, proof_water, proof_mask, mobile_radio, fast_respawn, auto_tp, super_jump, beast_jump, healthregen, healthregenrate, hud, superman, custom_weapon_stop, prompt_ambient_animations, persist_outfit, persist_outfits_mis)
|
||||
} self{};
|
||||
|
||||
struct session
|
||||
|
@ -127,6 +127,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
g_script_mgr.add_script(std::make_unique<script>(&context_menu_service::context_menu, "Context Menu"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::tunables_script, "Tunables"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::squad_spawner, "Squad Spawner"));
|
||||
g_script_mgr.add_script(std::make_unique<script>(&backend::ambient_animations_loop, "Ambient Animations"));
|
||||
|
||||
LOG(INFO) << "Scripts registered.";
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "util/entity.hpp"
|
||||
#include "util/ped.hpp"
|
||||
#include "util/teleport.hpp"
|
||||
#include "services/ped_animations/ped_animations_service.hpp"
|
||||
|
||||
|
||||
namespace big
|
||||
@ -145,10 +146,13 @@ namespace big
|
||||
[this] {
|
||||
PED::SET_PED_TO_RAGDOLL(m_handle, 2000, 2000, 0, 0, 0, 0);
|
||||
}},
|
||||
{"DANCE",
|
||||
{"ANIMATION",
|
||||
[this] {
|
||||
ped::ped_play_animation(m_handle, "mini@strip_club@private_dance@part1", "priv_dance_p1", 3.5f, -4.0f, -1, 1);
|
||||
}},
|
||||
if(STREAMING::DOES_ANIM_DICT_EXIST(g_ped_animation_service.current_animation.dict.data()))
|
||||
g_ped_animation_service.play_saved_ped_animation(g_ped_animation_service.current_animation, m_handle);
|
||||
else
|
||||
ped::ped_play_animation(m_handle, "mini@strip_club@private_dance@part1", "priv_dance_p1", 3.5f, -4.0f, -1, 1);
|
||||
}},
|
||||
{"RECRUIT", [this] {
|
||||
TASK::CLEAR_PED_TASKS(m_handle);
|
||||
PED::SET_PED_AS_GROUP_MEMBER(m_handle, PED::GET_PED_GROUP_INDEX(self::ped));
|
||||
|
@ -15,6 +15,7 @@ namespace big
|
||||
MOBILE,
|
||||
OUTFIT_EDITOR,
|
||||
OUTFIT_SLOTS,
|
||||
ANIMATIONS,
|
||||
|
||||
VEHICLE,
|
||||
HANDLING,
|
||||
@ -94,6 +95,7 @@ namespace big
|
||||
}}},
|
||||
{TAB_DECL(OUTFIT_EDITOR), view::outfit_editor}},
|
||||
{TAB_DECL(OUTFIT_SLOTS), view::outfit_slots}},
|
||||
{TAB_DECL(ANIMATIONS), view::animations}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
180
src/services/ped_animations/ped_animations_service.cpp
Normal file
180
src/services/ped_animations/ped_animations_service.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
#include "ped_animations_service.hpp"
|
||||
|
||||
#include "gta/enums.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/ped.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
std::filesystem::path ped_animation_service::get_ped_animations_file_path()
|
||||
{
|
||||
return g_file_manager.get_project_file("ped_animations.json").get_path();
|
||||
}
|
||||
|
||||
std::vector<ped_animation> ped_animation_service::saved_animations_filtered_list(std::string filter = "")
|
||||
{
|
||||
std::vector<ped_animation> filterlist{};
|
||||
|
||||
static auto to_lower = [=](std::string text) -> std::string {
|
||||
std::transform(text.begin(), text.end(), text.begin(), ::tolower);
|
||||
return text;
|
||||
};
|
||||
|
||||
for (auto& ped_animation : all_saved_animations | std::views::values | std::views::join)
|
||||
if (to_lower(ped_animation.name).find(to_lower(filter)) != std::string::npos)
|
||||
filterlist.push_back(ped_animation);
|
||||
|
||||
return filterlist;
|
||||
}
|
||||
|
||||
std::vector<ped_animation> ped_animation_service::saved_animations_ambient_list()
|
||||
{
|
||||
std::vector<ped_animation> ambientlist{};
|
||||
|
||||
for (auto& ped_animation : all_saved_animations | std::views::values | std::views::join)
|
||||
if (ped_animation.ambient)
|
||||
ambientlist.push_back(ped_animation);
|
||||
|
||||
return ambientlist;
|
||||
}
|
||||
|
||||
bool ped_animation_service::fetch_saved_animations()
|
||||
{
|
||||
all_saved_animations.clear();
|
||||
|
||||
auto path = get_ped_animations_file_path();
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
|
||||
try
|
||||
{
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
|
||||
nlohmann::json j;
|
||||
file >> j;
|
||||
all_saved_animations = j.get<std::map<std::string, std::vector<ped_animation>>>();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << "Failed fetching saved animations: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ped_animation_service::save_new_animation(const std::string& category, ped_animation p)
|
||||
{
|
||||
const auto& pair = all_saved_animations.insert({category, {p}});
|
||||
if (!pair.second)
|
||||
{
|
||||
pair.first->second.push_back(p);
|
||||
}
|
||||
|
||||
auto path = get_ped_animations_file_path();
|
||||
|
||||
std::ofstream file_out(path, std::ofstream::trunc | std::ofstream::binary);
|
||||
if (!file_out.is_open())
|
||||
return false;
|
||||
|
||||
nlohmann::json j = all_saved_animations;
|
||||
file_out << j.dump(4);
|
||||
file_out.close();
|
||||
|
||||
g_notification_service->push_success("Animations", std::format("Succesfully saved location {}", p.name));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ped_animation_service::delete_saved_animation(const std::string& category, ped_animation p)
|
||||
{
|
||||
auto path = get_ped_animations_file_path();
|
||||
|
||||
const auto& it = all_saved_animations.find(category);
|
||||
if (it == all_saved_animations.end())
|
||||
return false;
|
||||
|
||||
std::erase_if(it->second, [p](ped_animation p_) {
|
||||
return p_.name == p.name;
|
||||
});
|
||||
|
||||
if (!it->second.size())
|
||||
{
|
||||
all_saved_animations.erase(category);
|
||||
}
|
||||
|
||||
std::ofstream file_out(path, std::ofstream::trunc | std::ofstream::binary);
|
||||
if (!file_out.is_open())
|
||||
return false;
|
||||
|
||||
nlohmann::json j = all_saved_animations;
|
||||
file_out << j.dump(4);
|
||||
file_out.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ped_animation* ped_animation_service::get_animation_by_name(std::string name)
|
||||
{
|
||||
for (auto& anim : saved_animations_filtered_list())
|
||||
if (anim.name == name)
|
||||
return &anim;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ped_animation_service::play_saved_ped_animation(ped_animation p, Ped ped)
|
||||
{
|
||||
ped::ped_play_animation(ped, p.dict, p.anim, p.blendin, p.blendout, p.time_to_play, p.flags, p.start_phase, false, {p.pos[0], p.pos[1], p.pos[2]}, {p.rot[0], p.rot[1], p.rot[2]});
|
||||
}
|
||||
|
||||
void ped_animation_service::ambient_animations_prompt_tick()
|
||||
{
|
||||
if (!g.self.prompt_ambient_animations)
|
||||
return;
|
||||
|
||||
auto ambient_list = saved_animations_ambient_list();
|
||||
|
||||
ped_animation closest_ambient_animation{};
|
||||
float distance = 500;
|
||||
|
||||
for (auto& anim : ambient_list)
|
||||
{
|
||||
Vector3 anim_vector = {anim.pos[0], anim.pos[1], anim.pos[2]};
|
||||
|
||||
auto new_distance = math::distance_between_vectors(self::pos, anim_vector);
|
||||
if (new_distance < distance)
|
||||
{
|
||||
closest_ambient_animation = anim;
|
||||
distance = new_distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (math::distance_between_vectors(self::pos,
|
||||
{closest_ambient_animation.pos[0], closest_ambient_animation.pos[1], closest_ambient_animation.pos[2]})
|
||||
< 5)
|
||||
{
|
||||
if (!ENTITY::IS_ENTITY_PLAYING_ANIM(self::ped,
|
||||
closest_ambient_animation.dict.data(),
|
||||
closest_ambient_animation.anim.data(),
|
||||
3))
|
||||
{
|
||||
notify::display_help_text(
|
||||
std::format("~b~Ambient Animation~w~\nPress ~INPUT_PICKUP~ to play ~g~{}", closest_ambient_animation.name));
|
||||
if (PAD::IS_CONTROL_JUST_PRESSED(0, (int)ControllerInputs::INPUT_PICKUP))
|
||||
{
|
||||
play_saved_ped_animation(closest_ambient_animation, self::ped);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
notify::display_help_text(std::format("Press ~INPUT_PICKUP~ to stop playing ~g~{}", closest_ambient_animation.name));
|
||||
if (PAD::IS_CONTROL_JUST_PRESSED(0, (int)ControllerInputs::INPUT_PICKUP))
|
||||
{
|
||||
TASK::CLEAR_PED_TASKS(self::ped);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
src/services/ped_animations/ped_animations_service.hpp
Normal file
44
src/services/ped_animations/ped_animations_service.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "file_manager.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
struct ped_animation
|
||||
{
|
||||
std::string name;
|
||||
std::string dict;
|
||||
std::string anim;
|
||||
float blendin = 4.f;
|
||||
float blendout = -4.f;
|
||||
int time_to_play = -1;
|
||||
int flags = 0;
|
||||
float start_phase = 0;
|
||||
bool ambient = false;
|
||||
float pos[3] = {};
|
||||
float rot[3] = {};
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ped_animation, name, dict, anim, blendin, blendout, time_to_play, flags, start_phase, ambient, pos, rot);
|
||||
|
||||
class ped_animation_service
|
||||
{
|
||||
public:
|
||||
std::map<std::string, std::vector<ped_animation>> all_saved_animations;
|
||||
ped_animation current_animation;
|
||||
|
||||
bool fetch_saved_animations();
|
||||
bool save_new_animation(const std::string& category, ped_animation);
|
||||
bool delete_saved_animation(const std::string& category, ped_animation);
|
||||
ped_animation* get_animation_by_name(std::string name);
|
||||
std::vector<ped_animation> saved_animations_filtered_list(std::string filter);
|
||||
std::vector<ped_animation> saved_animations_ambient_list();
|
||||
|
||||
void play_saved_ped_animation(ped_animation p, Ped ped);
|
||||
void ambient_animations_prompt_tick();
|
||||
|
||||
private:
|
||||
std::filesystem::path get_ped_animations_file_path();
|
||||
};
|
||||
|
||||
inline ped_animation_service g_ped_animation_service;
|
||||
}
|
@ -7,14 +7,48 @@
|
||||
|
||||
namespace big::animations
|
||||
{
|
||||
|
||||
inline static std::multimap<std::string, std::string> all_anims;
|
||||
inline static std::vector<std::string> all_dicts;
|
||||
|
||||
enum class anim_flags
|
||||
{
|
||||
LOOPING = 1 << 0,
|
||||
HOLD_LAST_FRAME = 1 << 1,
|
||||
REPOSITION_WHEN_FINISHED = 1 << 2,
|
||||
NOT_INTERRUPTABLE = 1 << 3,
|
||||
UPPERBODY = 1 << 4,
|
||||
SECONDARY = 1 << 5,
|
||||
REORIENT_WHEN_FINISHED = 1 << 6,
|
||||
ABORT_ON_PED_MOVEMENT = 1 << 7,
|
||||
ADDITIVE = 1 << 8,
|
||||
TURN_OFF_COLLISION = 1 << 9,
|
||||
OVERRIDE_PHYSICS = 1 << 10,
|
||||
IGNORE_GRAVITY = 1 << 11,
|
||||
EXTRACT_INITIAL_OFFSET = 1 << 12,
|
||||
EXIT_AFTER_INTERRUPTED = 1 << 13,
|
||||
TAG_SYNC_IN = 1 << 14,
|
||||
TAG_SYNC_OUT = 1 << 15,
|
||||
TAG_SYNC_CONTINUOUS = 1 << 16,
|
||||
FORCE_START = 1 << 17,
|
||||
USE_KINEMATIC_PHYSICS = 1 << 18,
|
||||
USE_MOVER_EXTRACTION = 1 << 19,
|
||||
HIDE_WEAPON = 1 << 20,
|
||||
ENDS_IN_DEAD_POSE = 1 << 21,
|
||||
ACTIVATE_RAGDOLL_ON_COLLISION = 1 << 22,
|
||||
DONT_EXIT_ON_DEATH = 1 << 23,
|
||||
ABORT_ON_WEAPON_DAMAGE = 1 << 24,
|
||||
DISABLE_FORCED_PHYSICS_UPDATE = 1 << 25,
|
||||
PROCESS_ATTACHMENTS_ON_START = 1 << 26,
|
||||
EXPAND_PED_CAPSULE_FROM_SKELETON = 1 << 27,
|
||||
USE_ALTERNATIVE_FP_ANIM = 1 << 28,
|
||||
BLENDOUT_WRT_LAST_FRAME = 1 << 29,
|
||||
USE_FULL_BLENDING = 1 << 30
|
||||
};
|
||||
|
||||
/*
|
||||
Built with https://raw.githubusercontent.com/DurtyFree/gta-v-data-dumps/master/animDictsCompact.json in mind
|
||||
*/
|
||||
void populate_anim_list(nlohmann::json j)
|
||||
inline void populate_anim_list(nlohmann::json j)
|
||||
{
|
||||
for (const auto& animdict : j)
|
||||
{
|
||||
@ -57,9 +91,9 @@ namespace big::animations
|
||||
|
||||
if (!std::filesystem::exists(g_file_manager.get_project_file("animDictsCompact.json").get_path()))
|
||||
{
|
||||
LOG(INFO) << "Animations file is not in directory. https://raw.githubusercontent.com/DurtyFree/gta-v-data-dumps/master/animDictsCompact.json";
|
||||
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.");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = g_file_manager.get_project_file("animDictsCompact.json").get_path();
|
||||
|
@ -585,10 +585,13 @@ namespace big::ped
|
||||
return STREAMING::HAS_ANIM_DICT_LOADED(dict);
|
||||
}
|
||||
|
||||
inline void ped_play_animation(Ped ped, const std::string_view& animDict, const std::string_view& animName, float speed = 4.f, float speedMultiplier = -4.f, int duration = -1, int flag = 0, float playbackRate = 0, bool lockPos = false)
|
||||
inline void ped_play_animation(Ped ped, const std::string_view& animDict, const std::string_view& animName, float speed = 4.f, float speedMultiplier = -4.f, int duration = -1, int flag = 0, float playbackRate = 0, bool lockPos = false, Vector3 pos = {}, Vector3 rot = {}, int ik_flags = 0)
|
||||
{
|
||||
if (load_animation_dict(animDict.data()))
|
||||
TASK::TASK_PLAY_ANIM(ped, animDict.data(), animName.data(), speed, speedMultiplier, duration, flag, playbackRate, lockPos, lockPos, lockPos);
|
||||
if(pos.x == 0 && pos.y == 0 && pos.z == 0)
|
||||
TASK::TASK_PLAY_ANIM(ped, animDict.data(), animName.data(), speed, speedMultiplier, duration, flag, playbackRate, lockPos, lockPos, lockPos);
|
||||
else
|
||||
TASK::TASK_PLAY_ANIM_ADVANCED(ped, animDict.data(), animName.data(), pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, speed, speedMultiplier, duration, flag, playbackRate, lockPos, ik_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -19,7 +19,11 @@ namespace big
|
||||
script_events();
|
||||
scripts();
|
||||
threads();
|
||||
animations();
|
||||
if (ImGui::BeginTabItem("Animations"))
|
||||
{
|
||||
animations();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::End();
|
||||
|
@ -9,7 +9,7 @@ namespace big::debug
|
||||
extern void script_events();
|
||||
extern void scripts();
|
||||
extern void threads();
|
||||
extern void animations();
|
||||
extern void animations(std::string* dict = nullptr, std::string* anim = nullptr); // Can be used to retrieve animations
|
||||
|
||||
extern void main();
|
||||
}
|
@ -5,72 +5,74 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
void debug::animations()
|
||||
void debug::animations(std::string* dict, std::string* anim)
|
||||
{
|
||||
if (ImGui::BeginTabItem("Animations"))
|
||||
static std::string current_dict, current_anim;
|
||||
static std::vector<std::string> selected_dict_anim_list{};
|
||||
|
||||
if(dict && anim)
|
||||
{
|
||||
static std::string current_dict, current_anim;
|
||||
static std::vector<std::string> selected_dict_anim_list{};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
ImGui::Text("There are %d dictionaries with %d animations in memory", animations::anim_dict_count(), animations::total_anim_count());
|
||||
|
||||
components::button("Fetch All Anims", [] {
|
||||
animations::fetch_all_anims();
|
||||
});
|
||||
|
||||
ImGui::SetNextItemWidth(400);
|
||||
components::input_text_with_hint("##dictionaryfilter", "Dictionary", 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;
|
||||
|
||||
g_fiber_pool->queue_job([=] {
|
||||
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::EndListBox();
|
||||
}
|
||||
|
||||
components::button("Stop", [] {
|
||||
TASK::CLEAR_PED_TASKS(self::ped);
|
||||
});
|
||||
|
||||
ImGui::EndTabItem();
|
||||
*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(std::format("There are {} dictionaries with {} animations in memory", animations::anim_dict_count(), animations::total_anim_count()).data());
|
||||
|
||||
components::button("Fetch All Anims", [] {
|
||||
animations::fetch_all_anims();
|
||||
});
|
||||
|
||||
ImGui::SetNextItemWidth(400);
|
||||
components::input_text_with_hint("##dictionaryfilter", "Dictionary", 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("Play", [] {
|
||||
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("Stop", [] {
|
||||
TASK::CLEAR_PED_TASKS(self::ped);
|
||||
});
|
||||
}
|
||||
}
|
265
src/views/self/view_animations.cpp
Normal file
265
src/views/self/view_animations.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
#include "services/ped_animations/ped_animations_service.hpp"
|
||||
#include "util/animations.hpp"
|
||||
#include "views/debug/view_debug.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void view::animations()
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
|
||||
static std::string category = "Default";
|
||||
static ped_animation deletion_ped_animation{};
|
||||
|
||||
if (!std::string(deletion_ped_animation.name).empty())
|
||||
ImGui::OpenPopup("##deletepedanimation");
|
||||
|
||||
if (ImGui::BeginPopupModal("##deletepedanimation", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
|
||||
{
|
||||
ImGui::Text(std::format("Are you sure you want to delete {}", deletion_ped_animation.name).data());
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Yes"))
|
||||
{
|
||||
g_ped_animation_service.delete_saved_animation(category, deletion_ped_animation);
|
||||
deletion_ped_animation.name = "";
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No"))
|
||||
{
|
||||
deletion_ped_animation.name = "";
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PushItemWidth(250);
|
||||
components::input_text_with_hint("##dict", "Dict", g_ped_animation_service.current_animation.dict);
|
||||
components::options_modal(
|
||||
"Debug animations",
|
||||
[] {
|
||||
debug::animations(&g_ped_animation_service.current_animation.dict, &g_ped_animation_service.current_animation.anim);
|
||||
},
|
||||
true,
|
||||
"List From Debug");
|
||||
components::input_text_with_hint("##anim", "Anim", g_ped_animation_service.current_animation.anim);
|
||||
|
||||
ImGui::SameLine();
|
||||
components::button("Play", [] {
|
||||
g_ped_animation_service.play_saved_ped_animation(g_ped_animation_service.current_animation, self::ped);
|
||||
});
|
||||
ImGui::SameLine();
|
||||
components::button("Stop", [] {
|
||||
TASK::CLEAR_PED_TASKS(self::ped);
|
||||
});
|
||||
|
||||
if (ImGui::TreeNode("Advanced Options"))
|
||||
{
|
||||
ImGui::SliderFloat("Blend in", &g_ped_animation_service.current_animation.blendin, -5, 10);
|
||||
ImGui::SliderFloat("Blend out", &g_ped_animation_service.current_animation.blendout, -5, 10);
|
||||
ImGui::InputInt("Duration in ms", &g_ped_animation_service.current_animation.time_to_play);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("-1 will make the duration indefinite, assuming it is looped");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::Checkbox("Ambient", &g_ped_animation_service.current_animation.ambient);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Current location and rotation will be saved and used");
|
||||
|
||||
if (g_ped_animation_service.current_animation.ambient)
|
||||
{
|
||||
g_ped_animation_service.current_animation.pos[0] = self::pos.x;
|
||||
g_ped_animation_service.current_animation.pos[1] = self::pos.y;
|
||||
g_ped_animation_service.current_animation.pos[2] = self::pos.z;
|
||||
|
||||
g_ped_animation_service.current_animation.rot[0] = self::rot.x;
|
||||
g_ped_animation_service.current_animation.rot[1] = self::rot.y;
|
||||
g_ped_animation_service.current_animation.rot[2] = self::rot.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ped_animation_service.current_animation.pos[0] = 0;
|
||||
g_ped_animation_service.current_animation.pos[1] = 0;
|
||||
g_ped_animation_service.current_animation.pos[2] = 0;
|
||||
|
||||
g_ped_animation_service.current_animation.rot[0] = 0;
|
||||
g_ped_animation_service.current_animation.rot[1] = 0;
|
||||
g_ped_animation_service.current_animation.rot[2] = 0;
|
||||
}
|
||||
|
||||
ImGui::BeginGroup(); //Regular flags
|
||||
|
||||
ImGui::CheckboxFlags("Looped", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::LOOPING));
|
||||
ImGui::CheckboxFlags("Hold Last Frame", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::HOLD_LAST_FRAME));
|
||||
ImGui::CheckboxFlags("Uninterruptable", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::NOT_INTERRUPTABLE));
|
||||
ImGui::CheckboxFlags("Only Upperbody", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::UPPERBODY));
|
||||
ImGui::CheckboxFlags("Secondary slot", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::SECONDARY));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Allow primary animations to run simultaniously, such as walking");
|
||||
ImGui::CheckboxFlags("Realize Animation Orientation", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::REORIENT_WHEN_FINISHED));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Use the final orientation achieved in the animation");
|
||||
ImGui::CheckboxFlags("Hide Weapon", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::HIDE_WEAPON));
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
//Sync flags
|
||||
ImGui::CheckboxFlags("Sync In", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::TAG_SYNC_IN));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Seamless transition into the animation, for example from walking");
|
||||
ImGui::CheckboxFlags("Sync Out", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::TAG_SYNC_OUT));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Seamless transition out of the animation, for example to continue walking");
|
||||
ImGui::CheckboxFlags("Sync Continuous", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::TAG_SYNC_CONTINUOUS));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Seamless transition during the animation, especially usefull for upperbody animations");
|
||||
ImGui::CheckboxFlags("Force Start", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::FORCE_START));
|
||||
ImGui::CheckboxFlags("Disable Colission", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::TURN_OFF_COLLISION));
|
||||
ImGui::CheckboxFlags("Override Physics", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::OVERRIDE_PHYSICS));
|
||||
ImGui::CheckboxFlags("Ignore Gravity", reinterpret_cast<unsigned int*>(&g_ped_animation_service.current_animation.flags), static_cast<unsigned int>(animations::anim_flags::IGNORE_GRAVITY));
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Saving");
|
||||
|
||||
components::input_text_with_hint("Category", "Category", category);
|
||||
components::input_text_with_hint("Name", "Name", g_ped_animation_service.current_animation.name);
|
||||
|
||||
static auto save_response = [=]() -> bool {
|
||||
if (!STREAMING::DOES_ANIM_DICT_EXIST(g_ped_animation_service.current_animation.dict.data()))
|
||||
{
|
||||
g_notification_service->push_warning("Animations",
|
||||
std::format("Dict with the name {} does not exist", g_ped_animation_service.current_animation.dict));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_ped_animation_service.get_animation_by_name(g_ped_animation_service.current_animation.name))
|
||||
{
|
||||
g_notification_service->push_warning("Animations",
|
||||
std::format("Animation with the name {} already exists", g_ped_animation_service.current_animation.name));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (category.empty())
|
||||
{
|
||||
g_notification_service->push_warning("Animations", "Category can't be empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_ped_animation_service.current_animation.anim.empty())
|
||||
{
|
||||
g_notification_service->push_warning("Animations", "Animation name can't be empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
components::button("Save", [] {
|
||||
if (save_response())
|
||||
g_ped_animation_service.save_new_animation(category, g_ped_animation_service.current_animation);
|
||||
});
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SeparatorText("Saved");
|
||||
components::button("Refresh", [] {
|
||||
g_ped_animation_service.fetch_saved_animations();
|
||||
});
|
||||
|
||||
components::small_text("Double click to play\nShift click to delete");
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Checkbox("Prompt Ambient", &g.self.prompt_ambient_animations);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Ambient animations will be prompted if you are close to one");
|
||||
|
||||
static std::string filter;
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
components::input_text_with_hint("##filter", "Search", filter);
|
||||
|
||||
ImGui::BeginGroup();
|
||||
components::small_text("Categories");
|
||||
if (ImGui::BeginListBox("##categories", {200, static_cast<float>(*g_pointers->m_gta.m_resolution_y * 0.4)}))
|
||||
{
|
||||
for (auto& l : g_ped_animation_service.all_saved_animations | std::ranges::views::keys)
|
||||
{
|
||||
if (ImGui::Selectable(l.data(), l == category))
|
||||
{
|
||||
category = l;
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
components::small_text("Animations");
|
||||
if (ImGui::BeginListBox("##animations", {200, static_cast<float>(*g_pointers->m_gta.m_resolution_y * 0.4)}))
|
||||
{
|
||||
if (g_ped_animation_service.all_saved_animations.find(category)
|
||||
!= g_ped_animation_service.all_saved_animations.end())
|
||||
{
|
||||
std::vector<ped_animation> current_list{};
|
||||
|
||||
if (!filter.empty())
|
||||
current_list = g_ped_animation_service.saved_animations_filtered_list(filter);
|
||||
else
|
||||
current_list = g_ped_animation_service.all_saved_animations.at(category);
|
||||
|
||||
for (const auto& p : current_list)
|
||||
{
|
||||
if (ImGui::Selectable(p.name.data(), p.name == g_ped_animation_service.current_animation.name, ImGuiSelectableFlags_AllowDoubleClick))
|
||||
{
|
||||
if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
|
||||
{
|
||||
deletion_ped_animation = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ped_animation_service.current_animation = p;
|
||||
if (ImGui::IsMouseDoubleClicked(0))
|
||||
{
|
||||
g_fiber_pool->queue_job([p] {
|
||||
g_ped_animation_service.play_saved_ped_animation(g_ped_animation_service.current_animation, self::ped);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
if (p.name.length() > 25)
|
||||
ImGui::Text(p.name.data());
|
||||
|
||||
ImGui::Text(std::format("Dict: {}\nAnim: {}", p.dict, p.anim).data());
|
||||
|
||||
if (p.ambient)
|
||||
ImGui::BulletText("Ambient animation");
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ namespace big
|
||||
static void overlay();
|
||||
static void root();
|
||||
static void self();
|
||||
static void animations();
|
||||
static void network();
|
||||
static void missions();
|
||||
static void player_database();
|
||||
|
Reference in New Issue
Block a user