You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
10 KiB
372 lines
10 KiB
//========= 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; |
|
}
|
|
|