Files
Akebi-GC3_0/cheat-library/src/user/cheat/misc/Debug.cpp
2022-07-13 09:26:52 +01:00

1277 lines
47 KiB
C++

#include "pch-il2cpp.h"
#include "Debug.h"
#include <misc/cpp/imgui_stdlib.h>
#include <filesystem>
#include <fstream>
#include <cheat/events.h>
#include <cheat/teleport/MapTeleport.h>
#include <cheat/game/EntityManager.h>
#include <cheat/game/util.h>
#include <cheat/game/filters.h>
#include <cheat/esp/ESPRender.h>
#include <cheat/game/CacheFilterExecutor.h>
#include <cheat-base/render/renderer.h>
#include <helpers.h>
#include <imgui_internal.h>
// This module is for debug purpose, and... well.. it's shit coded ^)
namespace cheat::feature
{
static bool ActorAbilityPlugin_OnEvent_Hook(void* __this, app::BaseEvent* e, MethodInfo* method);
void OnGameUpdate();
static bool csvFriendly = true;
static bool includeHeaders = true;
Debug::Debug() : Feature()
{
events::GameUpdateEvent += FUNCTION_HANDLER(OnGameUpdate);
HookManager::install(app::MoleMole_ActorAbilityPlugin_OnEvent, ActorAbilityPlugin_OnEvent_Hook);
// HookManager::install(app::MoleMole_LuaShellManager_ReportLuaShellResult, LuaShellManager_ReportLuaShellResult_Hook);
// HookManager::install(app::MoleMole_LuaShellManager_DoString, LuaShellManager_DoString_Hook);
// HookManager::install(app::LuaEnv_DoString, LuaEnv_DoString_Hook);
// HookManager::install(app::Lua_xlua_pushasciistring, Lua_xlua_pushasciistring_Hook);
// HookManager::install(app::GameLogin_SendInfo_2, SendInfo_Hook);
// LOG_DEBUG("Hooked GameLogin::SendGameInfo. Origin at 0x%p", HookManager::getOrigin(SendInfo_Hook));
}
const FeatureGUIInfo& Debug::GetGUIInfo() const
{
static const FeatureGUIInfo info{ "DebugInfo", "Debug", false };
return info;
}
Debug& Debug::GetInstance()
{
static Debug instance;
return instance;
}
// Raise when player start game log in (after press a door)
// Contains information about player system and game integrity
static void SendInfo_Hook(app::MoleMole_NetworkManager* __this, app::GKOJAICIOPA* info, MethodInfo* method)
{
LOG_TRACE("Game sending game info to server.");
LOG_TRACE("Content: ");
#define printString(i) if (info->fields.string_ ## i > (void *)1 && info->fields.string_ ## i ##->fields.length > 0)\
LOG_TRACE("\tfield#%d: %s", i ,il2cppi_to_string(info->fields.string_ ## i).c_str());
printString(1);
printString(2);
printString(3);
printString(4);
printString(5);
printString(6);
printString(7);
printString(8);
printString(9);
printString(10);
printString(11);
printString(12);
printString(13);
printString(14);
printString(15);
printString(16);
#undef printString
CALL_ORIGIN(SendInfo_Hook, __this, info, method);
}
static void Lua_xlua_pushasciistring_Hook(void* __this, void* L, app::String* str, MethodInfo* method)
{
LOG_DEBUG("Pushed string: %s", il2cppi_to_string(str).c_str());
CALL_ORIGIN(Lua_xlua_pushasciistring_Hook, __this, L, str, method);
}
static int checkCount = 0;
static void* LuaEnv_DoString_Hook(void* __this, app::Byte__Array* chunk, app::String* chunkName, void* env, MethodInfo* method)
{
if (checkCount > 0)
{
LOG_DEBUG("After size %d; name: %s", chunk->bounds == nullptr ? chunk->max_length : chunk->bounds->length, il2cppi_to_string(chunkName).c_str());
checkCount--;
}
return CALL_ORIGIN(LuaEnv_DoString_Hook, __this, chunk, chunkName, env, method);
}
static void LuaShellManager_DoString_Hook(void* __this, app::Byte__Array* byteArray, MethodInfo* method)
{
LOG_DEBUG("Size %d", byteArray->bounds == nullptr ? byteArray->max_length : byteArray->bounds->length);
checkCount = 10;
CALL_ORIGIN(LuaShellManager_DoString_Hook, __this, byteArray, method);
}
static void LuaShellManager_ReportLuaShellResult_Hook(void* __this, app::String* type, app::String* value, MethodInfo* method)
{
std::cout << "Type: " << il2cppi_to_string(type) << std::endl;
std::cout << "Value: " << il2cppi_to_string(value) << std::endl;
CALL_ORIGIN(LuaShellManager_ReportLuaShellResult_Hook, __this, type, value, method);
}
static bool ActorAbilityPlugin_OnEvent_Hook(void* __this, app::BaseEvent* e, MethodInfo* method)
{
// LOG_DEBUG("Fire event: %s, targetID %u", magic_enum::enum_name(e->fields.eventID).data(), e->fields.targetID);
return CALL_ORIGIN(ActorAbilityPlugin_OnEvent_Hook, __this, e, method);
}
static void DrawWaypoints(UniDict<uint32_t, UniDict<uint32_t, app::MapModule_ScenePointData>*>* waypointsGrops)
{
if (waypointsGrops == nullptr)
{
ImGui::Text("Waypoint data doesn't exist.");
return;
}
auto singleton = GET_SINGLETON(MoleMole_MapModule);
for (const auto& [sceneId, waypoints] : waypointsGrops->pairs())
{
if (ImGui::TreeNode(("WTD " + std::to_string(sceneId)).c_str(), "Waypoint group id %d", sceneId))
{
for (const auto& [waypointId, waypoint] : waypoints->pairs())
{
if (ImGui::TreeNode(("WD " + std::to_string(waypointId)).c_str(), "Waypoint id %d", waypointId))
{
ImGui::Text("IsGroupLimit: %s", waypoint.isGroupLimit ? "true" : "false");
ImGui::Text("IsUnlocked: %s", waypoint.isUnlocked ? "true" : "false");
ImGui::Text("Level: %u", waypoint.level);
ImGui::Text("EntityId: %u", waypoint.entityId);
ImGui::Text("ModelHiden: %s", waypoint.isModelHidden ? "true" : "false");
if (waypoint.config != nullptr)
{
auto location = waypoint.config->fields;
ImGui::Text("Waypoint type: %s", magic_enum::enum_name(location._type).data());
ImGui::Text("Trans position: %s", il2cppi_to_string(location._tranPos).c_str());
ImGui::Text("Object position: %s", il2cppi_to_string(location._pos).c_str());
ImGui::Text("_unlocked: %s", location._unlocked ? "true" : "false");
ImGui::Text("_groupLimit: %s", location._groupLimit ? "true" : "false");
uint16_t areaId = app::MoleMole_SimpleSafeUInt16_get_Value(location.areaIdRawNum, nullptr);
ImGui::Text("areaId: %u", areaId);
ImGui::Text("areaUnlocked: %s", app::MoleMole_MapModule_IsAreaUnlock(singleton, sceneId, areaId, nullptr) ? "true" : "false");
ImGui::Text("gadgetIdRawNum: %u", location.gadgetIdRawNum);
}
ImGui::TreePop();
}
}
ImGui::TreePop();
}
}
}
void DrawManagerData()
{
auto singleton = GET_SINGLETON(MoleMole_MapModule);
if (singleton == nullptr)
{
ImGui::Text("Manager not initialized.");
return;
}
if (ImGui::TreeNode("Waypoints"))
{
auto waypoints = TO_UNI_DICT(singleton->fields._scenePointDics, uint32_t, UniDict<uint32_t COMMA app::MapModule_ScenePointData>*);
DrawWaypoints(waypoints);
ImGui::TreePop();
}
}
void DrawEntity(game::Entity* entity)
{
if (entity == nullptr)
{
ImGui::Text("Entity doesn't exist.");
return;
}
ImGui::Text("Entity type: %s", magic_enum::enum_name(entity->type()).data());
ImGui::Text("Entity name: %s", entity->name().c_str());
}
void CopyEntityDetailsToClipboard(std::vector<game::Entity*> entities)
{
std::string entitiesDetails = "";
if (csvFriendly && includeHeaders)
entitiesDetails.append("Entity,RuntimeID,Name,PosX,PosY,PosZ\n");
for (auto entity : entities) {
auto entityPos = entity->absolutePosition();
std::string baseString = csvFriendly ? "{},{},{},{},{},{}" : "{} {} {} x={} y={} z={}";
auto entityDetails = fmt::format(
fmt::runtime(baseString),
fmt::ptr(entity),
entity->runtimeID(),
entity->name().c_str(),
entityPos.x, entityPos.y, entityPos.z
);
entitiesDetails.append(entityDetails);
entitiesDetails.append("\n");
}
ImGui::SetClipboardText(entitiesDetails.c_str());
}
void CopyEntityDetailsToClipboard(game::Entity* entity)
{
auto entityPos = entity->absolutePosition();
std::string headerString = "Entity,RuntimeID,Name,PosX,PosY,PosZ\n";
std::string baseString = csvFriendly ? "{},{},{},{},{},{}" : "{} {} {} x={} y={} z={}";
if (csvFriendly && includeHeaders)
baseString = headerString.append(baseString);
auto entityDetails = fmt::format(
fmt::runtime(baseString),
fmt::ptr(entity),
entity->runtimeID(),
entity->name().c_str(),
entityPos.x, entityPos.y, entityPos.z
);
ImGui::SetClipboardText(entityDetails.c_str());
}
void DrawCombatDetails(game::Entity* entity)
{
auto combat = entity->combat();
if (combat != nullptr) {
auto combatProp = combat->fields._combatProperty_k__BackingField;
auto maxHP = app::MoleMole_SafeFloat_get_Value(combatProp->fields.maxHP, nullptr);
auto HP = app::MoleMole_SafeFloat_get_Value(combatProp->fields.HP, nullptr);
auto isLockHp = combatProp->fields.islockHP == nullptr || app::MoleMole_FixedBoolStack_get_value(combatProp->fields.islockHP, nullptr);
auto isInvincible = combatProp->fields.isInvincible == nullptr || app::MoleMole_FixedBoolStack_get_value(combatProp->fields.isInvincible, nullptr);
ImGui::BeginTooltip();
ImGui::Text("Combat: %s", combat == nullptr ? "No" : "Yes");
ImGui::Text("Combat Prop: %s", combatProp == nullptr ? "No" : "Yes");
ImGui::Text("HP Curr/Max: %.01f/%.01f", HP, maxHP);
ImGui::Text("Locked HP: %s", isLockHp ? "Yes" : "No");
ImGui::Text("Invincible: %s", isInvincible ? "Yes" : "No");
ImGui::EndTooltip();
}
}
void DrawEntityActionButtons(game::Entity* entity, bool& csvFriendly)
{
auto& manager = game::EntityManager::instance();
if (ImGui::SmallButton("T"))
{
auto& mapTeleport = MapTeleport::GetInstance();
mapTeleport.TeleportTo(entity->absolutePosition());
};
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Teleport");
ImGui::SameLine();
if (ImGui::SmallButton("S"))
entity->setRelativePosition(manager.avatar()->relativePosition());
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Summon");
ImGui::SameLine();
if (ImGui::SmallButton("B"))
entity->setRelativePosition({ 0, 0, 0 });
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Banish");
ImGui::SameLine();
if (ImGui::SmallButton("C"))
CopyEntityDetailsToClipboard(entity);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Copy Details");
}
std::vector<game::Entity*> SortEntities(std::vector<game::Entity*> entities, Debug::EntitySortCondition condition)
{
switch (condition) {
case Debug::EntitySortCondition::RuntimeID: {
std::sort(entities.begin(),
entities.end(),
[](game::Entity* e1, game::Entity* e2) {
auto s1 = e1->runtimeID();
auto s2 = e2->runtimeID();
return s1 < s2;
});
break;
}
case Debug::EntitySortCondition::Name: {
std::sort(entities.begin(),
entities.end(),
[](game::Entity* e1, game::Entity* e2) {
auto s1 = e1->name().c_str();
auto s2 = e2->name().c_str();
return s1 < s2;
});
break;
}
case Debug::EntitySortCondition::Distance: {
std::sort(entities.begin(),
entities.end(),
[](game::Entity* e1, game::Entity* e2) {
auto& manager = game::EntityManager::instance();
return manager.avatar()->distance(e1) < manager.avatar()->distance(e2);
});
break;
}
default:
break;
}
return entities;
}
void TeleportByCondition(std::vector<game::Entity*> entities, Debug::TeleportCondition condition)
{
auto& manager = game::EntityManager::instance();
auto& mapTeleport = MapTeleport::GetInstance();
// Opted for this instead of min_/max_element to guarantee no voodoo magic happens.
// We'll go for min_/max_element later on if we want to implement weird sorts like
// lowest HP/highest HP/etc. Even then, that will be in SortEntities, not here.
// Like so: SortEntities(entities, Debug::EntitySortCondition::Health);
auto sortedEntities = SortEntities(entities, Debug::EntitySortCondition::Distance);
// Always have a default target!
auto target = sortedEntities.front();
// Keeping this as a switch statement instead of ternary. We don't know yet how
// many cases we want to keep supporting. Ternary is cleaner, but not a big
// performance hit if we keep a switch statement.
switch (condition) {
case Debug::TeleportCondition::Closest: {
// We've already selected this.
break;
}
case Debug::TeleportCondition::Farthest: {
target = sortedEntities.back();
break;
}
}
// Separating this logic to keep it clean and consistent.
if (target != nullptr)
{
auto targetDist = manager.avatar()->distance(target);
if (targetDist > 30.0f)
mapTeleport.TeleportTo(target->absolutePosition());
else manager.avatar()->setRelativePosition(target->relativePosition());
}
}
void SummonEntities(game::Entity* entity)
{
auto& manager = game::EntityManager::instance();
entity->setRelativePosition(manager.avatar()->relativePosition());
}
void SummonEntities(std::vector<game::Entity*> entities)
{
for (auto entity : entities)
SummonEntities(entity);
}
void BanishEntities(game::Entity* entity)
{
entity->setRelativePosition({ 0, 0, 0 });
}
void BanishEntities(std::vector<game::Entity*> entities)
{
for (auto entity : entities)
BanishEntities(entity);
}
void DrawEntityGroupActionButtons(std::vector<game::Entity*> entities, bool& csvFriendly, bool& includeHeaders)
{
auto& manager = game::EntityManager::instance();
if (ImGui::Button("Teleport: Closest"))
TeleportByCondition(entities, Debug::TeleportCondition::Closest);
ImGui::SameLine();
if (ImGui::Button("Teleport: Farthest"))
TeleportByCondition(entities, Debug::TeleportCondition::Farthest);
ImGui::SameLine();
if (ImGui::Button("Summon All"))
SummonEntities(entities);
ImGui::SameLine();
if (ImGui::Button("Banish All"))
BanishEntities(entities);
ImGui::SameLine();
if (ImGui::Button("Copy All Details"))
CopyEntityDetailsToClipboard(entities);
ImGui::SameLine();
ImGui::Checkbox("CSV Friendly", &csvFriendly);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Uses comma separation and removes xyz from pos on copy.");
if (csvFriendly) {
ImGui::SameLine();
ImGui::Checkbox("Include Headers", &includeHeaders);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Includes headers when copying.");
}
}
void DrawEntitiesTable(std::vector<game::Entity*> entities)
{
auto& manager = game::EntityManager::instance();
auto clipSize = min(entities.size(), 15) + 1; // Number of rows in table as initial view. Past this is scrollbar territory.
static ImGuiTableFlags flags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable // | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
| ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
| ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("EntityTable", 8, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * clipSize), 0.0f))
{
ImGui::TableSetupColumn("Commands", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0, 0);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 0.0f, 1);
ImGui::TableSetupColumn("RuntimeID", ImGuiTableColumnFlags_WidthFixed, 0.0f, 2);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, 3);
ImGui::TableSetupColumn("Distance", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortAscending | ImGuiTableColumnFlags_WidthFixed, 0.0, 4);
ImGui::TableSetupColumn("Pos.x", ImGuiTableColumnFlags_WidthFixed, 0.0, 5);
ImGui::TableSetupColumn("Pos.y", ImGuiTableColumnFlags_WidthFixed, 0.0, 6);
ImGui::TableSetupColumn("Pos.z", ImGuiTableColumnFlags_WidthFixed, 0.0, 7);
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableHeadersRow();
ImGuiListClipper clipper;
clipper.Begin(static_cast<int>(entities.size()));
while (clipper.Step())
for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
{
auto entity = entities[row_n];
auto entityPos = entity->absolutePosition();
ImGui::PushID(entity->runtimeID());
ImGui::TableNextRow();
ImGui::TableNextColumn();
DrawEntityActionButtons(entity, csvFriendly);
ImGui::TableNextColumn();
ImGui::Text("0x%p", entity);
if (ImGui::IsItemHovered())
DrawCombatDetails(entity);
ImGui::TableNextColumn();
ImGui::Text("%u", entity->runtimeID());
ImGui::TableNextColumn();
ImGui::Text("%s", entity->name().c_str());
ImGui::TableNextColumn();
ImGui::Text("%.3fm", manager.avatar()->distance(entity));
ImGui::TableNextColumn();
ImGui::Text("%.04f", entityPos.x);
ImGui::TableNextColumn();
ImGui::Text("%.04f", entityPos.y);
ImGui::TableNextColumn();
ImGui::Text("%.04f", entityPos.z);
ImGui::PopID();
}
ImGui::EndTable();
}
}
static void DrawEntitiesData()
{
static bool typeFilters[0x63] = {};
static bool typeFiltersInitialized = false;
if (!typeFiltersInitialized) {
std::fill_n(typeFilters, 0x63, true);
typeFiltersInitialized = true;
}
static bool useObjectNameFilter = false;
static char objectNameFilter[128] = {};
static float radius = 0.0f;
static bool useRadius = false;
static bool groupByType = true;
static int typeFiltersColCount = 5;
static bool checkOnlyShells = false;
static bool showEmptyTypes = false;
static Debug::EntitySortCondition sortCondition = Debug::EntitySortCondition::Distance;
static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyScroll |
ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_TabListPopupButton;
auto& manager = game::EntityManager::instance();
auto entities = manager.entities();
auto entries = magic_enum::enum_entries<app::EntityType__Enum_1>();
std::vector<std::pair<app::EntityType__Enum_1, std::string_view>> sortedEntries;
sortedEntries.insert(sortedEntries.begin(), std::begin(entries), std::end(entries));
std::sort(sortedEntries.begin(), sortedEntries.end(), [](auto a1, auto a2) {
return a1.second < a2.second;
});
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
ImGui::Text("Entity Count %d", entities.size());
ImGui::Checkbox("## Enable Object Name Filter", &useObjectNameFilter); ImGui::SameLine();
if (!useObjectNameFilter)
ImGui::BeginDisabled();
ImGui::InputText("Entity Name Filter", objectNameFilter, 128);
if (!useObjectNameFilter)
ImGui::EndDisabled();
ImGui::Checkbox("Filter by Radius", &useRadius);
if (!useRadius)
ImGui::BeginDisabled();
ImGui::SameLine();
ImGui::PushItemWidth(200.0);
ImGui::SliderFloat("Radius", &radius, 0.0f, 100.0f);
ImGui::PopItemWidth();
if (!useRadius)
ImGui::EndDisabled();
if (ImGui::BeginTabBar("EntityManagerTabBar", tab_bar_flags))
{
if (ImGui::BeginTabItem("Type Filter"))
{
if (ImGui::Button("Select All"))
std::fill_n(typeFilters, 0x63, true);
ImGui::SameLine();
if (ImGui::Button("Deselect All"))
std::fill_n(typeFilters, 0x63, false);
ImGui::SameLine();
ImGui::PushItemWidth(100.0);
ImGui::SliderInt("No. of Columns", &typeFiltersColCount, 2, 5);
ImGui::PopItemWidth();
if (ImGui::BeginTable("Type Filter Table", typeFiltersColCount, ImGuiTableFlags_NoBordersInBody))
{
for (const auto& [value, name] : sortedEntries)
{
ImGui::TableNextColumn();
ImGui::Checkbox(name.data(), &typeFilters[(int)value]);
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Entity List"))
{
// Checkbox: Group by Type.
ImGui::Checkbox("Group by Type", &groupByType);
ImGui::SameLine();
if (groupByType) {
ImGui::Checkbox("Show Empty Types", &showEmptyTypes);
ImGui::SameLine();
}
ImGui::Checkbox("Show Only Oculi", &checkOnlyShells);
ImGui::SameLine();
bool sortConditionChanged = ComboEnum("Sort Mode", &sortCondition);
if (entities.size() > 0) {
if (groupByType) {
if (ImGui::BeginTabBar("EntityListTabBar", tab_bar_flags))
{
for (const auto& [currentType, typeName] : sortedEntries) {
if (!typeFilters[int(currentType)])
continue;
auto filteredEntities = manager.entities(game::SimpleFilter(currentType));
if (!showEmptyTypes && filteredEntities.size() == 0)
continue;
std::vector<cheat::game::Entity*> validEntities;
for (const auto& entity : filteredEntities)
{
if (entity == nullptr)
continue;
if (entity->type() != currentType)
continue;
if (checkOnlyShells && !game::filters::combined::Oculies.IsValid(entity))
continue;
if (useObjectNameFilter && entity->name().find(objectNameFilter) == -1)
continue;
if (useRadius)
{
auto dist = manager.avatar()->distance(entity);
if (dist > radius)
continue;
}
validEntities.push_back(entity);
}
if (validEntities.size() == 0 && !showEmptyTypes)
continue;
if (ImGui::BeginTabItem(typeName.data()))
{
auto sortedEntities = SortEntities(validEntities, sortCondition);
DrawEntityGroupActionButtons(sortedEntities, csvFriendly, includeHeaders);
DrawEntitiesTable(sortedEntities);
ImGui::EndTabItem();
}
}
}
ImGui::EndTabBar();
}
else {
std::vector<cheat::game::Entity*> validEntities;
for (const auto& entity : entities)
{
if (entity == nullptr)
continue;
if (!typeFilters[int(entity->type())])
continue;
if (checkOnlyShells && !game::filters::combined::Oculies.IsValid(entity))
continue;
if (useObjectNameFilter && entity->name().find(objectNameFilter) == -1)
continue;
if (useRadius)
{
auto dist = manager.avatar()->distance(entity);
if (dist > radius)
continue;
}
validEntities.push_back(entity);
}
auto sortedEntities = SortEntities(validEntities, sortCondition);
DrawEntityGroupActionButtons(sortedEntities, csvFriendly, includeHeaders);
DrawEntitiesTable(sortedEntities);
ImGui::TreePop();
}
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
#define DRAW_UINT(owner, fieldName) ImGui::Text("%s: %u", #fieldName, owner##->fields.##fieldName );
#define DRAW_FLOAT(owner, fieldName) ImGui::Text("%s: %f", #fieldName, owner##->fields.##fieldName );
#define DRAW_BOOL(owner, fieldName) ImGui::Text("%s: %s", #fieldName, owner##->fields.##fieldName ? "true" : "false");
static void DrawBaseInteraction(app::BaseInterAction* inter)
{
ImGui::Text("_type: %s", magic_enum::enum_name(inter->fields._type).data());
DRAW_UINT(inter, _mainQuestId);
DRAW_BOOL(inter, _isFromExternal);
DRAW_BOOL(inter, _isStarted);
DRAW_BOOL(inter, _isFinished);
auto cfg = inter->fields._cfg;
if (cfg == nullptr)
return;
ImGui::Text("Config: ");
ImGui::Text("_type: %s", magic_enum::enum_name(cfg->fields._type).data());
DRAW_FLOAT(cfg, _delayTime);
DRAW_FLOAT(cfg, _duration);
DRAW_FLOAT(cfg, _checkNextImmediately);
}
static void DrawInteractionManagerInfo()
{
auto interactionManager = GET_SINGLETON(InteractionManager);
if (interactionManager == nullptr)
{
ImGui::Text("Manager not loaded.");
return;
}
DRAW_UINT(interactionManager, _keyInterCnt);
DRAW_FLOAT(interactionManager, _endFadeInTime);
DRAW_FLOAT(interactionManager, _endFadeOutTime);
DRAW_BOOL(interactionManager, _hasKeyPre);
DRAW_BOOL(interactionManager, _havEndFade);
DRAW_BOOL(interactionManager, _inEndFade);
DRAW_BOOL(interactionManager, _inStartFade);
DRAW_BOOL(interactionManager, _talkLoading);
DRAW_BOOL(interactionManager, _voiceLoading);
DRAW_BOOL(interactionManager, _isLockGameTime);
DRAW_BOOL(interactionManager, _isInteeReadyChecking);
DRAW_BOOL(interactionManager, _isDelayClear);
DRAW_BOOL(interactionManager, _isFromPerformConfig);
DRAW_BOOL(interactionManager, _edtTalkWaiting);
DRAW_BOOL(interactionManager, _isManulAttackMode);
DRAW_BOOL(interactionManager, _canShowAvatarEffectWhenTalkStart);
auto keyList = TO_UNI_LINK_LIST(interactionManager->fields._keyInterList, app::InterActionGrp*);
if (keyList != nullptr && ImGui::TreeNode("KeyList"))
{
auto reminder = keyList->count;
auto current = keyList->first;
while (reminder > 0 && current != nullptr)
{
auto item = current->item;
if (ImGui::TreeNode(item, "Key item: gid %d", item->fields.groupId))
{
DRAW_UINT(item, groupId);
DRAW_UINT(item, nextGroupId);
DRAW_BOOL(item, isKeyList);
DRAW_BOOL(item, _isStarted);
if (item->fields._interActionList != nullptr && ImGui::TreeNode("Interactions"))
{
auto interactions = TO_UNI_LIST(item->fields._interActionList, app::BaseInterAction*);
for (auto& interaction : *interactions)
{
if (interaction == nullptr)
continue;
if (ImGui::TreeNode(interaction, "Base interaction"))
{
DrawBaseInteraction(interaction);
ImGui::TreePop();
}
}
ImGui::TreePop();
}
ImGui::TreePop();
}
current = current->forward;
reminder--;
}
ImGui::TreePop();
}
}
#undef DRAW_UINT
#undef DRAW_FLOAT
#undef DRAW_BOOL
void DrawPositionInfo()
{
auto avatarPos = app::ActorUtils_GetAvatarPos(nullptr);
ImGui::Text("Avatar position: %s", il2cppi_to_string(avatarPos).c_str());
auto relativePos = app::WorldShiftManager_GetRelativePosition(avatarPos, nullptr);
ImGui::Text("Relative position: %s", il2cppi_to_string(relativePos).c_str());
auto levelPos = app::Miscs_GenLevelPos_1(avatarPos, nullptr);
ImGui::Text("Level position: %s", il2cppi_to_string(levelPos).c_str());
static app::Vector3 teleportPos = {};
ImGui::InputFloat3("Teleport position", reinterpret_cast<float*>(&teleportPos));
auto& teleport = MapTeleport::GetInstance();
if (ImGui::Button("Map teleport"))
teleport.TeleportTo(app::Vector2{ teleportPos.x, teleportPos.y });
ImGui::SameLine();
if (ImGui::Button("World teleport"))
teleport.TeleportTo(teleportPos);
if (ImGui::TreeNode("Ground pos info"))
{
auto groundNormal = app::Miscs_CalcCurrentGroundNorm(avatarPos, nullptr);
ImGui::Text("Ground normal: %s", il2cppi_to_string(groundNormal).c_str());
static app::Vector3 pos{};
static bool fixedToPos;
ImGui::Checkbox("## Fixed to position", &fixedToPos); ImGui::SameLine();
if (fixedToPos) {
pos = relativePos;
pos.y = 1000;
}
ImGui::DragFloat3("Checked pos", (float*)&pos, 1.0f, -4000.0f, 4000.0f);
static float length = 1000;
ImGui::DragFloat("Raycast length", &length, 1.0f, -2000.0f, 2000.0f);
ImGui::Text("All: %f", app::Miscs_CalcCurrentGroundHeight_1(avatarPos.x, avatarPos.z, avatarPos.y, length, 0xFFFFFFFF, nullptr));
ImGui::TreePop();
}
if (ImGui::Button("Copy Position"))
{
auto text = il2cppi_to_string(avatarPos);
ImGui::SetClipboardText(text.c_str());
}
ImGui::SameLine();
if (ImGui::Button("Copy All Info"))
{
auto text = il2cppi_to_string(avatarPos) + "\n" + il2cppi_to_string(relativePos) + "\n" + il2cppi_to_string(levelPos) + "\n" + il2cppi_to_string(app::Miscs_CalcCurrentGroundNorm(avatarPos, nullptr));
ImGui::SetClipboardText(text.c_str());
}
ImGui::SameLine();
if (ImGui::Button("Copy as json"))
{
std::string text = "\"position\":[";
text += std::to_string(avatarPos.x) + ",";
text += std::to_string(avatarPos.y) + ",";
text += std::to_string(avatarPos.z) + "]";
std::string name = "";
for (int i = 0; i < 10; i++)
name += std::to_string(rand() % 10);
text = "\"name\":\"" + name + "\"," + text;
text = "{" + text + "}";
ImGui::SetClipboardText(text.c_str());
}
}
void DrawMapManager()
{
auto mapManager = GET_SINGLETON(MoleMole_MapManager);
if (mapManager == nullptr)
return;
int temp = mapManager->fields.playerSceneID;
ImGui::InputInt("Player scene id", &temp);
temp = mapManager->fields.mapSceneID;
ImGui::InputInt("Map scene id", &temp);
}
void DrawImGuiFocusTest()
{
ImGui::Text("Is any item active: %s", ImGui::IsAnyItemActive() ? "true" : "false");
ImGui::Text("Is any item focused: %s", ImGui::IsAnyItemFocused() ? "true" : "false");
ImGui::Button("Test");
auto hk = Hotkey();
int temp = 0;
InputHotkey("Test hotkey", &hk, false);
ImGui::InputInt("Test input", &temp);
}
std::map<std::string, std::string> chestNames;
std::unordered_set<std::string> notWrittenChests;
bool showNotWritten = false;
void OnGameUpdate()
{
if (!showNotWritten)
return;
auto& entityManager = game::EntityManager::instance();
notWrittenChests.clear();
for (auto& entity : entityManager.entities(game::filters::combined::Chests))
{
auto& entityName = entity->name();
if (chestNames.count(entityName) == 0)
notWrittenChests.insert(entityName);
}
}
void DrawChestPlugin()
{
static std::map<std::string, std::string> tempNames;
auto& entityManager = game::EntityManager::instance();
ImGui::Checkbox("Show not written", &showNotWritten);
for (auto& entity : entityManager.entities(game::filters::combined::Chests))
{
auto& entityName = entity->name();
if (showNotWritten && chestNames.count(entityName) > 0)
continue;
app::LCChestPlugin* chestPlugin = entity->plugin<app::LCChestPlugin>(*app::LCChestPlugin__TypeInfo);
if (chestPlugin == nullptr)
continue;
if (!ImGui::TreeNode(entity, "Chest 0x%p, Distance: %f", entity, entityManager.avatar()->distance(entity)))
continue;
auto& pluginData = chestPlugin->fields;
auto& owner = pluginData._owner->fields;
auto& ownerData = owner._dataItem->fields;
app::GadgetState__Enum chestState = static_cast<app::GadgetState__Enum>(ownerData.gadgetState);
ImGui::Text("Is ability locked: %s", pluginData._isLockByAbility ? "true" : "false");
ImGui::Text("State: %s", magic_enum::enum_name(chestState).data());
bool added = chestNames.count(entityName) > 0;
if (tempNames.count(entityName) == 0)
tempNames[entityName] = added ? chestNames[entityName] : std::string();
auto& tempName = tempNames[entityName];
ImGui::PushID(entity);
ImGui::Text("Name: %s", entityName.c_str());
ImGui::InputText("Friendly name", &tempName);
if (ImGui::Button(added ? "Update" : "Add"))
chestNames[entityName] = tempName;
if (ImGui::Button("Teleport"))
{
auto& mapTeleport = MapTeleport::GetInstance();
mapTeleport.TeleportTo(entity->absolutePosition());
}
ImGui::PopID();
ImGui::TreePop();
}
if (ImGui::TreeNode("Chest dictionary"))
{
std::stringstream text;
text << "{\n";
for (auto& [rawName, friendlyName] : chestNames)
{
text << "\t\"" << friendlyName << "\" : \"" << rawName << "\",\n";
}
text << "}";
std::string textStr = text.str();
ImGui::InputTextMultiline("Dict", &textStr);
ImGui::TreePop();
}
}
void DrawScenePropManager()
{
auto scenePropManager = GET_SINGLETON(MoleMole_ScenePropManager);
if (scenePropManager == nullptr)
{
ImGui::Text("Scene prop manager not loaded.");
return;
}
auto scenePropDict = TO_UNI_DICT(scenePropManager->fields._scenePropDict, int32_t, app::Object*);
if (scenePropDict == nullptr)
{
ImGui::Text("Scene prop dict is nullptr.");
return;
}
ImGui::Text("Prop count: %d", scenePropDict->count);
auto& manager = game::EntityManager::instance();
for (auto& [id, propObject] : scenePropDict->pairs())
{
auto tree = CastTo<app::SceneTreeObject>(propObject, *app::SceneTreeObject__TypeInfo);
if (tree == nullptr)
continue;
auto pos = tree->fields._.realBounds.m_Center;
auto config = tree->fields._config->fields;
auto pattern = config._._.scenePropPatternName;
app::MoleMole_Config_TreeType__Enum value;
bool result = app::MoleMole_ScenePropManager_GetTreeTypeByPattern(scenePropManager, pattern, &value, nullptr);
if (!result)
continue;
ImGui::Text("Tree at %s, type: %s, distance %0.3f", il2cppi_to_string(pos).c_str(), magic_enum::enum_name(value).data(),
manager.avatar()->distance(app::WorldShiftManager_GetRelativePosition(pos, nullptr)));
}
}
class ItemFilter : game::IEntityFilter
{
public:
ItemFilter() : ItemFilter(app::EntityType__Enum_1::None, "")
{}
ItemFilter(app::EntityType__Enum_1 type, const std::string& name) : m_Type(type), m_Name(name)
{
}
bool IsValid(game::Entity* entity) const override
{
return entity->type() == m_Type && entity->name() == m_Name;
}
app::EntityType__Enum_1 m_Type;
std::string m_Name;
};
static bool filtersIsLoaded = false;
static std::map<std::string, ItemFilter> simpleFilters;
static std::vector<ItemFilter> removedItems;
static const std::string filename = "picked_filters.json";
static bool filterItemPickerEnabled = false;
static ItemFilter tempFilter;
static std::string tempName;
static std::string tempSectionName;
static bool addingFilter;
static game::CacheFilterExecutor executor;
void FilterItemPickerLoad()
{
filtersIsLoaded = true;
std::ifstream fs(filename, std::ios::in);
if (!fs.is_open())
return;
nlohmann::json jRoot;
try {
jRoot = nlohmann::json::parse(fs);
}
catch (nlohmann::detail::parse_error& parseError)
{
UNREFERENCED_PARAMETER(parseError);
LOG_ERROR("Failed to parse json");
}
for (auto& [key, value] : jRoot["filters"].items())
simpleFilters[key] = ItemFilter(value["type"], value["name"]);
for (auto& value : jRoot["excluded"])
removedItems.push_back(ItemFilter(value["type"], value["name"]));
}
void FiltetItemPickerSave()
{
std::ofstream fs(filename, std::ios::out);
if (!fs.is_open())
{
LOG_ERROR("Failed to save changes.");
return;
}
nlohmann::json jRoot = {};
jRoot["filters"] = {};
for (auto& [key, value] : simpleFilters)
{
jRoot["filters"][key] = {};
jRoot["filters"][key]["name"] = value.m_Name;
jRoot["filters"][key]["type"] = value.m_Type;
}
jRoot["excluded"] = {};
for (auto& value : removedItems)
{
nlohmann::json item = {};
item["name"] = value.m_Name;
item["type"] = value.m_Type;
jRoot["excluded"].push_back(item);
}
fs << jRoot.dump(4);
fs.close();
}
void DrawFilterItemPicker()
{
ImGui::Checkbox("Enable ## itemPicker", &filterItemPickerEnabled);
if (!filterItemPickerEnabled)
return;
if (!filtersIsLoaded)
FilterItemPickerLoad();
for (auto& [key, filter] : simpleFilters)
{
ImGui::PushID(key.c_str());
ImGui::PushItemWidth(250);
std::string keyText = key;
ImGui::InputText("## Name", &keyText);
ImGui::SameLine();
ImGui::InputText("## ItemName", &filter.m_Name);
ImGui::SameLine();
std::string typeName = std::string(magic_enum::enum_name(filter.m_Type));
ImGui::InputText("## ItemType", &typeName);
ImGui::PopItemWidth();
ImGui::PopID();
}
}
void Debug::DrawExternal()
{
//auto draw = ImGui::GetBackgroundDrawList();
//std::string fpsString = fmt::format("{:.1f}/{:.1f}", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
//draw->AddText(ImVec2(100, 100), ImColor(0, 0, 0), fpsString.c_str());
if (!filterItemPickerEnabled)
return;
auto& manager = game::EntityManager::instance();
game::Entity* selectedEntity = nullptr;
esp::render::PrepareFrame();
for (auto& entity : manager.entities())
{
bool unexplored = true;
for (auto& [_, filter] : simpleFilters)
{
if (executor.ApplyFilter(entity, reinterpret_cast<game::IEntityFilter*>(&filter)))
{
unexplored = false;
break;
}
}
for (auto& filter : removedItems)
{
if (executor.ApplyFilter(entity, reinterpret_cast<game::IEntityFilter*>(&filter)))
{
unexplored = false;
break;
}
}
if (!unexplored)
continue;
bool isSelected = esp::render::DrawEntity(entity->name(), entity, ImColor(255, 0, 0, 255), ImColor(255, 0, 0, 255));
if (isSelected && selectedEntity == nullptr)
{
esp::render::DrawEntity(entity->name(), entity, ImColor(0, 255, 0, 255), ImColor(255, 0, 255, 255));
selectedEntity = entity;
}
}
bool updated = false;
if (!addingFilter)
{
if (ImGui::IsKeyPressed(ImGuiKey_R, false) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
{
if (removedItems.size() > 0)
{
removedItems.pop_back();
updated = true;
}
}
else if (selectedEntity != nullptr && ImGui::IsKeyPressed(ImGuiKey_R, false))
{
removedItems.push_back(ItemFilter(selectedEntity->type(), selectedEntity->name()));
updated = true;
}
if (selectedEntity != nullptr && ImGui::IsKeyPressed(ImGuiKey_T, false))
{
tempFilter = ItemFilter(selectedEntity->type(), selectedEntity->name());
addingFilter = true;
tempName = "";
renderer::SetInputLock(this, true);
}
}
if (addingFilter)
{
ImGui::Begin("Input name", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::PushItemWidth(500);
ImGui::InputText("Section", &tempSectionName);
if (!ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))
ImGui::SetKeyboardFocusHere(0);
ImGui::InputText("Name", &tempName);
ImGui::PopItemWidth();
ImGui::End();
if (ImGui::IsKeyPressed(ImGuiKey_Enter, false))
{
simpleFilters[fmt::format("{}::{}", tempSectionName, tempName)] = tempFilter;
renderer::SetInputLock(this, false);
addingFilter = false;
updated = true;
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape, false))
{
renderer::SetInputLock(this, false);
addingFilter = false;
}
}
if (updated)
FiltetItemPickerSave();
}
void DrawFPSGraph()
{
static float values[120] = { 0 };
static int values_offset = 0;
values[values_offset++] = ImGui::GetIO().Framerate;
if (values_offset >= IM_ARRAYSIZE(values))
values_offset = 0;
ImGui::PlotLines("", values, IM_ARRAYSIZE(values), values_offset, "", 0.0f, 100.0f, ImVec2(0, 80));
ImGui::Text("%.1f FPS", ImGui::GetIO().Framerate);
float avg_fps = 0.0f;
for (int i = 0; i < IM_ARRAYSIZE(values); i++)
avg_fps += values[i];
avg_fps /= IM_ARRAYSIZE(values);
ImGui::Text("%.1f FPS (avg)", avg_fps);
}
void Debug::DrawMain()
{
if (ImGui::CollapsingHeader("Entity Manager", ImGuiTreeNodeFlags_None))
DrawEntitiesData();
if (ImGui::CollapsingHeader("Position", ImGuiTreeNodeFlags_None))
{
DrawMapManager();
DrawPositionInfo();
}
//if (ImGui::CollapsingHeader("Filter item picker"))
// DrawFilterItemPicker();
//if (ImGui::CollapsingHeader("Chest plugin", ImGuiTreeNodeFlags_None))
// DrawChestPlugin();
//if (ImGui::CollapsingHeader("Interaction manager", ImGuiTreeNodeFlags_None))
// DrawInteractionManagerInfo();
if (ImGui::CollapsingHeader("Map Manager", ImGuiTreeNodeFlags_None))
DrawManagerData();
if (ImGui::CollapsingHeader("FPS Graph", ImGuiTreeNodeFlags_None))
DrawFPSGraph();
}
bool Debug::NeedInfoDraw() const
{
return showNotWritten && notWrittenChests.size() > 0;
}
void Debug::DrawInfo()
{
for (auto& name : notWrittenChests)
{
ImGui::Text("%s", name.c_str());
}
}
}