Add gamepad slowdown and lockon aimassists

This commit is contained in:
Jan 2021-08-25 19:59:55 +02:00
parent ae8dd7bb33
commit 7ce7ba883b
7 changed files with 265 additions and 3 deletions

View File

@ -79,6 +79,11 @@ newoption {
description = "Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches." description = "Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches."
} }
newoption {
trigger = "aimassist-enable",
description = "Enables code for controller aim assist."
}
newaction { newaction {
trigger = "version", trigger = "version",
description = "Returns the version string for the current commit of the source code.", description = "Returns the version string for the current commit of the source code.",
@ -332,6 +337,9 @@ workspace "iw4x"
if _OPTIONS["iw4x-zones"] then if _OPTIONS["iw4x-zones"] then
defines { "GENERATE_IW4X_SPECIFIC_ZONES" } defines { "GENERATE_IW4X_SPECIFIC_ZONES" }
end end
if _OPTIONS["aimassist-enable"] then
defines { "AIM_ASSIST_ENABLED" }
end
-- Pre-compiled header -- Pre-compiled header
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives

View File

@ -1,6 +1,7 @@
#include "STDInclude.hpp" #include "STDInclude.hpp"
#include <limits> #include <limits>
#include <cmath>
namespace Game namespace Game
{ {
@ -180,6 +181,17 @@ namespace Components
Dvar::Var Gamepad::aim_scale_view_axis; Dvar::Var Gamepad::aim_scale_view_axis;
Dvar::Var Gamepad::cl_bypassMouseInput; Dvar::Var Gamepad::cl_bypassMouseInput;
Dvar::Var Gamepad::cg_mapLocationSelectionCursorSpeed; Dvar::Var Gamepad::cg_mapLocationSelectionCursorSpeed;
Dvar::Var Gamepad::aim_aimAssistRangeScale;
Dvar::Var Gamepad::aim_slowdown_enabled;
Dvar::Var Gamepad::aim_slowdown_debug;
Dvar::Var Gamepad::aim_slowdown_pitch_scale;
Dvar::Var Gamepad::aim_slowdown_pitch_scale_ads;
Dvar::Var Gamepad::aim_slowdown_yaw_scale;
Dvar::Var Gamepad::aim_slowdown_yaw_scale_ads;
Dvar::Var Gamepad::aim_lockon_enabled;
Dvar::Var Gamepad::aim_lockon_deflection;
Dvar::Var Gamepad::aim_lockon_pitch_strength;
Dvar::Var Gamepad::aim_lockon_strength;
Dvar::Var Gamepad::xpadSensitivity; Dvar::Var Gamepad::xpadSensitivity;
Dvar::Var Gamepad::xpadEarlyTime; Dvar::Var Gamepad::xpadEarlyTime;
@ -348,6 +360,116 @@ namespace Components
return target; return target;
} }
bool Gamepad::AimAssist_DoBoundsIntersectCenterBox(const float* clipMins, const float* clipMaxs, const float clipHalfWidth, const float clipHalfHeight)
{
return clipHalfWidth >= clipMins[0] && clipMaxs[0] >= -clipHalfWidth
&& clipHalfHeight >= clipMins[1] && clipMaxs[1] >= -clipHalfHeight;
}
bool Gamepad::AimAssist_IsPlayerUsingOffhand(Game::AimAssistPlayerState* ps)
{
if ((ps->weapFlags & 2) == 0)
return false;
if (!ps->weapIndex)
return false;
const auto* weaponDef = Game::BG_GetWeaponDef(ps->weapIndex);
return weaponDef->offhandClass != Game::OFFHAND_CLASS_NONE;
}
const Game::AimScreenTarget* Gamepad::AimAssist_GetBestTarget(const Game::AimAssistGlobals* aaGlob, const float range, const float regionWidth, const float regionHeight)
{
const auto rangeSqr = range * range;
for (auto targetIndex = 0; targetIndex < aaGlob->screenTargetCount; targetIndex++)
{
const auto* currentTarget = &aaGlob->screenTargets[targetIndex];
if (currentTarget->distSqr <= rangeSqr && AimAssist_DoBoundsIntersectCenterBox(currentTarget->clipMins, currentTarget->clipMaxs, regionWidth, regionHeight))
{
return currentTarget;
}
}
return nullptr;
}
const Game::AimScreenTarget* Gamepad::AimAssist_GetTargetFromEntity(const Game::AimAssistGlobals* aaGlob, const int entIndex)
{
if (entIndex == Game::AIM_TARGET_INVALID)
return nullptr;
for (auto targetIndex = 0; targetIndex < aaGlob->screenTargetCount; targetIndex++)
{
const auto* currentTarget = &aaGlob->screenTargets[targetIndex];
if (currentTarget->entIndex == entIndex)
return currentTarget;
}
return nullptr;
}
const Game::AimScreenTarget* Gamepad::AimAssist_GetPrevOrBestTarget(const Game::AimAssistGlobals* aaGlob, const float range, const float regionWidth, const float regionHeight,
const int prevTargetEnt)
{
const auto screenTarget = AimAssist_GetTargetFromEntity(aaGlob, prevTargetEnt);
if (screenTarget && (range * range) > screenTarget->distSqr && AimAssist_DoBoundsIntersectCenterBox(screenTarget->clipMins, screenTarget->clipMaxs, regionWidth, regionHeight))
return screenTarget;
return AimAssist_GetBestTarget(aaGlob, range, regionWidth, regionHeight);
}
void Gamepad::AimAssist_ApplyLockOn(const Game::AimInput* input, Game::AimOutput* output)
{
#ifdef AIM_ASSIST_ENABLED
assert(input);
assert(input->localClientNum < Game::MAX_GAMEPADS);
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
const auto prevTargetEnt = aaGlob.lockOnTargetEnt;
aaGlob.lockOnTargetEnt = Game::AIM_TARGET_INVALID;
if (!aim_lockon_enabled.get<bool>() || AimAssist_IsPlayerUsingOffhand(&aaGlob.ps) || aaGlob.autoAimActive || aaGlob.autoMeleeState == Game::AIM_MELEE_STATE_UPDATING)
return;
const auto* weaponDef = Game::BG_GetWeaponDef(aaGlob.ps.weapIndex);
if (weaponDef->requireLockonToFire)
return;
const auto deflection = aim_lockon_deflection.get<float>();
if (deflection > std::fabs(input->pitchAxis) && deflection > std::fabs(input->yawAxis) && deflection > std::fabs(input->rightAxis))
return;
if (!aaGlob.ps.weapIndex)
return;
const auto aimAssistRange = AimAssist_Lerp(weaponDef->aimAssistRange, weaponDef->aimAssistRangeAds, aaGlob.adsLerp) * aim_aimAssistRangeScale.get<float>();
const auto screenTarget = AimAssist_GetPrevOrBestTarget(&aaGlob, aimAssistRange, aaGlob.tweakables.lockOnRegionWidth, aaGlob.tweakables.lockOnRegionHeight, prevTargetEnt);
if (screenTarget && screenTarget->distSqr > 0.0f)
{
aaGlob.lockOnTargetEnt = screenTarget->entIndex;
const auto arcLength = std::sqrt(screenTarget->distSqr) * static_cast<float>(M_PI);
const auto pitchTurnRate =
(screenTarget->velocity[0] * aaGlob.viewAxis[2][0] + screenTarget->velocity[1] * aaGlob.viewAxis[2][1] + screenTarget->velocity[2] * aaGlob.viewAxis[2][2]
- (aaGlob.ps.velocity[0] * aaGlob.viewAxis[2][0] + aaGlob.ps.velocity[1] * aaGlob.viewAxis[2][1] + aaGlob.ps.velocity[2] * aaGlob.viewAxis[2][2]))
/ arcLength * 180.0f * aim_lockon_pitch_strength.get<float>();
const auto yawTurnRate =
(screenTarget->velocity[0] * aaGlob.viewAxis[1][0] + screenTarget->velocity[1] * aaGlob.viewAxis[1][1] + screenTarget->velocity[2] * aaGlob.viewAxis[1][2]
- (aaGlob.ps.velocity[0] * aaGlob.viewAxis[1][0] + aaGlob.ps.velocity[1] * aaGlob.viewAxis[1][1] + aaGlob.ps.velocity[2] * aaGlob.viewAxis[1][2]))
/ arcLength * 180.0f * aim_lockon_strength.get<float>();
output->pitch -= pitchTurnRate * input->deltaTime;
output->yaw += yawTurnRate * input->deltaTime;
}
#endif
}
void Gamepad::AimAssist_CalcAdjustedAxis(const Game::AimInput* input, float* pitchAxis, float* yawAxis) void Gamepad::AimAssist_CalcAdjustedAxis(const Game::AimInput* input, float* pitchAxis, float* yawAxis)
{ {
assert(input); assert(input);
@ -390,14 +512,63 @@ namespace Components
} }
} }
void Gamepad::AimAssist_CalcSlowdown(const Game::AimInput* /*input*/, float* pitchScale, float* yawScale) bool Gamepad::AimAssist_IsSlowdownActive(const Game::AimAssistPlayerState* ps)
{ {
/*assert(input); */ if (!aim_slowdown_enabled.get<bool>())
return false;
if (!ps->weapIndex)
return false;
const auto* weaponDef = Game::BG_GetWeaponDef(ps->weapIndex);
if (weaponDef->requireLockonToFire)
return false;
if (ps->linkFlags & 4)
return false;
if (ps->weaponState >= Game::WEAPON_STUNNED_START && ps->weaponState <= Game::WEAPON_STUNNED_END)
return false;
if (ps->eFlags & 0x300800)
return false;
if (!ps->hasAmmo)
return false;
return true;
}
void Gamepad::AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale)
{
assert(input);
assert(input->localClientNum < Game::MAX_GAMEPADS);
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
assert(pitchScale); assert(pitchScale);
assert(yawScale); assert(yawScale);
*pitchScale = 1.0f; *pitchScale = 1.0f;
*yawScale = 1.0f; *yawScale = 1.0f;
#ifdef AIM_ASSIST_ENABLED
if (!AimAssist_IsSlowdownActive(&aaGlob.ps))
return;
const auto* weaponDef = Game::BG_GetWeaponDef(aaGlob.ps.weapIndex);
const auto aimAssistRange = AimAssist_Lerp(weaponDef->aimAssistRange, weaponDef->aimAssistRangeAds, aaGlob.adsLerp) * aim_aimAssistRangeScale.get<float>();
const auto screenTarget = AimAssist_GetBestTarget(&aaGlob, aimAssistRange, aaGlob.tweakables.slowdownRegionWidth, aaGlob.tweakables.slowdownRegionHeight);
if (screenTarget)
{
*pitchScale = AimAssist_Lerp(aim_slowdown_pitch_scale.get<float>(), aim_slowdown_pitch_scale_ads.get<float>(), aaGlob.adsLerp);
*yawScale = AimAssist_Lerp(aim_slowdown_yaw_scale.get<float>(), aim_slowdown_yaw_scale_ads.get<float>(), aaGlob.adsLerp);
}
if (AimAssist_IsPlayerUsingOffhand(&aaGlob.ps))
*pitchScale = 1.0f;
#endif
} }
float Gamepad::AimAssist_Lerp(const float from, const float to, const float fraction) float Gamepad::AimAssist_Lerp(const float from, const float to, const float fraction)
@ -483,6 +654,7 @@ namespace Components
AimAssist_ApplyTurnRates(input, output); AimAssist_ApplyTurnRates(input, output);
Game::AimAssist_ApplyAutoMelee(input, output); Game::AimAssist_ApplyAutoMelee(input, output);
AimAssist_ApplyLockOn(input, output);
} }
aaGlob.prevButtons = input->buttons; aaGlob.prevButtons = input->buttons;
@ -1451,6 +1623,17 @@ namespace Components
aim_scale_view_axis = Dvar::Var("aim_scale_view_axis"); aim_scale_view_axis = Dvar::Var("aim_scale_view_axis");
cl_bypassMouseInput = Dvar::Var("cl_bypassMouseInput"); cl_bypassMouseInput = Dvar::Var("cl_bypassMouseInput");
cg_mapLocationSelectionCursorSpeed = Dvar::Var("cg_mapLocationSelectionCursorSpeed"); cg_mapLocationSelectionCursorSpeed = Dvar::Var("cg_mapLocationSelectionCursorSpeed");
aim_aimAssistRangeScale = Dvar::Var("aim_aimAssistRangeScale");
aim_slowdown_enabled = Dvar::Var("aim_slowdown_enabled");
aim_slowdown_debug = Dvar::Var("aim_slowdown_debug");
aim_slowdown_pitch_scale = Dvar::Var("aim_slowdown_pitch_scale");
aim_slowdown_pitch_scale_ads = Dvar::Var("aim_slowdown_pitch_scale_ads");
aim_slowdown_yaw_scale = Dvar::Var("aim_slowdown_yaw_scale");
aim_slowdown_yaw_scale_ads = Dvar::Var("aim_slowdown_yaw_scale_ads");
aim_lockon_enabled = Dvar::Var("aim_lockon_enabled");
aim_lockon_deflection = Dvar::Var("aim_lockon_deflection");
aim_lockon_pitch_strength = Dvar::Var("aim_lockon_pitch_strength");
aim_lockon_strength = Dvar::Var("aim_lockon_strength");
} }
void Gamepad::IN_Init_Hk() void Gamepad::IN_Init_Hk()

