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.
718 lines
19 KiB
718 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
#include "cbase.h" |
|
#include "tf_weapon_grenadelauncher.h" |
|
#include "tf_fx_shared.h" |
|
#include "tf_weapon_grenade_pipebomb.h" |
|
#include "tf_gamerules.h" |
|
#include "in_buttons.h" |
|
#include "tf_weaponbase_gun.h" |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
#include "c_tf_gamestats.h" |
|
#include "bone_setup.h" |
|
|
|
// Server specific. |
|
#else |
|
#include "tf_player.h" |
|
#include "tf_gamestats.h" |
|
#include "tf_fx.h" |
|
#endif |
|
|
|
ConVar tf_double_donk_window( "tf_double_donk_window", "0.5", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "How long after an impact from a cannonball that an explosion will count as a double-donk." ); |
|
|
|
#define TF_TUBE_COUNT 6 |
|
|
|
// X is time as a fraction of cProceduralBarrelRotationTime, which is in seconds. |
|
// Y is rotation in degrees |
|
// Z is slope at Y. |
|
// These are hermite spline control points that match maya. |
|
const Vector cProceduralBarrelRotationAnimationPoints[] = |
|
{ |
|
Vector( 0, 0, 0 ), |
|
Vector( 0.7519f, 63.546f, 0 ), |
|
Vector( 1.0f, 60, 0 ) |
|
}; |
|
|
|
static_assert( ARRAYSIZE( cProceduralBarrelRotationAnimationPoints ) > 1, "cProceduralBarrelRotationAnimationPoints must have at least two elements." ); |
|
|
|
const float cProceduralBarrelRotationTime = 0.2666f; |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Grenade Launcher tables. |
|
// |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFGrenadeLauncher, DT_WeaponGrenadeLauncher ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFGrenadeLauncher, DT_WeaponGrenadeLauncher ) |
|
#ifdef CLIENT_DLL |
|
RecvPropFloat( RECVINFO( m_flDetonateTime ) ), |
|
RecvPropInt( RECVINFO( m_iCurrentTube ) ), |
|
RecvPropInt( RECVINFO( m_iGoalTube ) ), |
|
#else |
|
SendPropFloat( SENDINFO( m_flDetonateTime ) ), |
|
SendPropInt( SENDINFO( m_iCurrentTube ) ), |
|
SendPropInt( SENDINFO( m_iGoalTube ) ), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
#ifdef CLIENT_DLL |
|
BEGIN_PREDICTION_DATA( CTFGrenadeLauncher ) |
|
DEFINE_FIELD( m_flDetonateTime, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_iCurrentTube, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iGoalTube, FIELD_INTEGER ) |
|
END_PREDICTION_DATA() |
|
#endif |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_grenadelauncher, CTFGrenadeLauncher ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_grenadelauncher ); |
|
|
|
CREATE_SIMPLE_WEAPON_TABLE( TFCannon, tf_weapon_cannon ) |
|
|
|
// Server specific. |
|
#ifndef CLIENT_DLL |
|
BEGIN_DATADESC( CTFGrenadeLauncher ) |
|
END_DATADESC() |
|
#endif |
|
|
|
#define TF_GRENADE_LAUNCER_MIN_VEL 1200 |
|
|
|
#define TF_DETONATE_MODE_AIR 2 |
|
|
|
#define TF_WEAPON_CANNON_CHARGE_SOUND "Weapon_LooseCannon.Charge" |
|
|
|
//============================================================================= |
|
// |
|
// Weapon Grenade Launcher functions. |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : - |
|
//----------------------------------------------------------------------------- |
|
CTFGrenadeLauncher::CTFGrenadeLauncher() |
|
{ |
|
m_bReloadsSingly = true; |
|
|
|
#ifdef CLIENT_DLL |
|
m_pCannonFuseSparkEffect = NULL; |
|
m_pCannonCharge = NULL; |
|
#endif // CLIENT_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : - |
|
//----------------------------------------------------------------------------- |
|
CTFGrenadeLauncher::~CTFGrenadeLauncher() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::Spawn( void ) |
|
{ |
|
m_iAltFireHint = HINT_ALTFIRE_GRENADELAUNCHER; |
|
BaseClass::Spawn(); |
|
|
|
ResetDetonateTime(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Reset the charge when we holster |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrenadeLauncher::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
ResetDetonateTime(); |
|
return BaseClass::Holster( pSwitchingTo ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Reset the charge when we deploy |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrenadeLauncher::Deploy( void ) |
|
{ |
|
ResetDetonateTime(); |
|
return BaseClass::Deploy(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFGrenadeLauncher::GetMaxClip1( void ) const |
|
{ |
|
#ifdef _X360 |
|
return TF_GRENADE_LAUNCHER_XBOX_CLIP; |
|
#endif |
|
|
|
return BaseClass::GetMaxClip1(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFGrenadeLauncher::GetDefaultClip1( void ) const |
|
{ |
|
#ifdef _X360 |
|
return TF_GRENADE_LAUNCHER_XBOX_CLIP; |
|
#endif |
|
|
|
return BaseClass::GetDefaultClip1(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::PrimaryAttack( void ) |
|
{ |
|
// Check for ammunition. |
|
if ( m_iClip1 <= 0 && m_iClip1 != -1 ) |
|
return; |
|
|
|
// Are we capable of firing again? |
|
if ( m_flNextPrimaryAttack > gpGlobals->curtime ) |
|
return; |
|
|
|
if ( !CanAttack() ) |
|
{ |
|
ResetDetonateTime(); |
|
return; |
|
} |
|
|
|
m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; |
|
|
|
if ( CanCharge() ) |
|
{ |
|
if ( m_flDetonateTime == 0.f ) |
|
{ |
|
m_flDetonateTime = gpGlobals->curtime + GetMortarDetonateTimeLength(); |
|
SendWeaponAnim( ACT_VM_PULLBACK ); |
|
#ifdef CLIENT_DLL |
|
EmitSound( TF_WEAPON_CANNON_CHARGE_SOUND ); |
|
#endif // CLIENT_DLL |
|
} |
|
else |
|
{ |
|
#ifdef CLIENT_DLL |
|
StartChargeEffects(); |
|
#endif // CLIENT_DLL |
|
} |
|
} |
|
else |
|
{ |
|
LaunchGrenade(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::ItemPostFrame( void ) |
|
{ |
|
BaseClass::ItemPostFrame(); |
|
|
|
if ( m_flDetonateTime > 0.f ) |
|
{ |
|
if ( m_flDetonateTime > gpGlobals->curtime ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
// If we're not holding down the attack button, launch our grenade |
|
if ( m_iClip1 > 0 && !(pPlayer->m_nButtons & IN_ATTACK) ) |
|
{ |
|
LaunchGrenade(); |
|
} |
|
} |
|
else |
|
{ |
|
Misfire(); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::Misfire( void ) |
|
{ |
|
BaseClass::Misfire(); |
|
|
|
LaunchGrenade(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::WeaponIdle( void ) |
|
{ |
|
BaseClass::WeaponIdle(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::FireProjectileInternal( CTFPlayer* pTFPlayer ) |
|
{ |
|
#ifdef GAME_DLL |
|
CTFGrenadePipebombProjectile *pProjectile = static_cast<CTFGrenadePipebombProjectile*>( FireProjectile( pTFPlayer ) ); |
|
if ( pProjectile ) |
|
{ |
|
if ( GetDetonateMode() == TF_DETONATE_MODE_AIR ) |
|
{ |
|
pProjectile->m_bWallShatter = true; |
|
} |
|
|
|
if ( m_flDetonateTime > 0.f ) |
|
{ |
|
float flDetonateTimeLength = ( gpGlobals->curtime - GetChargeBeginTime() ); |
|
pProjectile->SetDetonateTimerLength( flDetonateTimeLength ); |
|
if ( flDetonateTimeLength == 0.f ) |
|
{ |
|
trace_t tr; |
|
UTIL_TraceLine( pProjectile->GetAbsOrigin(), pTFPlayer->EyePosition(), MASK_SOLID, pProjectile, COLLISION_GROUP_NONE, &tr ); |
|
pProjectile->Explode( &tr, GetDamageType() ); |
|
} |
|
} |
|
|
|
float flDetonationPenalty = 1.0f; |
|
CALL_ATTRIB_HOOK_FLOAT( flDetonationPenalty, grenade_detonation_damage_penalty ); |
|
if ( flDetonationPenalty != 1.0f ) |
|
{ |
|
// Setting the initial damage of a grenade lower will set its fused time damage lower |
|
// on contact detonations reset the damage to max |
|
pProjectile->SetDamage( pProjectile->GetDamage() * flDetonationPenalty ); |
|
} |
|
} |
|
#else |
|
FireProjectile( pTFPlayer ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::WeaponReset( void ) |
|
{ |
|
BaseClass::WeaponReset(); |
|
|
|
ResetDetonateTime(); |
|
|
|
m_iCurrentTube = 0; |
|
m_iGoalTube = 0; |
|
m_bCurrentAndGoalTubeEqual = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGrenadeLauncher::SendWeaponAnim( int iActivity ) |
|
{ |
|
// Client procedurally animates the barrel bone |
|
if ( iActivity == ACT_VM_PRIMARYATTACK ) |
|
{ |
|
m_iGoalTube = ( m_iCurrentTube + 1 ) % TF_TUBE_COUNT; |
|
m_flBarrelRotateBeginTime = gpGlobals->curtime; |
|
} |
|
|
|
// When we start firing, play the startup firing anim first |
|
if ( iActivity == ACT_VM_PRIMARYATTACK ) |
|
{ |
|
// If we're already playing the fire anim, let it continue. It loops. |
|
if ( GetActivity() == ACT_VM_PRIMARYATTACK ) |
|
return true; |
|
|
|
// Otherwise, play the start it |
|
return BaseClass::SendWeaponAnim( ACT_VM_PRIMARYATTACK ); |
|
} |
|
|
|
return BaseClass::SendWeaponAnim( iActivity ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::PostFire() |
|
{ |
|
// Set next attack times. |
|
float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay ); |
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; |
|
|
|
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); |
|
|
|
// Check the reload mode and behave appropriately. |
|
if ( m_bReloadsSingly ) |
|
{ |
|
m_iReloadMode.Set( TF_RELOAD_START ); |
|
} |
|
|
|
#ifndef CLIENT_DLL |
|
if ( CanCharge() ) |
|
{ |
|
Vector vPosition; |
|
QAngle qAngles; |
|
if ( GetAttachment( "muzzle", vPosition, qAngles ) ) |
|
{ |
|
CPVSFilter filter( vPosition ); |
|
TE_TFParticleEffect( filter, 0.f, "loose_cannon_bang", PATTACH_POINT, this, "muzzle" ); |
|
} |
|
} |
|
#endif |
|
|
|
ResetDetonateTime(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::LaunchGrenade( void ) |
|
{ |
|
// Get the player owning the weapon. |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
CalcIsAttackCritical(); |
|
|
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK ); |
|
|
|
pPlayer->SetAnimation( PLAYER_ATTACK1 ); |
|
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); |
|
|
|
if ( !AutoFiresFullClipAllAtOnce() ) |
|
{ |
|
FireProjectileInternal( pPlayer ); |
|
} |
|
else |
|
{ |
|
int nCurrentClipSize = m_iClip1; |
|
m_nLauncherSlot = 0; |
|
int iSeed = CBaseEntity::GetPredictionRandomSeed() & 255; |
|
QAngle punchAngle = pPlayer->GetPunchAngle(); |
|
for ( int i=0; i<nCurrentClipSize; ++i, ++iSeed ) |
|
{ |
|
RandomSeed( iSeed ); |
|
FireProjectileInternal( pPlayer ); |
|
if ( i == 0 ) |
|
{ |
|
punchAngle = pPlayer->GetPunchAngle(); |
|
} |
|
} |
|
pPlayer->SetPunchAngle( punchAngle ); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); |
|
StopSound( TF_WEAPON_CANNON_CHARGE_SOUND ); |
|
#else |
|
pPlayer->SpeakWeaponFire(); |
|
CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); |
|
#endif |
|
|
|
PostFire(); |
|
|
|
if ( TFGameRules()->GameModeUsesUpgrades() ) |
|
{ |
|
PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" ); |
|
} |
|
} |
|
|
|
void CTFGrenadeLauncher::AddDonkVictim( const CBaseEntity* pVictim ) |
|
{ |
|
// Clear out old donk victims |
|
FOR_EACH_VEC_BACK( m_vecDonkVictims, i ) |
|
{ |
|
if( m_vecDonkVictims[i].m_flExpireTime <= gpGlobals->curtime ) |
|
{ |
|
m_vecDonkVictims.Remove( i ); |
|
} |
|
} |
|
|
|
// Add new donk victim |
|
Donks_t& donk = m_vecDonkVictims[ m_vecDonkVictims.AddToTail() ]; |
|
donk.m_hVictim.Set( pVictim ); |
|
donk.m_flExpireTime = gpGlobals->curtime + tf_double_donk_window.GetFloat(); |
|
} |
|
|
|
|
|
bool CTFGrenadeLauncher::IsDoubleDonk( const CBaseEntity* pVictim ) const |
|
{ |
|
if( GetWeaponID() != TF_WEAPON_CANNON ) |
|
return false; |
|
|
|
// Check each donk victim to see if we've donked them recently enough to |
|
// score a "double-donk" |
|
FOR_EACH_VEC( m_vecDonkVictims, i ) |
|
{ |
|
if( gpGlobals->curtime < m_vecDonkVictims[i].m_flExpireTime && m_vecDonkVictims[i].m_hVictim.Get() == pVictim ) |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
float CTFGrenadeLauncher::GetProjectileSpeed( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
|
|
if ( pOwner && pOwner->m_Shared.GetCarryingRuneType() == RUNE_PRECISION ) |
|
return 3000.f; |
|
|
|
float flLaunchSpeed = TF_GRENADE_LAUNCER_MIN_VEL; |
|
CALL_ATTRIB_HOOK_FLOAT( flLaunchSpeed, mult_projectile_speed ); |
|
return flLaunchSpeed; |
|
} |
|
|
|
int CTFGrenadeLauncher::GetDetonateMode( void ) const |
|
{ |
|
int iMode = 0; |
|
CALL_ATTRIB_HOOK_INT( iMode, set_detonate_mode ); |
|
return iMode; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Detonate this demoman's pipebombs |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::SecondaryAttack( void ) |
|
{ |
|
#ifdef GAME_DLL |
|
|
|
if ( !CanAttack() ) |
|
return; |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
pOwner->DoClassSpecialSkill(); |
|
|
|
#endif |
|
} |
|
|
|
bool CTFGrenadeLauncher::Reload( void ) |
|
{ |
|
return BaseClass::Reload(); |
|
} |
|
|
|
|
|
void CTFGrenadeLauncher::FireFullClipAtOnce( void ) |
|
{ |
|
m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; |
|
|
|
LaunchGrenade(); |
|
} |
|
|
|
|
|
bool CTFGrenadeLauncher::CanCharge( void ) |
|
{ |
|
if ( GetWeaponID() == TF_WEAPON_CANNON ) |
|
{ |
|
return GetMortarDetonateTimeLength() > 0.f; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
float CTFGrenadeLauncher::GetChargeBeginTime( void ) |
|
{ |
|
// Inverse begin time logic to get charge bar to decrease from a full bar instead of increase from an empty bar |
|
float flMortarDetonateTimeLength = GetMortarDetonateTimeLength(); |
|
float flModDetonateTimeLength = flMortarDetonateTimeLength; |
|
if ( m_flDetonateTime > 0.f ) |
|
{ |
|
flModDetonateTimeLength = Clamp( m_flDetonateTime - gpGlobals->curtime, 0.f, flMortarDetonateTimeLength ); |
|
} |
|
|
|
return gpGlobals->curtime - flModDetonateTimeLength; |
|
} |
|
|
|
|
|
float CTFGrenadeLauncher::GetChargeMaxTime( void ) |
|
{ |
|
return GetMortarDetonateTimeLength(); |
|
} |
|
|
|
|
|
void CTFGrenadeLauncher::ResetDetonateTime() |
|
{ |
|
m_flDetonateTime = 0.f; |
|
|
|
#ifdef CLIENT_DLL |
|
StopChargeEffects(); |
|
#endif // CLIENT_DLL |
|
} |
|
|
|
|
|
float CTFGrenadeLauncher::GetMortarDetonateTimeLength() |
|
{ |
|
float flMortarDetonateTimeLength = 0.f; |
|
CALL_ATTRIB_HOOK_FLOAT( flMortarDetonateTimeLength, grenade_launcher_mortar_mode ); |
|
return flMortarDetonateTimeLength; |
|
} |
|
|
|
|
|
#ifdef CLIENT_DLL |
|
void CTFGrenadeLauncher::StartChargeEffects() |
|
{ |
|
if ( !m_pCannonFuseSparkEffect ) |
|
{ |
|
m_pCannonFuseSparkEffect = ParticleProp()->Create( "loose_cannon_sparks", PATTACH_POINT_FOLLOW, "cannon_fuse" ); |
|
} |
|
if ( !m_pCannonCharge ) |
|
{ |
|
m_pCannonCharge = ParticleProp()->Create( "loose_cannon_buildup_smoke3", PATTACH_POINT_FOLLOW, "muzzle" ); |
|
} |
|
} |
|
|
|
|
|
void CTFGrenadeLauncher::StopChargeEffects() |
|
{ |
|
if ( m_pCannonFuseSparkEffect ) |
|
{ |
|
ParticleProp()->StopEmission( m_pCannonFuseSparkEffect ); |
|
m_pCannonFuseSparkEffect = NULL; |
|
} |
|
if ( m_pCannonCharge ) |
|
{ |
|
ParticleProp()->StopEmission( m_pCannonCharge ); |
|
m_pCannonCharge = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CStudioHdr *CTFGrenadeLauncher::OnNewModel( void ) |
|
{ |
|
CStudioHdr *hdr = BaseClass::OnNewModel(); |
|
|
|
m_iBarrelBone = LookupBone( "procedural_chamber" ); |
|
|
|
return hdr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask ) |
|
{ |
|
BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask ); |
|
|
|
if (m_iBarrelBone != -1) |
|
{ |
|
UpdateBarrelMovement(); |
|
|
|
AngleQuaternion( RadianEuler( 0, 0, m_flBarrelAngle ), q[m_iBarrelBone] ); |
|
} |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: For third person weapons. |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::OnDataChanged( DataUpdateType_t type ) |
|
{ |
|
if ( m_bCurrentAndGoalTubeEqual && m_iCurrentTube != m_iGoalTube ) |
|
m_flBarrelRotateBeginTime = gpGlobals->curtime; |
|
|
|
m_bCurrentAndGoalTubeEqual = ( m_iCurrentTube == m_iGoalTube ); |
|
|
|
BaseClass::OnDataChanged( type ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Updates the velocity and position of the rotating barrel |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::UpdateBarrelMovement( void ) |
|
{ |
|
if ( m_iGoalTube != m_iCurrentTube ) |
|
{ |
|
float flPartialRotationDeg = 0.0f; |
|
|
|
const float tVal = ( gpGlobals->curtime - m_flBarrelRotateBeginTime ) / cProceduralBarrelRotationTime; |
|
|
|
if ( tVal < 1.0f ) |
|
{ |
|
Assert( cProceduralBarrelRotationAnimationPoints[ 0 ].x == 0.0f ); |
|
Assert( cProceduralBarrelRotationAnimationPoints[ ARRAYSIZE( cProceduralBarrelRotationAnimationPoints ) - 1 ].x == 1.0f ); |
|
|
|
const Vector* pFirst = NULL; |
|
const Vector* pSecond = NULL; |
|
|
|
for ( int i = 1; i < ARRAYSIZE( cProceduralBarrelRotationAnimationPoints ); ++i ) |
|
{ |
|
// Need to be increasing in time, or we won't find the right span. |
|
Assert( cProceduralBarrelRotationAnimationPoints[ i - 1 ].x < cProceduralBarrelRotationAnimationPoints[ i ].x ); |
|
|
|
if ( tVal <= cProceduralBarrelRotationAnimationPoints[ i ].x ) |
|
{ |
|
pFirst = &cProceduralBarrelRotationAnimationPoints[ i - 1 ]; |
|
pSecond = &cProceduralBarrelRotationAnimationPoints[ i ]; |
|
break; |
|
} |
|
} |
|
|
|
Assert( pFirst && pSecond ); |
|
float flPartialT = ( tVal - pFirst->x ) / ( pSecond->x - pFirst->x ); |
|
flPartialRotationDeg = Hermite_Spline( pFirst->y, pSecond->y, pFirst->z, pSecond->z, flPartialT ); |
|
} |
|
else |
|
{ |
|
m_iCurrentTube = m_iGoalTube; |
|
m_bCurrentAndGoalTubeEqual = true; |
|
} |
|
|
|
const float flBaseDeg = 60.0f * m_iCurrentTube; |
|
m_flBarrelAngle = DEG2RAD( flBaseDeg + flPartialRotationDeg ); |
|
} |
|
} |
|
|
|
void CTFGrenadeLauncher::ViewModelAttachmentBlending( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask ) |
|
{ |
|
int iBarrelBone = Studio_BoneIndexByName( hdr, "procedural_chamber" ); |
|
|
|
// Assert( iBarrelBone != -1 ); |
|
|
|
if ( iBarrelBone != -1 ) |
|
{ |
|
if ( hdr->boneFlags( iBarrelBone ) & boneMask ) |
|
{ |
|
RadianEuler a; |
|
QuaternionAngles( q[ iBarrelBone ], a ); |
|
|
|
a.z = m_flBarrelAngle; |
|
|
|
AngleQuaternion( a, q[ iBarrelBone ] ); |
|
} |
|
} |
|
|
|
} |
|
|
|
#endif //CLIENT_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// won't be called for w_ version of the model, so this isn't getting updated twice |
|
//----------------------------------------------------------------------------- |
|
void CTFGrenadeLauncher::ItemPreFrame( void ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
UpdateBarrelMovement(); |
|
#endif |
|
|
|
#ifdef GAME_DLL |
|
if ( gpGlobals->curtime > m_flBarrelRotateBeginTime + cProceduralBarrelRotationTime ) |
|
m_iCurrentTube = m_iGoalTube; |
|
#endif |
|
|
|
BaseClass::ItemPreFrame(); |
|
}
|
|
|