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

372 lines
9.9 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Stun Stick- beating stick with a zappy end
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "npcevent.h"
#include "npc_metropolice.h"
#include "weapon_stunstick.h"
#include "IEffects.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar sk_plr_dmg_stunstick("sk_plr_dmg_stunstick", "0");
ConVar sk_npc_dmg_stunstick("sk_npc_dmg_stunstick", "0");
extern ConVar metropolice_move_and_melee;
//-----------------------------------------------------------------------------
// CWeaponStunStick
//-----------------------------------------------------------------------------
IMPLEMENT_SERVERCLASS_ST(CWeaponStunStick, DT_WeaponStunStick)
SendPropInt(SENDINFO(m_bActive), 1, SPROP_UNSIGNED),
END_SEND_TABLE()
//#ifndef HL2MP
LINK_ENTITY_TO_CLASS(weapon_stunstick, CWeaponStunStick);
PRECACHE_WEAPON_REGISTER(weapon_stunstick);
//#endif
acttable_t CWeaponStunStick::m_acttable[] =
{
{ ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true },
};
IMPLEMENT_ACTTABLE(CWeaponStunStick);
BEGIN_DATADESC(CWeaponStunStick)
DEFINE_FIELD(m_bActive, FIELD_BOOLEAN),
END_DATADESC()
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CWeaponStunStick::CWeaponStunStick(void)
{
// HACK: Don't call SetStunState because this tried to Emit a sound before
// any players are connected which is a bug
m_bActive = false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CWeaponStunStick::Spawn()
{
Precache();
BaseClass::Spawn();
AddSolidFlags(FSOLID_NOT_STANDABLE);
}
void CWeaponStunStick::Precache()
{
BaseClass::Precache();
PrecacheScriptSound("Weapon_StunStick.Activate");
PrecacheScriptSound("Weapon_StunStick.Deactivate");
}
//-----------------------------------------------------------------------------
// Purpose: Get the damage amount for the animation we're doing
// Input : hitActivity - currently played activity
// Output : Damage amount
//-----------------------------------------------------------------------------
float CWeaponStunStick::GetDamageForActivity(Activity hitActivity)
{
if ((GetOwner() != NULL) && (GetOwner()->IsPlayer()))
return sk_plr_dmg_stunstick.GetFloat();
return sk_npc_dmg_stunstick.GetFloat();
}
//-----------------------------------------------------------------------------
// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
//-----------------------------------------------------------------------------
extern ConVar sk_crowbar_lead_time;
int CWeaponStunStick::WeaponMeleeAttack1Condition(float flDot, float flDist)
{
// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
CBaseEntity *pEnemy = pNPC->GetEnemy();
if (!pEnemy)
return COND_NONE;
Vector vecVelocity;
AngularImpulse angVelocity;
pEnemy->GetVelocity(&vecVelocity, &angVelocity);
// Project where the enemy will be in a little while, add some randomness so he doesn't always hit
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 flExtrapolatedDot = DotProduct2D(vecDelta.AsVector2D(), vecForward.AsVector2D());
if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
{
return COND_NOT_FACING_ATTACK;
}
float flExtrapolatedDist = Vector2DNormalize(vecDelta.AsVector2D());
if (pEnemy->IsPlayer())
{
//Vector vecDir = pEnemy->GetSmoothedVelocity();
//float flSpeed = VectorNormalize( vecDir );
// If player will be in front of me in one-half second, clock his arse.
Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35);
Vector vecProjectMe = GetAbsOrigin();
if ((vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f)
{
return COND_CAN_MELEE_ATTACK1;
}
}
/*
if( metropolice_move_and_melee.GetBool() )
{
if( pNPC->IsMoving() )
{
flTargetDist *= 1.5f;
}
}
*/
float flTargetDist = 48.0f;
if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist))
{
return COND_TOO_FAR_TO_ATTACK;
}
return COND_CAN_MELEE_ATTACK1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponStunStick::ImpactEffect(trace_t &traceHit)
{
//Glowing spark effect for hit
//UTIL_DecalTrace( &m_trLineHit, "PlasmaGlowFade" );
//FIXME: need new decals
UTIL_ImpactTrace(&traceHit, DMG_CLUB);
}
void CWeaponStunStick::Operator_HandleAnimEvent(animevent_t *pEvent, CBaseCombatCharacter *pOperator)
{
switch (pEvent->event)
{
case EVENT_WEAPON_MELEE_HIT:
{
// Trace up or down based on where the enemy is...
// But only if we're basically facing that direction
Vector vecDirection;
AngleVectors(GetAbsAngles(), &vecDirection);
CBaseEntity *pEnemy = pOperator->MyNPCPointer() ? pOperator->MyNPCPointer()->GetEnemy() : NULL;
if (pEnemy)
{
Vector vecDelta;
VectorSubtract(pEnemy->WorldSpaceCenter(), pOperator->Weapon_ShootPosition(), vecDelta);
VectorNormalize(vecDelta);
Vector2D vecDelta2D = vecDelta.AsVector2D();
Vector2DNormalize(vecDelta2D);
if (DotProduct2D(vecDelta2D, vecDirection.AsVector2D()) > 0.8f)
{
vecDirection = vecDelta;
}
}
Vector vecEnd;
VectorMA(pOperator->Weapon_ShootPosition(), 32, vecDirection, vecEnd);
// Stretch the swing box down to catch low level physics objects
CBaseEntity *pHurt = pOperator->CheckTraceHullAttack(pOperator->Weapon_ShootPosition(), vecEnd,
Vector(-16, -16, -40), Vector(16, 16, 16), GetDamageForActivity(GetActivity()), DMG_CLUB, 0.5f, false);
// did I hit someone?
if (pHurt)
{
// play sound
WeaponSound(MELEE_HIT);
CBasePlayer *pPlayer = ToBasePlayer(pHurt);
CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(pOperator);
bool bFlashed = false;
if (pCop != NULL && pPlayer != NULL)
{
// See if we need to knock out this target
if (pCop->ShouldKnockOutTarget(pHurt))
{
float yawKick = random->RandomFloat(-48, -24);
//Kick the player angles
pPlayer->ViewPunch(QAngle(-16, yawKick, 2));
color32 white = { 255, 255, 255, 255 };
UTIL_ScreenFade(pPlayer, white, 0.2f, 1.0f, FFADE_OUT | FFADE_PURGE | FFADE_STAYOUT);
bFlashed = true;
pCop->KnockOutTarget(pHurt);
break;
}
else
{
// Notify that we've stunned a target
pCop->StunnedTarget(pHurt);
}
}
// Punch angles
if (pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE))
{
float yawKick = random->RandomFloat(-48, -24);
//Kick the player angles
pPlayer->ViewPunch(QAngle(-16, yawKick, 2));
Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
// If the player's on my head, don't knock him up
if (pPlayer->GetGroundEntity() == pOperator)
{
dir = vecDirection;
dir.z = 0;
}
VectorNormalize(dir);
dir *= 500.0f;
//If not on ground, then don't make them fly!
if (!(pPlayer->GetFlags() & FL_ONGROUND))
dir.z = 0.0f;
//Push the target back
pHurt->ApplyAbsVelocityImpulse(dir);
if (!bFlashed)
{
color32 red = { 128, 0, 0, 128 };
UTIL_ScreenFade(pPlayer, red, 0.5f, 0.1f, FFADE_IN);
}
// Force the player to drop anyting they were holding
pPlayer->ForceDropOfCarriedPhysObjects();
}
// do effect?
}
else
{
WeaponSound(MELEE_MISS);
}
}
break;
default:
BaseClass::Operator_HandleAnimEvent(pEvent, pOperator);
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the state of the stun stick
//-----------------------------------------------------------------------------
void CWeaponStunStick::SetStunState(bool state)
{
m_bActive = state;
if (m_bActive)
{
//FIXME: START - Move to client-side
Vector vecAttachment;
GetAttachment(1, vecAttachment);
g_pEffects->Sparks(vecAttachment);
//FIXME: END - Move to client-side
EmitSound("Weapon_StunStick.Activate");
}
else
{
EmitSound("Weapon_StunStick.Deactivate");
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponStunStick::Deploy(void)
{
SetStunState(true);
return BaseClass::Deploy();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponStunStick::Holster(CBaseCombatWeapon *pSwitchingTo)
{
if (BaseClass::Holster(pSwitchingTo) == false)
return false;
SetStunState(false);
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vecVelocity -
//-----------------------------------------------------------------------------
void CWeaponStunStick::Drop(const Vector &vecVelocity)
{
SetStunState(false);
BaseClass::Drop(vecVelocity);
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponStunStick::GetStunState(void)
{
return m_bActive;
}