Files
HL2Overcharged/game/server/hl2/weapon_crowbar.cpp
2025-05-21 21:20:08 +03:00

492 lines
14 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Crowbar - an old favorite
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "basehlcombatweapon.h"
#include "player.h"
#include "gamerules.h"
#include "ammodef.h"
#include "mathlib/mathlib.h"
#include "in_buttons.h"
#include "soundent.h"
#include "basebludgeonweapon.h"
#include "vstdlib/random.h"
#include "npcevent.h"
#include "ai_basenpc.h"
#include "weapon_crowbar.h"
#include "particle_parse.h"
#include "rumble_shared.h"
#include "gamestats.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar sk_plr_dmg_crowbar ( "sk_plr_dmg_crowbar","0");
ConVar sk_npc_dmg_crowbar ( "sk_npc_dmg_crowbar","0");
ConVar sk_crowbar_lead_time("sk_crowbar_lead_time", "0.9");
//-----------------------------------------------------------------------------
// CWeaponCrowbar
//-----------------------------------------------------------------------------
IMPLEMENT_SERVERCLASS_ST(CWeaponCrowbar, DT_WeaponCrowbar)
END_SEND_TABLE()
#ifndef HL2MP
LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar );
PRECACHE_WEAPON_REGISTER( weapon_crowbar );
#endif
acttable_t CWeaponCrowbar::m_acttable[] =
{
/*{ ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, //old
{ ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false },*/
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false },
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false },
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false },
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false },
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false },
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false },
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false },
{ ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true },
{ ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false },
{ ACT_IDLE_RELAXED, ACT_IDLE, false },
{ ACT_RUN_RELAXED, ACT_RUN, false },
{ ACT_WALK_RELAXED, ACT_WALK, false },
};
IMPLEMENT_ACTTABLE(CWeaponCrowbar);
//#if 0
Activity CWeaponCrowbar::ChooseIntersectionPointAndActivity(trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner)
{
int i, j, k;
float distance;
const float *minmaxs[2] = { mins.Base(), maxs.Base() };
trace_t tmpTrace;
Vector vecHullEnd = hitTrace.endpos;
Vector vecEnd;
distance = 1e6f;
Vector vecSrc = hitTrace.startpos;
vecHullEnd = vecSrc + ((vecHullEnd - vecSrc) * 2);
UTIL_TraceLine(vecSrc, vecHullEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace);
if (tmpTrace.fraction == 1.0)
{
for (i = 0; i < 2; i++)
{
for (j = 0; j < 2; j++)
{
for (k = 0; k < 2; k++)
{
vecEnd.x = vecHullEnd.x + minmaxs[i][0];
vecEnd.y = vecHullEnd.y + minmaxs[j][1];
vecEnd.z = vecHullEnd.z + minmaxs[k][2];
UTIL_TraceLine(vecSrc, vecEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace);
if (tmpTrace.fraction < 1.0)
{
float thisDistance = (tmpTrace.endpos - vecSrc).Length();
if (thisDistance < distance)
{
hitTrace = tmpTrace;
distance = thisDistance;
}
}
}
}
}
}
else
{
hitTrace = tmpTrace;
}
return ACT_VM_HITCENTER;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CWeaponCrowbar::CWeaponCrowbar(void)
{
ChangeOnce = true;
bIsSecondary = true;
}
bool CWeaponCrowbar::Deploy(void)
{
ChangeOnce = true;
m_bAltattackAnim = false;
m_bAltattackChecker = false;
Fire = false;
Time = 0;
m_bDelayedSwing = false;
m_flDelayedSwingTime = 0.0f;
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose: Get the damage amount for the animation we're doing
// Input : hitActivity - currently played activity
// Output : Damage amount
//-----------------------------------------------------------------------------
float CWeaponCrowbar::GetDamageForActivity(Activity hitActivity)
{
if (m_bAltattackChecker) // Light Kill : Do check for alt attack...
{
if ((GetOwner() != NULL) && (GetOwner()->IsPlayer()))
{
return sk_plr_dmg_crowbar.GetFloat();
}
}
else
{
if ((GetOwner() != NULL) && (GetOwner()->IsPlayer()))
{
return sk_plr_dmg_crowbar.GetFloat();
}
}
return sk_plr_dmg_crowbar.GetFloat();
}
//-----------------------------------------------------------------------------
// Attempt to lead the target (needed because citizens can't hit manhacks with the Wrench!)
//-----------------------------------------------------------------------------
int CWeaponCrowbar::WeaponMeleeAttack1Condition(float flDot, float flDist)
{
// Attempt to lead the target (needed because citizens can't hit manhacks with the Wrench!)
CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
CBaseEntity *pEnemy = pNPC->GetEnemy();
if (!pEnemy)
return COND_NONE;
Vector vecVelocity;
vecVelocity = pEnemy->GetSmoothedVelocity();
// Project where the enemy will be in a little while
float dt = sk_crowbar_lead_time.GetFloat();
dt += random->RandomFloat(-0.3f, 0.2f);
if (dt < 0.0f)
dt = 0.0f;
Vector vecExtrapolatedPos;
VectorMA(pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos);
Vector vecDelta;
VectorSubtract(vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta);
if (fabs(vecDelta.z) > 70)
{
return COND_TOO_FAR_TO_ATTACK;
}
Vector vecForward = pNPC->BodyDirection2D();
vecDelta.z = 0.0f;
float flExtrapolatedDist = Vector2DNormalize(vecDelta.AsVector2D());
if ((flDist > 64) && (flExtrapolatedDist > 64))
{
return COND_TOO_FAR_TO_ATTACK;
}
float flExtrapolatedDot = DotProduct2D(vecDelta.AsVector2D(), vecForward.AsVector2D());
if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
{
return COND_NOT_FACING_ATTACK;
}
return COND_CAN_MELEE_ATTACK1;
}
//-----------------------------------------------------------------------------
// Animation event handlers
//-----------------------------------------------------------------------------
void CWeaponCrowbar::HandleAnimEventMeleeHit(animevent_t *pEvent, CBaseCombatCharacter *pOperator)
{
trace_t traceHit;
// Try a ray
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
if (!pOwner)
return;
pOwner->RumbleEffect(RUMBLE_CROWBAR_SWING, 0, RUMBLE_FLAG_RESTART);
Vector swingStart = pOwner->Weapon_ShootPosition();
Vector forward;
forward = pOwner->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT, GetRange());
Vector swingEnd = swingStart + forward * GetRange();
UTIL_TraceLine(swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit);
Activity nHitActivity = GetWpnData().animData[m_bFireMode].MeleeAttack1;
// Like bullets, bludgeon traces have to trace against triggers.
CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), DMG_CLUB);
triggerInfo.SetDamagePosition(traceHit.startpos);
triggerInfo.SetDamageForce(forward);
TraceAttackToTriggers(triggerInfo, traceHit.startpos, traceHit.endpos, forward);
if (traceHit.fraction == 1.0)
{
float bludgeonHullRadius = 1.732f * GetHull(); // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point
// Back off by hull "radius"
swingEnd -= forward * bludgeonHullRadius;
UTIL_TraceHull(swingStart, swingEnd, GetMeleeHullMin(), GetMeleeHullMax(), MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit);
if (traceHit.fraction < 1.0 && traceHit.m_pEnt)
{
Vector vecToTarget = traceHit.m_pEnt->GetAbsOrigin() - swingStart;
VectorNormalize(vecToTarget);
float dot = vecToTarget.Dot(forward);
// YWB: Make sure they are sort of facing the guy at least...
if (dot < 0.70721f)
{
// Force amiss
traceHit.fraction = 1.0f;
}
else
{
//nHitActivity = ChooseIntersectionPointAndActivity(traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner);
}
}
}
if (!bIsSecondary)
{
m_iPrimaryAttacks++;
}
else
{
m_iSecondaryAttacks++;
}
gamestats->Event_WeaponFired(pOwner, !bIsSecondary, GetClassname());
// -------------------------
// Miss
// -------------------------
if (traceHit.fraction == 1.0f)
{
nHitActivity = bIsSecondary ? GetWpnData().animData[m_bFireMode].MeleeMiss2 : GetWpnData().animData[m_bFireMode].MeleeMiss1;
// We want to test the first swing again
Vector testEnd = swingStart + forward * GetRange();
// See if we happened to hit water
ImpactWater(swingStart, testEnd);
}
else
{
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
//Make sound for the AI
CSoundEnt::InsertSound(SOUND_BULLET_IMPACT, traceHit.endpos, 400, 0.2f, pPlayer);
// This isn't great, but it's something for when the crowbar hits.
pPlayer->RumbleEffect(RUMBLE_AR2, 0, RUMBLE_FLAG_RESTART);
CBaseEntity *pHitEntity = traceHit.m_pEnt;
//Apply damage to a hit target
if (pHitEntity != NULL)
{
Vector hitDirection;
pPlayer->EyeVectors(&hitDirection, NULL, NULL);
VectorNormalize(hitDirection);
CTakeDamageInfo info(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), DMG_CLUB);
if (pPlayer && pHitEntity->IsNPC())
{
// If bonking an NPC, adjust damage.
info.AdjustPlayerDamageInflictedForSkillLevel();
}
CalculateMeleeDamageForce(&info, hitDirection, traceHit.endpos);
pHitEntity->DispatchTraceAttack(info, hitDirection, &traceHit);
ApplyMultiDamage();
// Now hit all triggers along the ray that...
TraceAttackToTriggers(info, traceHit.startpos, traceHit.endpos, hitDirection);
if (ToBaseCombatCharacter(pHitEntity))
{
gamestats->Event_WeaponHit(pPlayer, !bIsSecondary, GetClassname(), info);
}
}
ImpactEffect(traceHit);
}
m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
}
//-----------------------------------------------------------------------------
// Animation event
//-----------------------------------------------------------------------------
void CWeaponCrowbar::Operator_HandleAnimEvent(animevent_t *pEvent, CBaseCombatCharacter *pOperator)
{
switch (pEvent->event)
{
case EVENT_WEAPON_MELEE_HIT:
{
Time = 0;
m_bAltattackAnim = false;
if (!IsNearWall() && !GetOwnerIsRunning())
{
HandleAnimEventMeleeHit(pEvent, pOperator);
}
}
break;
default:
BaseClass::Operator_HandleAnimEvent(pEvent, pOperator);
break;
}
}
void CWeaponCrowbar::ItemPostFrame(void)
{
if (!m_bInReload && (IsNearWall() || GetOwnerIsRunning()))
{
m_flNextPrimaryAttack = gpGlobals->curtime + 0.45;
m_flNextSecondaryAttack = gpGlobals->curtime + 0.45;
/*if (GetActivity() != GetWpnData().MeleeIdle
&& IsViewModelSequenceFinished() && !m_bAltattackAnim)
{
SendWeaponAnim(GetWpnData().MeleeIdle);
}*/
return;
}
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
if (pOwner == NULL)
return;
trace_t traceHit;
Vector swingStart = pOwner->Weapon_ShootPosition();
Vector forward;
forward = pOwner->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT, GetRange());
Vector swingEnd = swingStart + forward * GetRange();
UTIL_TraceLine(swingStart, swingEnd, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &traceHit);
//CheckAdmireAnimations(pOwner);
if (!((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (CanReload() && pOwner->m_nButtons & IN_RELOAD) || (pOwner->m_nButtons & IN_FIREMODE))) // BriJee OVR: IN_FIREMODE added
{
// no fire buttons down or reloading
if (!ReloadOrSwitchWeapons() && (m_bInReload == false) && m_bReloadComplete == 0 && pOwner->GetActiveWeapon())
{
if (pOwner->GetActiveWeapon()->GetWpnData().animData[m_bFireMode].ZeroIdleAnim == 0 && pOwner->GetActiveWeapon()->thisType != TYPE_GRENADE)
WeaponIdle();
}
}
if (pOwner->m_nButtons & IN_ZOOM) // L1ght 15 : Zoom bug fixed
{
m_bAltattackAnim = false;
return;
}
// Light Kill : Do delay between attack
if (m_bDelayedSwing && gpGlobals->curtime > m_flDelayedSwingTime)
{
m_bDelayedSwing = false;
}
if ((pOwner->m_nButtons & IN_ATTACK) && m_flNextPrimaryAttack <= gpGlobals->curtime)//!Fire )
{
m_bAltattackChecker = false;
WeaponSound(SINGLE);
//Do view kick
AddViewKick();
if (traceHit.DidHit())
{
SendWeaponAnim(GetWpnData().animData[m_bFireMode].MeleeAttack1);
m_flNextPrimaryAttack = gpGlobals->curtime + GetWpnData().fireRate;
}
else
{
//if (GetActivity() != ACT_VM_MISSCENTER)
SendWeaponAnim(GetWpnData().animData[m_bFireMode].MeleeMiss1);
m_flNextPrimaryAttack = gpGlobals->curtime + GetWpnData().fireRate * 1.25f;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &traceHit -
//-----------------------------------------------------------------------------
bool CWeaponCrowbar::ImpactWater(const Vector &start, const Vector &end)
{
//FIXME: This doesn't handle the case of trying to splash while being underwater, but that's not going to look good
// right now anyway...
// We must start outside the water
if (UTIL_PointContents(start) & (CONTENTS_WATER | CONTENTS_SLIME))
return false;
// We must end inside of water
if (!(UTIL_PointContents(end) & (CONTENTS_WATER | CONTENTS_SLIME)))
return false;
trace_t waterTrace;
UTIL_TraceLine(start, end, (CONTENTS_WATER | CONTENTS_SLIME), GetOwner(), COLLISION_GROUP_NONE, &waterTrace);
if (waterTrace.fraction < 1.0f)
{
CEffectData data;
data.m_fFlags = 0;
data.m_vOrigin = waterTrace.endpos;
data.m_vNormal = waterTrace.plane.normal;
data.m_flScale = 8.0f;
// See if we hit slime
if (waterTrace.contents & CONTENTS_SLIME)
{
data.m_fFlags |= FX_WATER_IN_SLIME;
}
DispatchEffect("watersplash", data);
}
return true;
}
//#endif