mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-29 17:43:01 +00:00
373 lines
10 KiB
C++
373 lines
10 KiB
C++
//========= Copyright 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;
|
|
}
|