View File

@ -115,6 +115,43 @@ namespace Game
GamepadVirtualAxisMapping virtualAxes[GPAD_VIRTAXIS_COUNT]; GamepadVirtualAxisMapping virtualAxes[GPAD_VIRTAXIS_COUNT];
}; };
enum weaponstate_t
{
WEAPON_READY = 0x0,
WEAPON_RAISING = 0x1,
WEAPON_RAISING_ALTSWITCH = 0x2,
WEAPON_DROPPING = 0x3,
WEAPON_DROPPING_QUICK = 0x4,
WEAPON_DROPPING_ALT = 0x5,
WEAPON_FIRING = 0x6,
WEAPON_RECHAMBERING = 0x7,
WEAPON_RELOADING = 0x8,
WEAPON_RELOADING_INTERUPT = 0x9,
WEAPON_RELOAD_START = 0xA,
WEAPON_RELOAD_START_INTERUPT = 0xB,
WEAPON_RELOAD_END = 0xC,
WEAPON_MELEE_INIT = 0xD,
WEAPON_MELEE_FIRE = 0xE,
WEAPON_MELEE_END = 0xF,
WEAPON_OFFHAND_INIT = 0x10,
WEAPON_OFFHAND_PREPARE = 0x11,
WEAPON_OFFHAND_HOLD = 0x12,
WEAPON_OFFHAND_FIRE = 0x13,
WEAPON_OFFHAND_DETONATE = 0x14,
WEAPON_OFFHAND_END = 0x15,
WEAPON_DETONATING = 0x16,
WEAPON_SPRINT_RAISE = 0x17,
WEAPON_SPRINT_LOOP = 0x18,
WEAPON_SPRINT_DROP = 0x19,
WEAPON_STUNNED_START = 0x1A,
WEAPON_STUNNED_LOOP = 0x1B,
WEAPON_STUNNED_END = 0x1C,
WEAPON_NIGHTVISION_WEAR = 0x1D,
WEAPON_NIGHTVISION_REMOVE = 0x1E,
WEAPONSTATES_NUM
};
struct AimAssistPlayerState struct AimAssistPlayerState
{ {
float velocity[3]; float velocity[3];
@ -142,7 +179,8 @@ namespace Game
float lockOnRegionWidth; float lockOnRegionWidth;
float lockOnRegionHeight; float lockOnRegionHeight;
}; };
constexpr auto AIM_TARGET_INVALID = 0x3FF;
struct AimScreenTarget struct AimScreenTarget
{ {
int entIndex; int entIndex;
@ -273,6 +311,17 @@ namespace Components
static Dvar::Var aim_scale_view_axis; static Dvar::Var aim_scale_view_axis;
static Dvar::Var cl_bypassMouseInput; static Dvar::Var cl_bypassMouseInput;
static Dvar::Var cg_mapLocationSelectionCursorSpeed; static Dvar::Var cg_mapLocationSelectionCursorSpeed;
static Dvar::Var aim_aimAssistRangeScale;
static Dvar::Var aim_slowdown_enabled;
static Dvar::Var aim_slowdown_debug;
static Dvar::Var aim_slowdown_pitch_scale;
static Dvar::Var aim_slowdown_pitch_scale_ads;
static Dvar::Var aim_slowdown_yaw_scale;
static Dvar::Var aim_slowdown_yaw_scale_ads;
static Dvar::Var aim_lockon_enabled;
static Dvar::Var aim_lockon_deflection;
static Dvar::Var aim_lockon_pitch_strength;
static Dvar::Var aim_lockon_strength;
static Dvar::Var xpadSensitivity; static Dvar::Var xpadSensitivity;
static Dvar::Var xpadEarlyTime; static Dvar::Var xpadEarlyTime;
@ -289,7 +338,14 @@ namespace Components
static void MSG_ReadDeltaUsercmdKeyStub2(); static void MSG_ReadDeltaUsercmdKeyStub2();
static float LinearTrack(float target, float current, float rate, float deltaTime); static float LinearTrack(float target, float current, float rate, float deltaTime);
static bool AimAssist_DoBoundsIntersectCenterBox(const float* clipMins, const float* clipMaxs, float clipHalfWidth, float clipHalfHeight);
static bool AimAssist_IsPlayerUsingOffhand(Game::AimAssistPlayerState* ps);
static const Game::AimScreenTarget* AimAssist_GetBestTarget(const Game::AimAssistGlobals* aaGlob, float range, float regionWidth, float regionHeight);
static const Game::AimScreenTarget* AimAssist_GetTargetFromEntity(const Game::AimAssistGlobals* aaGlob, int entIndex);
static const Game::AimScreenTarget* AimAssist_GetPrevOrBestTarget(const Game::AimAssistGlobals* aaGlob, float range, float regionWidth, float regionHeight, int prevTargetEnt);
static void AimAssist_ApplyLockOn(const Game::AimInput* input, Game::AimOutput* output);
static void AimAssist_CalcAdjustedAxis(const Game::AimInput* input, float* pitchAxis, float* yawAxis); static void AimAssist_CalcAdjustedAxis(const Game::AimInput* input, float* pitchAxis, float* yawAxis);
static bool AimAssist_IsSlowdownActive(const Game::AimAssistPlayerState* ps);
static void AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale); static void AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale);
static float AimAssist_Lerp(float from, float to, float fraction); static float AimAssist_Lerp(float from, float to, float fraction);
static void AimAssist_ApplyTurnRates(const Game::AimInput* input, Game::AimOutput* output); static void AimAssist_ApplyTurnRates(const Game::AimInput* input, Game::AimOutput* output);

