diff --git a/src/game/server/NextBot/Player/NextBotPlayer.h b/src/game/server/NextBot/Player/NextBotPlayer.h index d12df0fe5e..5252f9b6f4 100644 --- a/src/game/server/NextBot/Player/NextBotPlayer.h +++ b/src/game/server/NextBot/Player/NextBotPlayer.h @@ -699,6 +699,7 @@ inline void _NextBot_BuildUserCommand( CUserCmd *cmd, const QAngle &viewangles, #ifdef NEO extern ConVar bot_mimic; extern ConVar bot_mimic_yaw_offset; +extern ConVar bot_mimic_mirror; #endif // NEO template < typename PlayerType > inline void NextBotPlayer< PlayerType >::PhysicsSimulate( void ) @@ -743,6 +744,12 @@ inline void NextBotPlayer< PlayerType >::PhysicsSimulate( void ) cmd = *pPlayerMimicked->GetLastUserCommand(); cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat(); + if (bot_mimic_mirror.GetBool()) + { + cmd.viewangles[YAW] = -cmd.viewangles[YAW]; + cmd.viewangles[PITCH] = -cmd.viewangles[PITCH]; + } + // allocate a new command and add it to the player's list of command to process this->ProcessUsercmds(&cmd, 1, 1, 0, false); diff --git a/src/game/server/NextBot/Player/NextBotPlayerBody.cpp b/src/game/server/NextBot/Player/NextBotPlayerBody.cpp index 720aef93fd..f6c12f2c8a 100644 --- a/src/game/server/NextBot/Player/NextBotPlayerBody.cpp +++ b/src/game/server/NextBot/Player/NextBotPlayerBody.cpp @@ -121,6 +121,7 @@ void PlayerBody::Reset( void ) ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." ); #ifdef NEO ConVar bot_mimic_yaw_offset("bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw."); +ConVar bot_mimic_mirror("bot_mimic_mirror", "0", 0, "Mirrors the mimic axes."); #endif // NEO //----------------------------------------------------------------------------------------------- diff --git a/src/game/shared/gamemovement.cpp b/src/game/shared/gamemovement.cpp index 6224668be0..0f9b051bda 100644 --- a/src/game/shared/gamemovement.cpp +++ b/src/game/shared/gamemovement.cpp @@ -14,9 +14,11 @@ #include "decals.h" #include "coordsize.h" #include "rumble_shared.h" +#ifndef NEO #ifdef CLIENT_DLL #include "prediction.h" #endif +#endif #if defined(HL2_DLL) || defined(HL2_CLIENT_DLL) #include "hl_movedata.h" diff --git a/src/game/shared/neo/weapons/weapon_knife.cpp b/src/game/shared/neo/weapons/weapon_knife.cpp index 2952f45c3e..8e35cc96b9 100644 --- a/src/game/shared/neo/weapons/weapon_knife.cpp +++ b/src/game/shared/neo/weapons/weapon_knife.cpp @@ -1,15 +1,27 @@ #include "cbase.h" #include "weapon_knife.h" -#ifndef CLIENT_DLL +#ifdef GAME_DLL #include "ilagcompensationmanager.h" #include "effect_dispatch_data.h" #include "te_effect_dispatch.h" +#else +#include "prediction.h" #endif +#include "takedamageinfo.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +static ConVar sv_neo_backstab_ignorez("sv_neo_backstab_ignorez", "1", FCVAR_REPLICATED | FCVAR_NOTIFY, + "Whether knife backstabs angle calculations should ignore the pitch component.", true, false, true, true); + +// NEO NOTE (Rain): Changed to degrees to make it more intuitive for players. +// Was previously: 0.6435011 radians; // ~ asin(0.6); +static ConVar sv_neo_backstab_angle("sv_neo_backstab_angle", "37", FCVAR_REPLICATED | FCVAR_NOTIFY, + "Maximum angle away from perfectly behind the back that still counts as a backstab.", true, 0, true, 180); + #define KNIFE_VM_ATTACK_ACT ACT_VM_PRIMARYATTACK #define KNIFE_HULL_DIM 16.0f @@ -255,64 +267,93 @@ Activity CWeaponKnife::ChooseIntersectionPointAndActivity(trace_t& hitTrace, con return KNIFE_VM_ATTACK_ACT; } -void CWeaponKnife::Hit(trace_t& traceHit, [[maybe_unused]] Activity nHitActivity) +inline void CWeaponKnife::ApplyDamageToHitTarget(trace_t& traceHit) { - Assert(nHitActivity == KNIFE_VM_ATTACK_ACT); +#ifdef CLIENT_DLL + if (prediction->InPrediction() && !prediction->IsFirstTimePredicted()) + return; +#endif - CBasePlayer *pPlayer = ToBasePlayer(GetOwner()); + if (!traceHit.m_pEnt) + return; - // Do view kick - //AddViewKick(); + auto *pPlayer = assert_cast(GetOwner()); + AssertMsg(pPlayer != traceHit.m_pEnt, "Shouldn't be able to hit self"); + // NEO NOTE (Rain): client-side abs ang queries don't seem to work well, + // so I've changed the Vector forward to use the eye angles forward for + // prediction purposes. It probably feels more fair to the player as well, + // since as long as they keep their eyes on the attacker, i.e. the attacker + // is physically in front of them, they cannot be backstabbed (assuming the + // server agrees on those angles). + Vector forward; + AngleVectors(traceHit.m_pEnt->EyeAngles(), &forward); - CBaseEntity *pHitEntity = traceHit.m_pEnt; + Vector attackerToTarget = traceHit.m_pEnt->GetAbsOrigin() - pPlayer->GetAbsOrigin(); - //Apply damage to a hit target - if (pHitEntity != NULL) + if (sv_neo_backstab_ignorez.GetBool()) { - // Shouldn't be able to hit self - Assert(pPlayer != pHitEntity); - - Vector hitDirection; - pPlayer->EyeVectors(&hitDirection, NULL, NULL); - VectorNormalize(hitDirection); - -#ifndef CLIENT_DLL - - Vector forward; - AngleVectors(pHitEntity->GetAbsAngles(), &forward); - - Vector2D forward2D = Vector2D(forward.x, forward.y); - forward2D.NormalizeInPlace(); + forward.AsVector2D().NormalizeInPlace(); + forward.z = 0; - Vector attackerToTarget = pHitEntity->GetAbsOrigin() - pPlayer->GetAbsOrigin(); - Vector2D attackerToTarget2D = Vector2D(attackerToTarget.x, attackerToTarget.y); - attackerToTarget2D.NormalizeInPlace(); - - const float currentAngle = acos(DotProduct2D(forward2D, attackerToTarget2D)); - - static constexpr float maxBackStabAngle = 0.6435011; // ~ asin(0.6); + attackerToTarget.AsVector2D().NormalizeInPlace(); + attackerToTarget.z = 0; + } + else + { + attackerToTarget.NormalizeInPlace(); + } + AssertFloatEquals(forward.Length(), 1.f, 0.01f); + AssertFloatEquals(attackerToTarget.Length(), 1.f, 0.01f); - static constexpr int damageToOneShotSupport = MAX_HEALTH_FOR_CLASS[NEO_CLASS_SUPPORT] + 1; + const float currentAngle = RAD2DEG(acos(forward.Dot(attackerToTarget))); - CTakeDamageInfo info(GetOwner(), GetOwner(), KNIFE_DAMAGE, DMG_SLASH); + CTakeDamageInfo info(pPlayer, pPlayer, KNIFE_DAMAGE, DMG_SLASH); + Vector hitDirection; + pPlayer->EyeVectors(&hitDirection); + AssertFloatEquals(hitDirection.Length(), 1.f, 0.01f); + CalculateMeleeDamageForce(&info, hitDirection, traceHit.endpos, 0.05f); - CalculateMeleeDamageForce(&info, hitDirection, traceHit.endpos, 0.05f); + const bool isBackstab = (currentAngle <= sv_neo_backstab_angle.GetFloat()); + // increase damage if backstabbing only after melee damage force has been calculated, + // so objects cannot be "backstabbed" to launch them further + if (isBackstab) + { + // If this doesn't do any damage, step ApplyMultiDamage or the below: + // traceHit.m_pEnt->GetReceivedDamageScale(pPlayer); +#if 0 + const char* help = ""; +#ifdef GAME_DLL + if (!traceHit.m_pEnt->GetReceivedDamageScale(pPlayer)) + help = " (dmg scale 0, maybe in spawn protection?)"; +#endif - if (currentAngle <= maxBackStabAngle) - { // increase damage if backstabbing only after melee damage force has been calculated, so objects cannot be "backstabbed" to launch them further - info.SetDamage(damageToOneShotSupport); - } + DevMsg("[%s] \"%s\" backstabbed \"%s\"%s\n", + (IsServer() ? "SRV auth" : "CLI pred"), + pPlayer->GetNeoPlayerName(), + assert_cast(traceHit.m_pEnt)->GetNeoPlayerName(), + help + ); +#endif + static constexpr float oneShotDamage = MAX_HEALTH_FOR_CLASS[NEO_CLASS_SUPPORT] + 1; + info.SetMaxDamage(info.GetMaxDamage() + oneShotDamage); + info.AddDamage(oneShotDamage); + Assert(traceHit.m_pEnt->GetMaxHealth() < info.GetDamage() + info.GetDamageBonus()); + } - pHitEntity->DispatchTraceAttack(info, hitDirection, &traceHit); - ApplyMultiDamage(); + traceHit.m_pEnt->DispatchTraceAttack(info, hitDirection, &traceHit); + ApplyMultiDamage(); - // Now hit all triggers along the ray that... - TraceAttackToTriggers(info, traceHit.startpos, traceHit.endpos, hitDirection); +#ifdef GAME_DLL + // Now hit all triggers along that ray... + TraceAttackToTriggers(info, traceHit.startpos, traceHit.endpos, hitDirection); #endif - WeaponSound(MELEE_HIT); - } +} - // Apply an impact effect +void CWeaponKnife::Hit(trace_t& traceHit, [[maybe_unused]] Activity nHitActivity) +{ + Assert(nHitActivity == KNIFE_VM_ATTACK_ACT); + ApplyDamageToHitTarget(traceHit); + WeaponSound(MELEE_HIT); ImpactEffect(traceHit); } diff --git a/src/game/shared/neo/weapons/weapon_knife.h b/src/game/shared/neo/weapons/weapon_knife.h index 13346eefc6..eeae459ea6 100644 --- a/src/game/shared/neo/weapons/weapon_knife.h +++ b/src/game/shared/neo/weapons/weapon_knife.h @@ -18,7 +18,7 @@ #define CWeaponKnife C_WeaponKnife #endif -#define NEO_WEP_KNIFE_RANGE 51.f; +#define NEO_WEP_KNIFE_RANGE 51.f class CWeaponKnife : public CNEOBaseCombatWeapon { @@ -33,6 +33,7 @@ class CWeaponKnife : public CNEOBaseCombatWeapon #endif CWeaponKnife(); + CWeaponKnife(const CWeaponKnife& other) = delete; virtual void PrimaryAttack() final; virtual void Drop(const Vector &vecVelocity) final { /* knives shouldn't drop */ } @@ -63,7 +64,7 @@ class CWeaponKnife : public CNEOBaseCombatWeapon bool ImpactWater(const Vector &start, const Vector &end); void Hit(trace_t& traceHit, Activity nHitActivity); private: - CWeaponKnife(const CWeaponKnife &other); + inline void ApplyDamageToHitTarget(trace_t& traceHit); }; #endif // NEO_WEAPON_KNIFE_H diff --git a/src/game/shared/takedamageinfo.cpp b/src/game/shared/takedamageinfo.cpp index 6c60b56cf7..ee43a6db9e 100644 --- a/src/game/shared/takedamageinfo.cpp +++ b/src/game/shared/takedamageinfo.cpp @@ -215,12 +215,16 @@ void ApplyMultiDamage( void ) return; #ifndef CLIENT_DLL +#ifndef NEO const CBaseEntity *host = te->GetSuppressHost(); te->SetSuppressHost( NULL ); +#endif g_MultiDamage.GetTarget()->TakeDamage( g_MultiDamage ); +#ifndef NEO te->SetSuppressHost( (CBaseEntity*)host ); +#endif #endif // Damage is done, clear it out @@ -246,7 +250,11 @@ void AddMultiDamage( const CTakeDamageInfo &info, CBaseEntity *pEntity ) g_MultiDamage.SetDamageForce( g_MultiDamage.GetDamageForce() + info.GetDamageForce() ); g_MultiDamage.SetDamagePosition( info.GetDamagePosition() ); g_MultiDamage.SetReportedPosition( info.GetReportedPosition() ); +#ifdef NEO + g_MultiDamage.SetMaxDamage( Max( g_MultiDamage.GetMaxDamage(), info.GetMaxDamage() ) ); +#else g_MultiDamage.SetMaxDamage( MAX( g_MultiDamage.GetMaxDamage(), info.GetDamage() ) ); +#endif g_MultiDamage.SetAmmoType( info.GetAmmoType() ); g_MultiDamage.SetCritType( info.GetCritType() );