feat: add recoil malfunction and aim fixes
This commit is contained in:
parent
632dc46116
commit
99d599026d
|
|
@ -43,8 +43,19 @@ namespace
|
|||
return value;
|
||||
}
|
||||
|
||||
bool IsFiniteRotation(const Vector2& value)
|
||||
{
|
||||
return std::isfinite(value.x) && std::isfinite(value.y);
|
||||
}
|
||||
|
||||
bool IsUsableAimAddress(const uintptr_t address)
|
||||
{
|
||||
return address > 0x10000ULL && address < 0x0000800000000000ULL;
|
||||
}
|
||||
|
||||
float FrameScale()
|
||||
{
|
||||
|
||||
float deltaTime = ImGui::GetIO().DeltaTime;
|
||||
if (!(deltaTime > 0.f) || deltaTime > 0.2f)
|
||||
deltaTime = 1.f / 60.f;
|
||||
|
|
@ -382,14 +393,28 @@ void CFramework::AimMouse(const Vector2& target, const Vector2& center)
|
|||
// オフセットは g.g_AimMcOffset / g.g_AimRotOffset (运行时可变) を使用
|
||||
void CFramework::AimMemory(const Vector3& target, const Vector3& localPos)
|
||||
{
|
||||
uintptr_t mc = m.Read<uintptr_t>(local.m_address + (uintptr_t)g.g_AimMcOffset);
|
||||
if (!mc)
|
||||
if (!IsUsableAimAddress(local.m_address) ||
|
||||
g.g_AimMcOffset <= 0 || g.g_AimMcOffset > 0x1000 ||
|
||||
g.g_AimRotOffset < 0 || g.g_AimRotOffset > 0x1000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 現在の回転 [rad]
|
||||
Vector2 rot = m.Read<Vector2>(mc + (uintptr_t)g.g_AimRotOffset);
|
||||
uintptr_t mc = 0;
|
||||
if (!m.TryRead(local.m_address + static_cast<uintptr_t>(g.g_AimMcOffset), mc) ||
|
||||
!IsUsableAimAddress(mc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// MovementContext.Rotation_1 is a dump-confirmed Vector2 at +0x27C in this build.
|
||||
Vector2 rot{};
|
||||
if (!m.TryRead(mc + static_cast<uintptr_t>(g.g_AimRotOffset), rot) ||
|
||||
!IsFiniteRotation(rot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 目標までの方向
|
||||
Vector3 delta = target - localPos;
|
||||
float len2d = sqrtf(delta.x * delta.x + delta.z * delta.z);
|
||||
if (len2d < 0.001f)
|
||||
|
|
@ -401,19 +426,27 @@ void CFramework::AimMemory(const Vector3& target, const Vector3& localPos)
|
|||
yaw = ClampAngle(yaw);
|
||||
pitch = ClampAngle(pitch);
|
||||
|
||||
// Distance-adaptive exponential smoothing. It is never 0 or 1, so the
|
||||
// slider cannot produce either a one-frame snap or a permanently lagging aim.
|
||||
float dyaw = ClampAngle(yaw - rot.x);
|
||||
float dpitch = ClampAngle(pitch - rot.y);
|
||||
float angleError = sqrtf(dyaw * dyaw + dpitch * dpitch);
|
||||
if (!(angleError > 0.0001f) || !std::isfinite(angleError))
|
||||
return;
|
||||
|
||||
float t = AimResponse(angleError, g.g_AimSmooth, 0.75f);
|
||||
|
||||
// Cap the absolute angular step. AimResponse alone still gives a large
|
||||
// first-frame move for a far target, which looks like a teleport in memory mode.
|
||||
const float smooth = Clamp01(g.g_AimSmooth * 0.01f);
|
||||
const float maxAngleStep = 0.10f + (1.f - smooth) * 0.12f;
|
||||
if (angleError > maxAngleStep)
|
||||
t = min(t, maxAngleStep / angleError);
|
||||
|
||||
Vector2 newRot{
|
||||
ClampAngle(rot.x + dyaw * t),
|
||||
ClampAngle(rot.y + dpitch * t)
|
||||
};
|
||||
|
||||
m.Write<Vector2>(mc + (uintptr_t)g.g_AimRotOffset, newRot);
|
||||
m.TryWrite<Vector2>(mc + static_cast<uintptr_t>(g.g_AimRotOffset), newRot);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "Framework.h"
|
||||
#include <cmath>
|
||||
|
||||
// 只实现 dump 明确的对象字段;后座/故障链路等类布局确认后再接入,当前不写内存。
|
||||
// 只写入 dump 明确的对象字段;后座与故障也只使用本轮已确认的链路,弹道追踪暂不写。
|
||||
|
||||
void CFramework::GetESPInfo(const int& SpawnType, std::string& vOutStr, ImColor& vOutColor)
|
||||
{
|
||||
|
|
@ -227,10 +227,162 @@ void CFramework::UpdateFeatures()
|
|||
UpdateSwayOverride();
|
||||
else
|
||||
ResetSwayOverride();
|
||||
|
||||
if (g.g_NoRecoil)
|
||||
UpdateNoRecoilOverride();
|
||||
else
|
||||
ResetNoRecoilOverride();
|
||||
|
||||
if (g.g_NoMalfunction)
|
||||
UpdateNoMalfunctionOverride();
|
||||
else
|
||||
ResetNoMalfunctionOverride();
|
||||
}
|
||||
|
||||
bool CFramework::ResolveCurrentWeapon(uintptr_t& weaponAnimation, uintptr_t& shotEffector,
|
||||
uintptr_t& weapon, uintptr_t& weaponTemplate)
|
||||
{
|
||||
weaponAnimation = 0;
|
||||
shotEffector = 0;
|
||||
weapon = 0;
|
||||
weaponTemplate = 0;
|
||||
|
||||
if (!IsUsableAddress(local.m_address) ||
|
||||
!m.TryRead(local.m_address + offset::Player::pWeaponAnimation, weaponAnimation) ||
|
||||
!IsUsableAddress(weaponAnimation) ||
|
||||
!m.TryRead(weaponAnimation + offset::WeaponAnimations::ShotEffector, shotEffector) ||
|
||||
!IsUsableAddress(shotEffector) ||
|
||||
!m.TryRead(shotEffector + offset::ShotEffector::MainWeaponInHands, weapon) ||
|
||||
!IsUsableAddress(weapon) ||
|
||||
!m.TryRead(weapon + offset::Weapon::Template, weaponTemplate) ||
|
||||
!IsUsableAddress(weaponTemplate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFramework::UpdateNoRecoilOverride()
|
||||
{
|
||||
uintptr_t weaponAnimation = 0;
|
||||
uintptr_t shotEffector = 0;
|
||||
uintptr_t weapon = 0;
|
||||
uintptr_t weaponTemplate = 0;
|
||||
uintptr_t recoilSettings = 0;
|
||||
|
||||
if (!ResolveCurrentWeapon(weaponAnimation, shotEffector, weapon, weaponTemplate) ||
|
||||
!m.TryRead(weaponTemplate + offset::WeaponTemplate::WeaponRecoilSettings, recoilSettings) ||
|
||||
!IsUsableAddress(recoilSettings))
|
||||
{
|
||||
ResetNoRecoilOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_noRecoilOverride.captured &&
|
||||
(m_noRecoilOverride.player != local.m_address ||
|
||||
m_noRecoilOverride.weaponAnimation != weaponAnimation ||
|
||||
m_noRecoilOverride.shotEffector != shotEffector ||
|
||||
m_noRecoilOverride.weapon != weapon ||
|
||||
m_noRecoilOverride.weaponTemplate != weaponTemplate ||
|
||||
m_noRecoilOverride.recoilSettings != recoilSettings))
|
||||
{
|
||||
ResetNoRecoilOverride();
|
||||
}
|
||||
|
||||
if (!m_noRecoilOverride.captured)
|
||||
{
|
||||
bool enable = false;
|
||||
if (!m.TryRead(recoilSettings + offset::WeaponRecoil::Enable, enable))
|
||||
{
|
||||
ResetNoRecoilOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
m_noRecoilOverride.player = local.m_address;
|
||||
m_noRecoilOverride.weaponAnimation = weaponAnimation;
|
||||
m_noRecoilOverride.shotEffector = shotEffector;
|
||||
m_noRecoilOverride.weapon = weapon;
|
||||
m_noRecoilOverride.weaponTemplate = weaponTemplate;
|
||||
m_noRecoilOverride.recoilSettings = recoilSettings;
|
||||
m_noRecoilOverride.enable = enable;
|
||||
m_noRecoilOverride.captured = true;
|
||||
}
|
||||
|
||||
// WeaponRecoil.Enable is a dump-confirmed instance field. Do not touch its Values array yet.
|
||||
m.TryWrite<bool>(recoilSettings + offset::WeaponRecoil::Enable, false);
|
||||
}
|
||||
|
||||
void CFramework::UpdateNoMalfunctionOverride()
|
||||
{
|
||||
uintptr_t weaponAnimation = 0;
|
||||
uintptr_t shotEffector = 0;
|
||||
uintptr_t weapon = 0;
|
||||
uintptr_t weaponTemplate = 0;
|
||||
uintptr_t malfunctionState = 0;
|
||||
|
||||
if (!ResolveCurrentWeapon(weaponAnimation, shotEffector, weapon, weaponTemplate) ||
|
||||
!m.TryRead(weapon + offset::Weapon::MalfState, malfunctionState) ||
|
||||
!IsUsableAddress(malfunctionState))
|
||||
{
|
||||
ResetNoMalfunctionOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_noMalfunctionOverride.captured &&
|
||||
(m_noMalfunctionOverride.player != local.m_address ||
|
||||
m_noMalfunctionOverride.weapon != weapon ||
|
||||
m_noMalfunctionOverride.weaponTemplate != weaponTemplate ||
|
||||
m_noMalfunctionOverride.malfunctionState != malfunctionState))
|
||||
{
|
||||
ResetNoMalfunctionOverride();
|
||||
}
|
||||
|
||||
if (!m_noMalfunctionOverride.captured)
|
||||
{
|
||||
bool allowJam = false;
|
||||
bool allowFeed = false;
|
||||
bool allowMisfire = false;
|
||||
bool allowSlide = false;
|
||||
float baseChance = 0.f;
|
||||
int state = 0;
|
||||
|
||||
if (!m.TryRead(weaponTemplate + offset::WeaponTemplate::AllowJam, allowJam) ||
|
||||
!m.TryRead(weaponTemplate + offset::WeaponTemplate::AllowFeed, allowFeed) ||
|
||||
!m.TryRead(weaponTemplate + offset::WeaponTemplate::AllowMisfire, allowMisfire) ||
|
||||
!m.TryRead(weaponTemplate + offset::WeaponTemplate::AllowSlide, allowSlide) ||
|
||||
!m.TryRead(weaponTemplate + offset::WeaponTemplate::BaseMalfunctionChance, baseChance) ||
|
||||
!m.TryRead(malfunctionState + offset::WeaponMalfunctionState::State, state) ||
|
||||
!std::isfinite(baseChance))
|
||||
{
|
||||
ResetNoMalfunctionOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
m_noMalfunctionOverride.player = local.m_address;
|
||||
m_noMalfunctionOverride.weapon = weapon;
|
||||
m_noMalfunctionOverride.weaponTemplate = weaponTemplate;
|
||||
m_noMalfunctionOverride.malfunctionState = malfunctionState;
|
||||
m_noMalfunctionOverride.allowJam = allowJam;
|
||||
m_noMalfunctionOverride.allowFeed = allowFeed;
|
||||
m_noMalfunctionOverride.allowMisfire = allowMisfire;
|
||||
m_noMalfunctionOverride.allowSlide = allowSlide;
|
||||
m_noMalfunctionOverride.baseChance = baseChance;
|
||||
m_noMalfunctionOverride.state = state;
|
||||
m_noMalfunctionOverride.captured = true;
|
||||
}
|
||||
|
||||
m.TryWrite<bool>(weaponTemplate + offset::WeaponTemplate::AllowJam, false);
|
||||
m.TryWrite<bool>(weaponTemplate + offset::WeaponTemplate::AllowFeed, false);
|
||||
m.TryWrite<bool>(weaponTemplate + offset::WeaponTemplate::AllowMisfire, false);
|
||||
m.TryWrite<bool>(weaponTemplate + offset::WeaponTemplate::AllowSlide, false);
|
||||
m.TryWrite<float>(weaponTemplate + offset::WeaponTemplate::BaseMalfunctionChance, 0.f);
|
||||
m.TryWrite<int>(malfunctionState + offset::WeaponMalfunctionState::State, 0);
|
||||
}
|
||||
|
||||
void CFramework::UpdateStaminaOverride()
|
||||
{
|
||||
|
||||
uintptr_t physical = 0;
|
||||
uintptr_t stamina = 0;
|
||||
uintptr_t handsStamina = 0;
|
||||
|
|
@ -370,8 +522,48 @@ void CFramework::UpdateSwayOverride()
|
|||
m.TryWrite<Vector3>(motionEffector + offset::MotionEffector::SwayFactors, Vector3{});
|
||||
}
|
||||
|
||||
void CFramework::ResetNoRecoilOverride()
|
||||
{
|
||||
if (m_noRecoilOverride.captured && IsUsableAddress(m_noRecoilOverride.recoilSettings))
|
||||
{
|
||||
m.TryWrite<bool>(m_noRecoilOverride.recoilSettings + offset::WeaponRecoil::Enable,
|
||||
m_noRecoilOverride.enable);
|
||||
}
|
||||
|
||||
m_noRecoilOverride = {};
|
||||
}
|
||||
|
||||
void CFramework::ResetNoMalfunctionOverride()
|
||||
{
|
||||
if (m_noMalfunctionOverride.captured)
|
||||
{
|
||||
if (IsUsableAddress(m_noMalfunctionOverride.weaponTemplate))
|
||||
{
|
||||
m.TryWrite<bool>(m_noMalfunctionOverride.weaponTemplate + offset::WeaponTemplate::AllowJam,
|
||||
m_noMalfunctionOverride.allowJam);
|
||||
m.TryWrite<bool>(m_noMalfunctionOverride.weaponTemplate + offset::WeaponTemplate::AllowFeed,
|
||||
m_noMalfunctionOverride.allowFeed);
|
||||
m.TryWrite<bool>(m_noMalfunctionOverride.weaponTemplate + offset::WeaponTemplate::AllowMisfire,
|
||||
m_noMalfunctionOverride.allowMisfire);
|
||||
m.TryWrite<bool>(m_noMalfunctionOverride.weaponTemplate + offset::WeaponTemplate::AllowSlide,
|
||||
m_noMalfunctionOverride.allowSlide);
|
||||
m.TryWrite<float>(m_noMalfunctionOverride.weaponTemplate + offset::WeaponTemplate::BaseMalfunctionChance,
|
||||
m_noMalfunctionOverride.baseChance);
|
||||
}
|
||||
|
||||
if (IsUsableAddress(m_noMalfunctionOverride.malfunctionState))
|
||||
{
|
||||
m.TryWrite<int>(m_noMalfunctionOverride.malfunctionState + offset::WeaponMalfunctionState::State,
|
||||
m_noMalfunctionOverride.state);
|
||||
}
|
||||
}
|
||||
|
||||
m_noMalfunctionOverride = {};
|
||||
}
|
||||
|
||||
void CFramework::ResetStaminaOverride()
|
||||
{
|
||||
|
||||
if (m_staminaOverride.captured)
|
||||
{
|
||||
if (IsUsableAddress(m_staminaOverride.stamina))
|
||||
|
|
@ -404,4 +596,6 @@ void CFramework::ResetFeatureOverrides()
|
|||
ResetStaminaOverride();
|
||||
ResetFallDamageOverride();
|
||||
ResetSwayOverride();
|
||||
ResetNoRecoilOverride();
|
||||
ResetNoMalfunctionOverride();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,40 @@ private:
|
|||
bool captured{ false };
|
||||
};
|
||||
|
||||
struct NoRecoilOverrideState
|
||||
{
|
||||
uintptr_t player{ 0 };
|
||||
uintptr_t weaponAnimation{ 0 };
|
||||
uintptr_t shotEffector{ 0 };
|
||||
uintptr_t weapon{ 0 };
|
||||
uintptr_t weaponTemplate{ 0 };
|
||||
uintptr_t recoilSettings{ 0 };
|
||||
bool enable{ false };
|
||||
bool captured{ false };
|
||||
};
|
||||
|
||||
struct NoMalfunctionOverrideState
|
||||
{
|
||||
uintptr_t player{ 0 };
|
||||
uintptr_t weapon{ 0 };
|
||||
uintptr_t weaponTemplate{ 0 };
|
||||
uintptr_t malfunctionState{ 0 };
|
||||
bool allowJam{ false };
|
||||
bool allowFeed{ false };
|
||||
bool allowMisfire{ false };
|
||||
bool allowSlide{ false };
|
||||
float baseChance{ 0.f };
|
||||
int state{ 0 };
|
||||
bool captured{ false };
|
||||
};
|
||||
|
||||
bool ResolveCurrentWeapon(uintptr_t& weaponAnimation, uintptr_t& shotEffector,
|
||||
uintptr_t& weapon, uintptr_t& weaponTemplate);
|
||||
void UpdateNoRecoilOverride();
|
||||
void UpdateNoMalfunctionOverride();
|
||||
void ResetNoRecoilOverride();
|
||||
void ResetNoMalfunctionOverride();
|
||||
|
||||
void UpdateStaminaOverride();
|
||||
void UpdateFallDamageOverride();
|
||||
void UpdateSwayOverride();
|
||||
|
|
@ -66,6 +100,8 @@ private:
|
|||
StaminaOverrideState m_staminaOverride{};
|
||||
FallDamageOverrideState m_fallDamageOverride{};
|
||||
SwayOverrideState m_swayOverride{};
|
||||
NoRecoilOverrideState m_noRecoilOverride{};
|
||||
NoMalfunctionOverrideState m_noMalfunctionOverride{};
|
||||
|
||||
|
||||
std::mutex m_mtxEntityList;
|
||||
|
|
|
|||
|
|
@ -123,11 +123,13 @@ void CFramework::RenderMenu()
|
|||
ImGui::Checkbox("无限耐力", &g.g_InfStamina);
|
||||
ImGui::Checkbox("无跌落伤害", &g.g_NoFallDamage);
|
||||
ImGui::Checkbox("无摇摆", &g.g_NoSway);
|
||||
ImGui::Checkbox("无后座(内存)", &g.g_NoRecoil);
|
||||
ImGui::Checkbox("无故障/卡弹", &g.g_NoMalfunction);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::TextDisabled("仅写入 dump 已确认的对象字段");
|
||||
ImGui::TextDisabled("后座 / 故障:等待下一轮类布局确认");
|
||||
ImGui::TextDisabled("只写入 dump 已确认的对象字段");
|
||||
ImGui::TextDisabled("弹道追踪:暂不写入,等待 runtime 验证");
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::BeginChild("##C021", ImVec2(ImGui::GetContentRegionAvail()), true);
|
||||
|
|
|
|||
|
|
@ -131,6 +131,20 @@ void CPlayer::UpdateHealth()
|
|||
|
||||
void CPlayer::UpdateBone()
|
||||
{
|
||||
// Refresh the direct PlayerBody -> PlayerBones link before reading the selected aim bone.
|
||||
// The list thread is intentionally slow, while animation/weapon objects can be replaced sooner.
|
||||
if (m_address)
|
||||
{
|
||||
uintptr_t playerBody = 0;
|
||||
uintptr_t playerBones = 0;
|
||||
if (m.TryRead(m_address + offset::Player::pPlayerBody, playerBody) && playerBody &&
|
||||
m.TryRead(playerBody + offset::PlayerBody::PlayerBones, playerBones) && playerBones)
|
||||
{
|
||||
m_pPlayerBody = playerBody;
|
||||
m_pPlayerBones = playerBones;
|
||||
}
|
||||
}
|
||||
|
||||
m_vecHeadOrigin = GetBonePosition(BONE_HEAD);
|
||||
m_vecNeckOrigin = GetBonePosition(BONE_NECK);
|
||||
m_vecBoneList.clear();
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ namespace offset
|
|||
{
|
||||
constexpr auto Breath = 0x30;
|
||||
constexpr auto MotionReact = 0x40; // MotionEffector
|
||||
constexpr auto ShotEffector = 0x50; // ShotEffector / Shootingg
|
||||
constexpr auto isAiming = 0x1C5;
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +113,41 @@ namespace offset
|
|||
constexpr auto SwayFactors = 0x94; // UnityEngine.Vector3
|
||||
}
|
||||
|
||||
// -.ShotEffector
|
||||
namespace ShotEffector
|
||||
{
|
||||
constexpr auto MainWeaponInHands = 0x38; // EFT.InventoryLogic.Weapon
|
||||
}
|
||||
|
||||
// EFT.InventoryLogic.Weapon
|
||||
namespace Weapon
|
||||
{
|
||||
constexpr auto Template = 0x40; // CompoundItem.Template
|
||||
constexpr auto MalfState = 0xF0; // WeaponMalfunctionStateClass
|
||||
}
|
||||
|
||||
// EFT.InventoryLogic.WeaponTemplate
|
||||
namespace WeaponTemplate
|
||||
{
|
||||
constexpr auto WeaponRecoilSettings = 0x180; // WeaponRecoil
|
||||
constexpr auto AllowJam = 0x2F0;
|
||||
constexpr auto AllowFeed = 0x2F1;
|
||||
constexpr auto AllowMisfire = 0x2F2;
|
||||
constexpr auto AllowSlide = 0x2F3;
|
||||
constexpr auto BaseMalfunctionChance = 0x2F4;
|
||||
}
|
||||
|
||||
// -.WeaponRecoil / -.Weapon.WeaponMalfunctionStateClass
|
||||
namespace WeaponRecoil
|
||||
{
|
||||
constexpr auto Enable = 0x18;
|
||||
}
|
||||
|
||||
namespace WeaponMalfunctionState
|
||||
{
|
||||
constexpr auto State = 0x6C;
|
||||
}
|
||||
|
||||
// -.GClass774 (stamina/oxygen state)
|
||||
namespace PhysicalState
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@ struct Globals
|
|||
float g_ESP_MaxItemDistance = 50.f;
|
||||
|
||||
// Misc (仅使用 dump 已确认的对象字段)
|
||||
bool g_InfStamina = false;
|
||||
bool g_NoFallDamage = false;
|
||||
bool g_NoSway = false;
|
||||
bool g_InfStamina = false;
|
||||
bool g_NoFallDamage = false;
|
||||
bool g_NoSway = false;
|
||||
bool g_NoRecoil = false;
|
||||
bool g_NoMalfunction = false;
|
||||
|
||||
|
||||
// Aim (自瞄)
|
||||
|
|
|
|||
Loading…
Reference in New Issue