View File

@ -30,6 +30,7 @@ namespace Game
BG_GetNumWeapons_t BG_GetNumWeapons = BG_GetNumWeapons_t(0x4F5CC0); BG_GetNumWeapons_t BG_GetNumWeapons = BG_GetNumWeapons_t(0x4F5CC0);
BG_GetWeaponName_t BG_GetWeaponName = BG_GetWeaponName_t(0x4E6EC0); BG_GetWeaponName_t BG_GetWeaponName = BG_GetWeaponName_t(0x4E6EC0);
BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj = BG_LoadWeaponDef_LoadObj_t(0x57B5F0); BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj = BG_LoadWeaponDef_LoadObj_t(0x57B5F0);
BG_GetWeaponDef_t BG_GetWeaponDef = BG_GetWeaponDef_t(0x440EB0);
Cbuf_AddServerText_t Cbuf_AddServerText = Cbuf_AddServerText_t(0x4BB9B0); Cbuf_AddServerText_t Cbuf_AddServerText = Cbuf_AddServerText_t(0x4BB9B0);
Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20); Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20);

View File

@ -40,6 +40,9 @@ namespace Game
typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename); typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename);
extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj; extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj;
typedef WeaponDef* (__cdecl* BG_GetWeaponDef_t)(int weaponIndex);
extern BG_GetWeaponDef_t BG_GetWeaponDef;
typedef void(__cdecl * Cbuf_AddServerText_t)(); typedef void(__cdecl * Cbuf_AddServerText_t)();
extern Cbuf_AddServerText_t Cbuf_AddServerText; extern Cbuf_AddServerText_t Cbuf_AddServerText;

View File

@ -1288,6 +1288,16 @@ namespace Game
unsigned int playerCardNameplate; unsigned int playerCardNameplate;
}; };
enum usercmdButtonBits
{
CMD_BUTTON_ATTACK = 0x1,
CMD_BUTTON_SPRINT = 0x2,
CMD_BUTTON_MELEE = 0x4,
CMD_BUTTON_ACTIVATE = 0x8,
CMD_BUTTON_RELOAD = 0x10,
CMD_BUTTON_USE_RELOAD = 0x20,
};
#pragma pack(push, 4) #pragma pack(push, 4)
struct usercmd_s struct usercmd_s
{ {

View File

@ -10,6 +10,7 @@
#define VC_EXTRALEAN #define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/ // Requires Visual Leak Detector plugin: http://vld.codeplex.com/
#define VLD_FORCE_ENABLE #define VLD_FORCE_ENABLE