mirror of
https://github.com/A-Archives-and-Forks/Akebi-GC.git
synced 2026-10-11 10:08:00 +08:00
305 lines
12 KiB
C++
305 lines
12 KiB
C++
#include "pch-il2cpp.h"
|
|
#include "MapTeleport.h"
|
|
|
|
#include <helpers.h>
|
|
#include <cheat/events.h>
|
|
#include <cheat/game/EntityManager.h>
|
|
#include <cheat/game/util.h>
|
|
|
|
namespace cheat::feature
|
|
{
|
|
static void InLevelMapPageContext_OnMapClicked_Hook(app::InLevelMapPageContext* __this, app::Vector2 screenPos, MethodInfo* method);
|
|
static void InLevelMapPageContext_OnMarkClicked_Hook(app::InLevelMapPageContext* __this, app::MonoMapMark* mark, MethodInfo* method);
|
|
static app::Vector3 LocalEntityInfoData_GetTargetPos_Hook(app::LocalEntityInfoData* __this, MethodInfo* method);
|
|
|
|
static bool LoadingManager_NeedTransByServer_Hook(app::LoadingManager* __this, uint32_t sceneId, app::Vector3 position, MethodInfo* method);
|
|
static void LoadingManager_PerformPlayerTransmit_Hook(app::LoadingManager* __this, app::Vector3 position, app::EnterType__Enum someEnum,
|
|
uint32_t someUint1, app::EvtTransmitAvatar_EvtTransmitAvatar_TransmitType__Enum teleportType, uint32_t someUint2, MethodInfo* method);
|
|
static void Entity_SetPosition_Hook(app::BaseEntity* __this, app::Vector3 position, bool someBool, MethodInfo* method);
|
|
|
|
MapTeleport::MapTeleport() : Feature(),
|
|
NF(f_Enabled, "Map teleport", "MapTeleport", false),
|
|
NF(f_DetectHeight, "Auto height detect", "MapTeleport", true),
|
|
NF(f_DefaultHeight, "Default teleport height", "MapTeleport", 300.0f),
|
|
NF(f_Key, "Teleport key", "MapTeleport", Hotkey('T'))
|
|
{
|
|
// Map touch
|
|
HookManager::install(app::InLevelMapPageContext_OnMarkClicked, InLevelMapPageContext_OnMarkClicked_Hook);
|
|
HookManager::install(app::InLevelMapPageContext_OnMapClicked, InLevelMapPageContext_OnMapClicked_Hook);
|
|
|
|
// Stage 1
|
|
HookManager::install(app::LocalEntityInfoData_GetTargetPos, LocalEntityInfoData_GetTargetPos_Hook);
|
|
HookManager::install(app::LoadingManager_NeedTransByServer, LoadingManager_NeedTransByServer_Hook);
|
|
|
|
// Stage 2
|
|
HookManager::install(app::LoadingManager_PerformPlayerTransmit, LoadingManager_PerformPlayerTransmit_Hook);
|
|
|
|
// Stage 3
|
|
HookManager::install(app::Entity_SetPosition, Entity_SetPosition_Hook);
|
|
|
|
events::GameUpdateEvent += MY_METHOD_HANDLER(MapTeleport::OnGameUpdate);
|
|
}
|
|
|
|
const FeatureGUIInfo& MapTeleport::GetGUIInfo() const
|
|
{
|
|
static const FeatureGUIInfo info { "Map Teleport", "Teleport", true };
|
|
return info;
|
|
}
|
|
|
|
void MapTeleport::DrawMain()
|
|
{
|
|
ConfigWidget("Enabled",
|
|
f_Enabled,
|
|
"Enable teleport-to-mark functionality.\n" \
|
|
"Usage: \n" \
|
|
"\t1. Open map.\n" \
|
|
"\t2. Hold [Teleport Key] and click with the LMB anywhere in the map.\n" \
|
|
"\tDone. You have been teleported to selected location.\n" \
|
|
"Teleport might glitch if teleporting to an extremely high location. \n" \
|
|
"Adjust Override Height accordingly to help avoid."
|
|
);
|
|
|
|
if (!f_Enabled)
|
|
ImGui::BeginDisabled();
|
|
|
|
ConfigWidget("Override Height (m)", f_DefaultHeight, 1.0F, 200.0F, 800.0F,
|
|
"If teleport cannot get ground height of target location,\nit will teleport you to the height specified here.\n" \
|
|
"It is recommended to have this value to be at least as high as a mountain.\nOtherwise, you may fall through the ground.");
|
|
|
|
ConfigWidget("Teleport Key", f_Key, true,
|
|
"Key to hold down before clicking on target location.");
|
|
|
|
if (!f_Enabled)
|
|
ImGui::EndDisabled();
|
|
}
|
|
|
|
MapTeleport& MapTeleport::GetInstance()
|
|
{
|
|
static MapTeleport instance;
|
|
return instance;
|
|
}
|
|
|
|
// Hook for game manager needs for starting teleport in game update thread.
|
|
// Because, when we use Teleport call in non game thread (imgui update thread for example)
|
|
// the game just skip this call, and only with second call you start teleporting,
|
|
// but to prev selected location.
|
|
void MapTeleport::OnGameUpdate()
|
|
{
|
|
if (taskInfo.waitingThread)
|
|
{
|
|
taskInfo.waitingThread = false;
|
|
auto someSingleton = GET_SINGLETON(LoadingManager);
|
|
app::LoadingManager_RequestSceneTransToPoint(someSingleton, taskInfo.sceneId, taskInfo.waypointId, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
// Finding nearest waypoint to position, and request teleport to it.
|
|
// After, in teleport events, change waypoint position to target position.
|
|
void MapTeleport::TeleportTo(app::Vector3 position, bool needHeightCalc, uint32_t sceneId)
|
|
{
|
|
LOG_DEBUG("Stage 0. Target location at %s", il2cppi_to_string(position).c_str());
|
|
|
|
auto avatarPosition = app::ActorUtils_GetAvatarPos(nullptr, nullptr);
|
|
auto nearestWaypoint = game::FindNearestWaypoint(position, sceneId);
|
|
|
|
if (nearestWaypoint.data == nullptr)
|
|
{
|
|
LOG_ERROR("Stage 0. Failed to find the nearest unlocked waypoint. Maybe you haven't unlocked anyone or the scene has no waypoints.");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
float dist = app::Vector3_Distance(nullptr, position, nearestWaypoint.position, nullptr);
|
|
LOG_DEBUG("Stage 0. Found nearest waypoint { sceneId: %d; waypointId: %d } with distance %fm.",
|
|
nearestWaypoint.sceneId, nearestWaypoint.waypointId, dist);
|
|
}
|
|
taskInfo = { true, needHeightCalc, 3, position, nearestWaypoint.sceneId, nearestWaypoint.waypointId };
|
|
}
|
|
|
|
static bool ScreenToMapPosition(app::InLevelMapPageContext* context, app::Vector2 screenPos, app::Vector2* outMapPos)
|
|
{
|
|
auto mapBackground = app::MonoInLevelMapPage_get_mapBackground(context->fields._pageMono, nullptr);
|
|
if (!mapBackground)
|
|
return false;
|
|
|
|
auto uimanager = GET_SINGLETON(UIManager_1);
|
|
if (uimanager == nullptr)
|
|
return false;
|
|
|
|
auto screenCamera = uimanager->fields._uiCamera;
|
|
if (screenCamera == nullptr)
|
|
return false;
|
|
|
|
bool result = app::RectTransformUtility_ScreenPointToLocalPointInRectangle(nullptr, mapBackground, screenPos, screenCamera, outMapPos, nullptr);
|
|
if (!result)
|
|
return false;
|
|
|
|
auto mapRect = app::MonoInLevelMapPage_get_mapRect(context->fields._pageMono, nullptr);
|
|
auto mapViewRect = context->fields._mapViewRect;
|
|
|
|
// Map rect pos to map view rect pos
|
|
outMapPos->x = (outMapPos->x - mapRect.m_XMin) / mapRect.m_Width;
|
|
outMapPos->x = (outMapPos->x * mapViewRect.m_Width) + mapViewRect.m_XMin;
|
|
|
|
outMapPos->y = (outMapPos->y - mapRect.m_YMin) / mapRect.m_Height;
|
|
outMapPos->y = (outMapPos->y * mapViewRect.m_Height) + mapViewRect.m_YMin;
|
|
|
|
return true;
|
|
}
|
|
|
|
void MapTeleport::TeleportTo(app::Vector2 mapPosition)
|
|
{
|
|
auto worldPosition = app::Miscs_GenWorldPos(nullptr, mapPosition, nullptr);
|
|
|
|
auto relativePos = app::WorldShiftManager_GetRelativePosition(nullptr, worldPosition, nullptr);
|
|
auto groundHeight = app::Miscs_CalcCurrentGroundHeight(nullptr, relativePos.x, relativePos.z, nullptr);
|
|
|
|
TeleportTo({ worldPosition.x, groundHeight > 0 ? groundHeight + 5 : f_DefaultHeight, worldPosition.z }, true, game::GetCurrentMapSceneID());
|
|
}
|
|
|
|
// Calling teleport if map clicked.
|
|
// This event invokes only when free space of map clicked,
|
|
// if clicked mark, invokes InLevelMapPageContext_OnMarkClicked_Hook.
|
|
static void InLevelMapPageContext_OnMapClicked_Hook(app::InLevelMapPageContext* __this, app::Vector2 screenPos, MethodInfo* method)
|
|
{
|
|
MapTeleport& mapTeleport = MapTeleport::GetInstance();
|
|
|
|
if (!mapTeleport.f_Enabled || !mapTeleport.f_Key.value().IsPressed())
|
|
return CALL_ORIGIN(InLevelMapPageContext_OnMapClicked_Hook, __this, screenPos, method);
|
|
|
|
app::Vector2 mapPosition{};
|
|
bool mapPosResult = ScreenToMapPosition(__this, screenPos, &mapPosition);
|
|
if (!mapPosResult)
|
|
return;
|
|
|
|
mapTeleport.TeleportTo(mapPosition);
|
|
}
|
|
|
|
// Calling teleport if map marks clicked.
|
|
static void InLevelMapPageContext_OnMarkClicked_Hook(app::InLevelMapPageContext* __this, app::MonoMapMark* mark, MethodInfo* method)
|
|
{
|
|
MapTeleport& mapTeleport = MapTeleport::GetInstance();
|
|
if (!mapTeleport.f_Enabled || !mapTeleport.f_Key.value().IsPressed())
|
|
return CALL_ORIGIN(InLevelMapPageContext_OnMarkClicked_Hook, __this, mark, method);
|
|
|
|
mapTeleport.TeleportTo(mark->fields._levelMapPos);
|
|
}
|
|
|
|
// Before call, game checked if distance is near (<60) to cast near teleport.
|
|
// But it check distance to waypoint location, given by this function.
|
|
// So, we need to replace target position to do correct check.
|
|
void MapTeleport::OnGetTargetPos(app::Vector3& position)
|
|
{
|
|
if (taskInfo.currentStage == 3)
|
|
{
|
|
position = taskInfo.targetPosition;
|
|
taskInfo.currentStage--;
|
|
LOG_DEBUG("Stage 1. Replace waypoint tp position.");
|
|
}
|
|
}
|
|
|
|
// Checking is teleport is far (>60m), if it isn't we clear stage.
|
|
void MapTeleport::OnCheckTeleportDistance(bool needTransByServer)
|
|
{
|
|
if (!needTransByServer && taskInfo.currentStage == 2)
|
|
{
|
|
LOG_DEBUG("Stage 1. Distance is less than 60m. Performing fast tp.");
|
|
taskInfo.currentStage = 0;
|
|
}
|
|
}
|
|
|
|
// After server responded, it will give us the waypoint target location to load.
|
|
// Change it to teleport location.
|
|
void MapTeleport::OnPerformPlayerTransmit(app::Vector3& position)
|
|
{
|
|
if (taskInfo.currentStage == 2)
|
|
{
|
|
LOG_DEBUG("Stage 2. Changing loading location.");
|
|
position = taskInfo.targetPosition;
|
|
taskInfo.currentStage--;
|
|
}
|
|
}
|
|
|
|
// Last event in teleportation is avatar teleport, we just change avatar position from
|
|
// waypoint location to teleport location. And also recalculate ground position if it needed.
|
|
void MapTeleport::OnSetAvatarPosition(app::Vector3& position)
|
|
{
|
|
if (taskInfo.currentStage == 1)
|
|
{
|
|
app::Vector3 originPosition = position;
|
|
position = taskInfo.targetPosition;
|
|
LOG_DEBUG("Stage 3. Changing avatar entity position.");
|
|
|
|
if (taskInfo.needHeightCalculation)
|
|
{
|
|
auto relativePos = app::WorldShiftManager_GetRelativePosition(nullptr, position, nullptr);
|
|
float groundHeight;
|
|
switch (taskInfo.sceneId)
|
|
{
|
|
// Underground mines has tunnel structure, so we need to calculate height from waypoint height to prevent tp above world.
|
|
case 6: // Underground mines scene id, if it was changed, please create issue
|
|
groundHeight = app::Miscs_CalcCurrentGroundHeight_1(nullptr, relativePos.x, relativePos.z, originPosition.y, 100,
|
|
app::Miscs_GetSceneGroundLayerMask(nullptr, nullptr), nullptr);
|
|
break;
|
|
default:
|
|
groundHeight = app::Miscs_CalcCurrentGroundWaterHeight(nullptr, relativePos.x, relativePos.z, nullptr);
|
|
break;
|
|
}
|
|
if (groundHeight > 0 && position.y != groundHeight)
|
|
{
|
|
position.y = groundHeight + 5;
|
|
LOG_DEBUG("Stage 3. Changing height to %f", position.y);
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG("Finish. Teleport to mark finished.");
|
|
taskInfo.currentStage--;
|
|
}
|
|
}
|
|
|
|
static app::Vector3 LocalEntityInfoData_GetTargetPos_Hook(app::LocalEntityInfoData* __this, MethodInfo* method)
|
|
{
|
|
auto result = CALL_ORIGIN(LocalEntityInfoData_GetTargetPos_Hook, __this, method);
|
|
|
|
MapTeleport& mapTeleport = MapTeleport::GetInstance();
|
|
mapTeleport.OnGetTargetPos(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool LoadingManager_NeedTransByServer_Hook(app::LoadingManager* __this, uint32_t sceneId, app::Vector3 position, MethodInfo* method)
|
|
{
|
|
auto result = CALL_ORIGIN(LoadingManager_NeedTransByServer_Hook, __this, sceneId, position, method);
|
|
|
|
MapTeleport& mapTeleport = MapTeleport::GetInstance();
|
|
mapTeleport.OnCheckTeleportDistance(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static void LoadingManager_PerformPlayerTransmit_Hook(app::LoadingManager* __this, app::Vector3 position, app::EnterType__Enum someEnum,
|
|
uint32_t someUint1, app::EvtTransmitAvatar_EvtTransmitAvatar_TransmitType__Enum teleportType, uint32_t someUint2, MethodInfo* method)
|
|
{
|
|
MapTeleport& mapTeleport = MapTeleport::GetInstance();
|
|
mapTeleport.OnPerformPlayerTransmit(position);
|
|
|
|
CALL_ORIGIN(LoadingManager_PerformPlayerTransmit_Hook, __this, position, someEnum, someUint1, teleportType, someUint2, method);
|
|
}
|
|
|
|
|
|
static void Entity_SetPosition_Hook(app::BaseEntity* __this, app::Vector3 position, bool someBool, MethodInfo* method)
|
|
{
|
|
auto& manager = game::EntityManager::instance();
|
|
if (manager.avatar()->raw() == __this)
|
|
{
|
|
MapTeleport& mapTeleport = MapTeleport::GetInstance();
|
|
mapTeleport.OnSetAvatarPosition(position);
|
|
}
|
|
|
|
CALL_ORIGIN(Entity_SetPosition_Hook, __this, position, someBool, method);
|
|
}
|
|
|
|
}
|
|
|