|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: TF Base Grenade.
//
//=============================================================================//
#include "cbase.h"
#include "tf_weaponbase.h"
#include "tf_weaponbase_grenade.h"
#include "tf_gamerules.h"
#include "npcevent.h"
#include "engine/IEngineSound.h"
#include "in_buttons.h"
#include "tf_weaponbase_grenadeproj.h"
#include "eventlist.h"
// Client specific.
#ifdef CLIENT_DLL
#include "c_tf_player.h"
// Server specific.
#else
#include "tf_player.h"
#include "items.h"
#endif
#define GRENADE_TIMER 1.5f // seconds
//=============================================================================
//
// TF Grenade tables.
//
IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGrenade, DT_TFWeaponBaseGrenade )
BEGIN_NETWORK_TABLE( CTFWeaponBaseGrenade, DT_TFWeaponBaseGrenade ) // Client specific.
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bPrimed ) ), RecvPropFloat( RECVINFO( m_flThrowTime ) ), RecvPropBool( RECVINFO( m_bThrow ) ), // Server specific.
#else
SendPropBool( SENDINFO( m_bPrimed ) ), SendPropTime( SENDINFO( m_flThrowTime ) ), SendPropBool( SENDINFO( m_bThrow ) ), #endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CTFWeaponBaseGrenade ) #ifdef CLIENT_DLL
DEFINE_PRED_FIELD( m_bPrimed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flThrowTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bThrow, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), #endif
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( tf_weaponbase_grenade, CTFWeaponBaseGrenade );
//=============================================================================
//
// TF Grenade functions.
//
CTFWeaponBaseGrenade::CTFWeaponBaseGrenade() { }
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Spawn( void ) { BaseClass::Spawn();
SetViewModelIndex( 1 ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Precache() { BaseClass::Precache(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::IsPrimed( void ) { return m_bPrimed; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::Deploy( void ) { if ( BaseClass::Deploy() ) { Prime(); return true; }
return false; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Prime() { CTFWeaponInfo weaponInfo = GetTFWpnData(); m_flThrowTime = gpGlobals->curtime + weaponInfo.m_flPrimerTime; m_bPrimed = true;
#ifndef CLIENT_DLL
if ( GetWeaponID() != TF_WEAPON_GRENADE_SMOKE_BOMB ) { // Get the player owning the weapon.
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return;
pPlayer->RemoveInvisibility(); } #endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::Throw() { if ( !m_bPrimed ) return;
m_bPrimed = false; m_bThrow = false;
// Get the owning player.
CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); if ( !pPlayer ) return;
#ifdef GAME_DLL
// Calculate the time remaining.
float flTime = m_flThrowTime - gpGlobals->curtime; bool bExplodingInHand = ( flTime <= 0.0f );
// Players who are dying may not have their death state set, so check that too
bool bExplodingOnDeath = ( !pPlayer->IsAlive() || pPlayer->StateGet() == TF_STATE_DYING );
Vector vecSrc, vecThrow; vecSrc = pPlayer->Weapon_ShootPosition();
if ( bExplodingInHand || bExplodingOnDeath ) { vecThrow = vec3_origin; } else { // Determine the throw angle and velocity.
QAngle angThrow = pPlayer->LocalEyeAngles(); if ( angThrow.x < 90.0f ) { angThrow.x = -10.0f + angThrow.x * ( ( 90.0f + 10.0f ) / 90.0f ); } else { angThrow.x = 360.0f - angThrow.x; angThrow.x = -10.0f + angThrow.x * -( ( 90.0f - 10.0f ) / 90.0f ); }
// Adjust for the lowering of the spawn point
angThrow.x -= 10;
float flVelocity = ( 90.0f - angThrow.x ) * 8.0f; if ( flVelocity > 950.0f ) { flVelocity = 950.0f; }
Vector vForward, vRight, vUp; AngleVectors( angThrow, &vForward, &vRight, &vUp );
// Throw from the player's left hand position.
vecSrc += vForward * 16.0f + vRight * -8.0f + vUp * -20.0f;
vecThrow = vForward * flVelocity; }
#if 0
// Debug!!!
char str[256]; Q_snprintf( str, sizeof( str ),"GrenadeTime = %f\n", flTime ); NDebugOverlay::ScreenText( 0.5f, 0.38f, str, 255, 255, 255, 255, 2.0f ); #endif
QAngle vecAngles = RandomAngle( 0, 360 );
// Create the projectile and send in the time remaining.
if ( !bExplodingInHand ) { EmitGrenade( vecSrc, vecAngles, vecThrow, AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ), pPlayer, flTime ); } else { // We're holding onto an exploding grenade
CTFWeaponBaseGrenadeProj *pGrenade = EmitGrenade( vecSrc, vecAngles, vecThrow, AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ), pPlayer, 0.0 ); if ( pGrenade ) { pGrenade->Detonate(); } }
// The grenade is about to be destroyed, so it won't be able to holster.
// Handle the viewmodel hiding for it.
if ( bExplodingInHand || bExplodingOnDeath ) { SendWeaponAnim( ACT_VM_IDLE ); CBaseViewModel *vm = pPlayer->GetViewModel( 1 ); if ( vm ) { vm->AddEffects( EF_NODRAW ); } } #endif
// Reset the throw time
m_flThrowTime = 0.0f; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::ShouldDetonate( void ) { return ( m_flThrowTime != 0.0f ) && ( m_flThrowTime < gpGlobals->curtime ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::ItemPostFrame() { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return;
if ( m_bPrimed ) { // Is our timer up? If so, blow up immediately
if ( ShouldDetonate() ) { Throw(); return; }
if ( !m_bThrow && !( pPlayer->m_nButtons & IN_GRENADE1 || pPlayer->m_nButtons & IN_GRENADE2 ) ) { // Start throwing
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE ); m_bThrow = true; } }
if ( m_bThrow ) { if ( GetActivity() != ACT_VM_PRIMARYATTACK ) { // Start the throw animation
if ( !SendWeaponAnim( ACT_VM_PRIMARYATTACK ) ) { Throw(); } } else if ( HasWeaponIdleTimeElapsed() ) { // The Throw call here exists solely to catch the lone case of thirdperson where the
// viewmodel isn't being drawn, and hence the anim event doesn't trigger and force a throw.
// In all other cases, it'll do nothing because the grenade has already been thrown.
Throw(); } return; }
if ( !m_bPrimed && !m_bThrow ) { // Once we've finished being holstered, we'll be hidden. When that happens,
// tell our player that we're all done with the grenade throw.
if ( IsEffectActive(EF_NODRAW) ) { pPlayer->FinishThrowGrenade(); return; }
// We've been thrown. Go away.
if ( HasWeaponIdleTimeElapsed() ) { Holster(); } }
// Go straight to idle anim when deploy is done
if ( m_flTimeWeaponIdle <= gpGlobals->curtime ) { SendWeaponAnim( ACT_VM_IDLE ); } }
bool CTFWeaponBaseGrenade::ShouldLowerMainWeapon( void ) { return GetTFWpnData().m_bLowerWeapon; }
//=============================================================================
//
// Client specific functions.
//
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFWeaponBaseGrenade::ShouldDraw( void ) { if ( !BaseClass::ShouldDraw() ) { // Grenades need to be visible whenever they're being primed & thrown
if ( !m_bPrimed ) return false;
// Don't draw primed grenades for local player in first person players
if ( !(ToPlayer(GetOwner())->ShouldDrawThisPlayer()) ) return false; }
return true; }
//=============================================================================
//
// Server specific functions.
//
#else
BEGIN_DATADESC( CTFWeaponBaseGrenade ) END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFWeaponBaseGrenade::HandleAnimEvent( animevent_t *pEvent ) { if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) ) { if ( pEvent->event == AE_WPN_PRIMARYATTACK ) { Throw(); return; } }
BaseClass::HandleAnimEvent( pEvent ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFWeaponBaseGrenadeProj *CTFWeaponBaseGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer, float flTime, int iFlags ) { Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" ); return NULL; }
#endif
|