//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============ // // Weapons. // //============================================================================= #include "cbase.h" #include "in_buttons.h" #include "takedamageinfo.h" #include "tf_weaponbase.h" #include "ammodef.h" #include "tf_gamerules.h" #include "eventlist.h" // Server specific. #if !defined( CLIENT_DLL ) #include "tf_player.h" // Client specific. #else #include "vgui/ISurface.h" #include "vgui_controls/Controls.h" #include "c_tf_player.h" #include "tf_viewmodel.h" #include "hud_crosshair.h" #include "c_tf_playerresource.h" #include "clientmode_tf.h" #include "r_efx.h" #include "dlight.h" #include "effect_dispatch_data.h" #include "c_te_effect_dispatch.h" #include "toolframework_client.h" // for spy material proxy #include "proxyentity.h" #include "materialsystem/imaterial.h" #include "materialsystem/imaterialvar.h" extern CTFWeaponInfo *GetTFWeaponInfo( int iWeapon ); #endif ConVar tf_weapon_criticals( "tf_weapon_criticals", "1", FCVAR_NOTIFY | FCVAR_REPLICATED, "Whether or not random crits are enabled." ); extern ConVar tf_useparticletracers; //============================================================================= // // Global functions. // //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool IsAmmoType( int iAmmoType, const char *pAmmoName ) { return GetAmmoDef()->Index( pAmmoName ) == iAmmoType; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity ) { int i, j, k; trace_t tmpTrace; Vector vecEnd; float distance = 1e6f; Vector minmaxs[2] = {mins, maxs}; Vector vecHullEnd = tr.endpos; vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2); UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace ); if ( tmpTrace.fraction < 1.0 ) { tr = tmpTrace; return; } for ( i = 0; i < 2; i++ ) { for ( j = 0; j < 2; j++ ) { for ( k = 0; k < 2; k++ ) { vecEnd.x = vecHullEnd.x + minmaxs[i][0]; vecEnd.y = vecHullEnd.y + minmaxs[j][1]; vecEnd.z = vecHullEnd.z + minmaxs[k][2]; UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace ); if ( tmpTrace.fraction < 1.0 ) { float thisDistance = (tmpTrace.endpos - vecSrc).Length(); if ( thisDistance < distance ) { tr = tmpTrace; distance = thisDistance; } } } } } } //============================================================================= // // TFWeaponBase tables. // IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBase, DT_TFWeaponBase ) BEGIN_NETWORK_TABLE( CTFWeaponBase, DT_TFWeaponBase ) // Client specific. #ifdef CLIENT_DLL RecvPropBool( RECVINFO( m_bLowered ) ), RecvPropInt( RECVINFO( m_iReloadMode ) ), RecvPropBool( RECVINFO( m_bResetParity ) ), RecvPropBool( RECVINFO( m_bReloadedThroughAnimEvent ) ), // Server specific. #else SendPropBool( SENDINFO( m_bLowered ) ), SendPropBool( SENDINFO( m_bResetParity ) ), SendPropInt( SENDINFO( m_iReloadMode ), 4, SPROP_UNSIGNED ), SendPropBool( SENDINFO( m_bReloadedThroughAnimEvent ) ), // World models have no animations so don't send these. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ), #endif END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CTFWeaponBase ) #ifdef CLIENT_DLL DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE ), DEFINE_PRED_FIELD( m_bLowered, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_iReloadMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bReloadedThroughAnimEvent, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), #endif END_PREDICTION_DATA() LINK_ENTITY_TO_CLASS( tf_weapon_base, CTFWeaponBase ); // Server specific. #if !defined( CLIENT_DLL ) BEGIN_DATADESC( CTFWeaponBase ) DEFINE_FUNCTION( FallThink ) END_DATADESC() // Client specific #else ConVar cl_crosshaircolor( "cl_crosshaircolor", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); ConVar cl_dynamiccrosshair( "cl_dynamiccrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); ConVar cl_scalecrosshair( "cl_scalecrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); ConVar cl_crosshairalpha( "cl_crosshairalpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); int g_iScopeTextureID = 0; int g_iScopeDustTextureID = 0; #endif //============================================================================= // // TFWeaponBase shared functions. // // ----------------------------------------------------------------------------- // Purpose: Constructor. // ----------------------------------------------------------------------------- CTFWeaponBase::CTFWeaponBase() { SetPredictionEligible( true ); // Nothing collides with these, but they get touch calls. AddSolidFlags( FSOLID_TRIGGER ); // Weapons can fire underwater. m_bFiresUnderwater = true; m_bAltFiresUnderwater = true; // Initialize the weapon modes. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; m_iReloadMode.Set( TF_RELOAD_START ); m_iAltFireHint = 0; m_bInAttack = false; m_bInAttack2 = false; m_flCritTime = 0; m_flLastCritCheckTime = 0; m_iLastCritCheckFrame = 0; m_bCurrentAttackIsCrit = false; m_iCurrentSeed = -1; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- void CTFWeaponBase::Spawn() { // Base class spawn. BaseClass::Spawn(); // Set this here to allow players to shoot dropped weapons. SetCollisionGroup( COLLISION_GROUP_WEAPON ); // Get the weapon information. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( GetClassname() ); Assert( hWpnInfo != GetInvalidWeaponInfoHandle() ); CTFWeaponInfo *pWeaponInfo = dynamic_cast( GetFileWeaponInfoFromHandle( hWpnInfo ) ); Assert( pWeaponInfo && "Failed to get CTFWeaponInfo in weapon spawn" ); m_pWeaponInfo = pWeaponInfo; if ( GetPlayerOwner() ) { ChangeTeam( GetPlayerOwner()->GetTeamNumber() ); } #ifdef GAME_DLL // Move it up a little bit, otherwise it'll be at the guy's feet, and its sound origin // will be in the ground so its EmitSound calls won't do anything. Vector vecOrigin = GetAbsOrigin(); SetAbsOrigin( Vector( vecOrigin.x, vecOrigin.y, vecOrigin.z + 5.0f ) ); #endif m_szTracerName[0] = '\0'; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- void CTFWeaponBase::FallInit( void ) { } //----------------------------------------------------------------------------- // Purpose: // Input : - //----------------------------------------------------------------------------- void CTFWeaponBase::Precache() { BaseClass::Precache(); if ( GetMuzzleFlashModel() ) { PrecacheModel( GetMuzzleFlashModel() ); } const CTFWeaponInfo *pTFInfo = &GetTFWpnData(); if ( pTFInfo->m_szExplosionSound && pTFInfo->m_szExplosionSound[0] ) { CBaseEntity::PrecacheScriptSound( pTFInfo->m_szExplosionSound ); } if ( pTFInfo->m_szBrassModel[0] ) { PrecacheModel( pTFInfo->m_szBrassModel ); } if ( pTFInfo->m_szMuzzleFlashParticleEffect && pTFInfo->m_szMuzzleFlashParticleEffect[0] ) { PrecacheParticleSystem( pTFInfo->m_szMuzzleFlashParticleEffect ); } if ( pTFInfo->m_szExplosionEffect && pTFInfo->m_szExplosionEffect[0] ) { PrecacheParticleSystem( pTFInfo->m_szExplosionEffect ); } if ( pTFInfo->m_szExplosionPlayerEffect && pTFInfo->m_szExplosionPlayerEffect[0] ) { PrecacheParticleSystem( pTFInfo->m_szExplosionPlayerEffect ); } if ( pTFInfo->m_szExplosionWaterEffect && pTFInfo->m_szExplosionWaterEffect[0] ) { PrecacheParticleSystem( pTFInfo->m_szExplosionWaterEffect ); } if ( pTFInfo->m_szTracerEffect && pTFInfo->m_szTracerEffect[0] ) { char pTracerEffect[128]; char pTracerEffectCrit[128]; Q_snprintf( pTracerEffect, sizeof(pTracerEffect), "%s_red", pTFInfo->m_szTracerEffect ); Q_snprintf( pTracerEffectCrit, sizeof(pTracerEffectCrit), "%s_red_crit", pTFInfo->m_szTracerEffect ); PrecacheParticleSystem( pTracerEffect ); PrecacheParticleSystem( pTracerEffectCrit ); Q_snprintf( pTracerEffect, sizeof(pTracerEffect), "%s_blue", pTFInfo->m_szTracerEffect ); Q_snprintf( pTracerEffectCrit, sizeof(pTracerEffectCrit), "%s_blue_crit", pTFInfo->m_szTracerEffect ); PrecacheParticleSystem( pTracerEffect ); PrecacheParticleSystem( pTracerEffectCrit ); } } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- const CTFWeaponInfo &CTFWeaponBase::GetTFWpnData() const { const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); const CTFWeaponInfo *pTFInfo = dynamic_cast< const CTFWeaponInfo* >( pWeaponInfo ); Assert( pTFInfo ); return *pTFInfo; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- int CTFWeaponBase::GetWeaponID( void ) const { Assert( false ); return TF_WEAPON_NONE; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- bool CTFWeaponBase::IsWeapon( int iWeapon ) const { return GetWeaponID() == iWeapon; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- const char *CTFWeaponBase::GetViewModel( int iViewModel ) const { if ( GetPlayerOwner() == NULL ) { return BaseClass::GetViewModel(); } return GetTFWpnData().szViewModel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::Drop( const Vector &vecVelocity ) { #ifndef CLIENT_DLL if ( m_iAltFireHint ) { CBasePlayer *pPlayer = GetPlayerOwner(); if ( pPlayer ) { pPlayer->StopHintTimer( m_iAltFireHint ); } } #endif BaseClass::Drop( vecVelocity ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFWeaponBase::Holster( CBaseCombatWeapon *pSwitchingTo ) { #ifndef CLIENT_DLL if ( m_iAltFireHint ) { CBasePlayer *pPlayer = GetPlayerOwner(); if ( pPlayer ) { pPlayer->StopHintTimer( m_iAltFireHint ); } } #endif return BaseClass::Holster( pSwitchingTo ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFWeaponBase::Deploy( void ) { #ifndef CLIENT_DLL if ( m_iAltFireHint ) { CBasePlayer *pPlayer = GetPlayerOwner(); if ( pPlayer ) { pPlayer->StartHintTimer( m_iAltFireHint ); } } #endif float flOriginalPrimaryAttack = m_flNextPrimaryAttack; bool bDeploy = BaseClass::Deploy(); if ( bDeploy ) { // Overrides the anim length for calculating ready time. // Don't override primary attacks that are already further out than this. This prevents // people exploiting weapon switches to allow weapons to fire faster. float flDeployTime = 0.67; m_flNextPrimaryAttack = MAX( flOriginalPrimaryAttack, gpGlobals->curtime + flDeployTime ); CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); if (!pPlayer) return false; pPlayer->SetNextAttack( m_flNextPrimaryAttack ); } return bDeploy; } //----------------------------------------------------------------------------- // Purpose: // Output : //----------------------------------------------------------------------------- void CTFWeaponBase::PrimaryAttack( void ) { // Set the weapon mode. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; if ( !CanAttack() ) return; BaseClass::PrimaryAttack(); if ( m_bReloadsSingly ) { m_iReloadMode.Set( TF_RELOAD_START ); } } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- void CTFWeaponBase::SecondaryAttack( void ) { // Set the weapon mode. m_iWeaponMode = TF_WEAPON_SECONDARY_MODE; // Don't hook secondary for now. return; } //----------------------------------------------------------------------------- // Purpose: Most calls use the prediction seed //----------------------------------------------------------------------------- void CTFWeaponBase::CalcIsAttackCritical( void) { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return; if ( gpGlobals->framecount == m_iLastCritCheckFrame ) return; m_iLastCritCheckFrame = gpGlobals->framecount; // if base entity seed has changed since last calculation, reseed with new seed int iSeed = CBaseEntity::GetPredictionRandomSeed(); if ( iSeed != m_iCurrentSeed ) { m_iCurrentSeed = iSeed; RandomSeed( m_iCurrentSeed ); } if ( ( TFGameRules()->State_Get() == GR_STATE_TEAM_WIN ) && ( TFGameRules()->GetWinningTeam() == pPlayer->GetTeamNumber() ) ) { m_bCurrentAttackIsCrit = true; } else { // call the weapon-specific helper method m_bCurrentAttackIsCrit = CalcIsAttackCriticalHelper(); } } //----------------------------------------------------------------------------- // Purpose: Weapon-specific helper method to calculate if attack is crit //----------------------------------------------------------------------------- bool CTFWeaponBase::CalcIsAttackCriticalHelper() { if ( !tf_weapon_criticals.GetBool() ) return false; CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return false; float flPlayerCritMult = pPlayer->GetCritMult(); if ( !CanFireCriticalShot() ) return false; if ( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_bUseRapidFireCrits ) { if ( m_flCritTime > gpGlobals->curtime ) return true; // only perform one crit check per second for rapid fire weapons if ( gpGlobals->curtime < m_flLastCritCheckTime + 1.0f ) return false; m_flLastCritCheckTime = gpGlobals->curtime; // get the total crit chance (ratio of total shots fired we want to be crits) float flTotalCritChance = clamp( TF_DAMAGE_CRIT_CHANCE_RAPID * flPlayerCritMult, 0.01f, 0.99f ); // get the fixed amount of time that we start firing crit shots for float flCritDuration = TF_DAMAGE_CRIT_DURATION_RAPID; // calculate the amount of time, on average, that we want to NOT fire crit shots for in order to achive the total crit chance we want float flNonCritDuration = ( flCritDuration / flTotalCritChance ) - flCritDuration; // calculate the chance per second of non-crit fire that we should transition into critting such that on average we achieve the total crit chance we want float flStartCritChance = 1 / flNonCritDuration; // see if we should start firing crit shots int iRandom = RandomInt( 0, WEAPON_RANDOM_RANGE-1 ); if ( iRandom <= flStartCritChance * WEAPON_RANDOM_RANGE ) { m_flCritTime = gpGlobals->curtime + TF_DAMAGE_CRIT_DURATION_RAPID; return true; } return false; } else { // single-shot weapon, just use random pct per shot return ( RandomInt( 0.0, WEAPON_RANDOM_RANGE-1 ) < ( TF_DAMAGE_CRIT_CHANCE * flPlayerCritMult ) * WEAPON_RANDOM_RANGE ); } } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CTFWeaponBase::Reload( void ) { // If we're not already reloading, check to see if we have ammo to reload and check to see if we are max ammo. if ( m_iReloadMode == TF_RELOAD_START ) { // If I don't have any spare ammo, I can't reload if ( GetOwner()->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) return false; if ( Clip1() >= GetMaxClip1()) return false; } // Reload one object at a time. if ( m_bReloadsSingly ) return ReloadSingly(); // Normal reload. DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::AbortReload( void ) { BaseClass::AbortReload(); m_iReloadMode.Set( TF_RELOAD_START ); } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CTFWeaponBase::ReloadSingly( void ) { // Don't reload. if ( m_flNextPrimaryAttack > gpGlobals->curtime ) return false; // Get the current player. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return false; // check to see if we're ready to reload switch ( m_iReloadMode ) { case TF_RELOAD_START: { // Play weapon and player animations. if ( SendWeaponAnim( ACT_RELOAD_START ) ) { SetReloadTimer( SequenceDuration() ); } else { // Update the reload timers with script values. UpdateReloadTimers( true ); } // Next reload the shells. m_iReloadMode.Set( TF_RELOADING ); m_iReloadStartClipAmount = Clip1(); return true; } case TF_RELOADING: { // Did we finish the reload start? Now we can reload a rocket. if ( m_flTimeWeaponIdle > gpGlobals->curtime ) return false; // Play weapon reload animations and sound. if ( Clip1() == m_iReloadStartClipAmount ) { pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); } else { pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_LOOP ); } m_bReloadedThroughAnimEvent = false; if ( SendWeaponAnim( ACT_VM_RELOAD ) ) { if ( GetWeaponID() == TF_WEAPON_GRENADELAUNCHER ) { SetReloadTimer( GetTFWpnData().m_WeaponData[TF_WEAPON_PRIMARY_MODE].m_flTimeReload ); } else { SetReloadTimer( SequenceDuration() ); } } else { // Update the reload timers. UpdateReloadTimers( false ); } #ifndef CLIENT_DLL WeaponSound( RELOAD ); #endif // Next continue to reload shells? m_iReloadMode.Set( TF_RELOADING_CONTINUE ); return true; } case TF_RELOADING_CONTINUE: { // Did we finish the reload start? Now we can finish reloading the rocket. if ( m_flTimeWeaponIdle > gpGlobals->curtime ) return false; // If we have ammo, remove ammo and add it to clip if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 && !m_bReloadedThroughAnimEvent ) { m_iClip1 = MIN( ( m_iClip1 + 1 ), GetMaxClip1() ); pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType ); } if ( Clip1() == GetMaxClip1() || pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) { m_iReloadMode.Set( TF_RELOAD_FINISH ); } else { m_iReloadMode.Set( TF_RELOADING ); } return true; } case TF_RELOAD_FINISH: default: { if ( SendWeaponAnim( ACT_RELOAD_FINISH ) ) { // We're done, allow primary attack as soon as we like //SetReloadTimer( SequenceDuration() ); } pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_END ); m_iReloadMode.Set( TF_RELOAD_START ); return true; } } } //----------------------------------------------------------------------------- // Purpose: // Input : *pEvent - // *pOperator - //----------------------------------------------------------------------------- void CTFWeaponBase::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) { if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) /*&& (pEvent->type & AE_TYPE_SERVER)*/ ) { if ( pEvent->event == AE_WPN_INCREMENTAMMO ) { if ( pOperator->GetAmmoCount( m_iPrimaryAmmoType ) > 0 && !m_bReloadedThroughAnimEvent ) { m_iClip1 = MIN( ( m_iClip1 + 1 ), GetMaxClip1() ); pOperator->RemoveAmmo( 1, m_iPrimaryAmmoType ); } m_bReloadedThroughAnimEvent = true; return; } } } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- bool CTFWeaponBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity ) { // The the owning local player. CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( !pPlayer ) return false; // Setup and check for reload. bool bReloadPrimary = false; bool bReloadSecondary = false; // If you don't have clips, then don't try to reload them. if ( UsesClipsForAmmo1() ) { // need to reload primary clip? int primary = MIN( iClipSize1 - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) ); if ( primary != 0 ) { bReloadPrimary = true; } } if ( UsesClipsForAmmo2() ) { // need to reload secondary clip? int secondary = MIN( iClipSize2 - m_iClip2, pPlayer->GetAmmoCount( m_iSecondaryAmmoType ) ); if ( secondary != 0 ) { bReloadSecondary = true; } } // We didn't reload. if ( !( bReloadPrimary || bReloadSecondary ) ) return false; #ifndef CLIENT_DLL // Play reload WeaponSound( RELOAD ); #endif // Play the player's reload animation pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); float flReloadTime; // First, see if we have a reload animation if ( SendWeaponAnim( iActivity ) ) { flReloadTime = SequenceDuration(); } else { // No reload animation. Use the script time. flReloadTime = GetTFWpnData().m_WeaponData[TF_WEAPON_PRIMARY_MODE].m_flTimeReload; if ( bReloadSecondary ) { flReloadTime = GetTFWpnData().m_WeaponData[TF_WEAPON_SECONDARY_MODE].m_flTimeReload; } } SetReloadTimer( flReloadTime ); m_bInReload = true; return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::UpdateReloadTimers( bool bStart ) { // Starting a reload? if ( bStart ) { // Get the reload start time. SetReloadTimer( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeReloadStart ); } // In reload. else { SetReloadTimer( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeReload ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::SetReloadTimer( float flReloadTime ) { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( !pPlayer ) return; float flTime = gpGlobals->curtime + flReloadTime; // Set next player attack time (weapon independent). pPlayer->m_flNextAttack = flTime; // Set next weapon attack times (based on reloading). m_flNextPrimaryAttack = flTime; // Don't push out secondary attack, because our secondary fire // systems are all separate from primary fire (sniper zooming, demoman pipebomb detonating, etc) //m_flNextSecondaryAttack = flTime; // Set next idle time (based on reloading). SetWeaponIdleTime( flTime ); } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- bool CTFWeaponBase::PlayEmptySound() { CPASAttenuationFilter filter( this ); filter.UsePredictionRules(); // TFTODO: Add default empty sound here! // EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" ); return false; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- void CTFWeaponBase::SendReloadEvents() { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( !pPlayer ) return; // Make the player play his reload animation. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::ItemBusyFrame( void ) { // Call into the base ItemBusyFrame. BaseClass::ItemBusyFrame(); CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); if ( !pOwner ) { return; } if ( (pOwner->m_nButtons & IN_ATTACK2) && m_bInReload == false && m_bInAttack2 == false ) { if ( pOwner->DoClassSpecialSkill() ) { m_flNextSecondaryAttack = gpGlobals->curtime + 0.5; } m_bInAttack2 = true; } else { m_bInAttack2 = false; } // Interrupt a reload on reload singly weapons. if ( m_bReloadsSingly ) { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( pPlayer ) { if ( pPlayer->m_nButtons & IN_ATTACK ) { if ( ( m_iReloadMode != TF_RELOAD_START ) && Clip1() > 0 ) { m_iReloadMode.Set( TF_RELOAD_START ); m_bInReload = false; pPlayer->m_flNextAttack = gpGlobals->curtime; m_flNextPrimaryAttack = gpGlobals->curtime; SetWeaponIdleTime( gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeIdle ); } } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::ItemPostFrame( void ) { CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); if ( !pOwner ) { return; } // debounce InAttack flags if ( m_bInAttack && !( pOwner->m_nButtons & IN_ATTACK ) ) { m_bInAttack = false; } if ( m_bInAttack2 && !( pOwner->m_nButtons & IN_ATTACK2 ) ) { m_bInAttack2 = false; } // If we're lowered, we're not allowed to fire if ( m_bLowered ) return; // Call the base item post frame. BaseClass::ItemPostFrame(); // Check for reload singly interrupts. if ( m_bReloadsSingly ) { ReloadSinglyPostFrame(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBase::ReloadSinglyPostFrame( void ) { if ( m_flTimeWeaponIdle > gpGlobals->curtime ) return; // if the clip is empty and we have ammo remaining, if ( ( ( Clip1() == 0 ) && ( GetOwner()->GetAmmoCount(m_iPrimaryAmmoType) > 0 ) ) || // or we are already in the process of reloading but not finished ( m_iReloadMode != TF_RELOAD_START ) ) { // reload/continue reloading Reload(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFWeaponBase::WeaponShouldBeLowered( void ) { // Can't be in the middle of another animation if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE && GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE ) return false; if ( m_bLowered ) return true; return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFWeaponBase::Ready( void ) { // If we don't have the anim, just hide for now if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) { RemoveEffects( EF_NODRAW ); } m_bLowered = false; SendWeaponAnim( ACT_VM_IDLE ); // Prevent firing until our weapon is back up CTFPlayer *pPlayer = GetTFPlayerOwner(); pPlayer->m_flNextAttack = gpGlobals->curtime + SequenceDuration(); return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFWeaponBase::Lower( void ) { AbortReload(); // If we don't have the anim, just hide for now if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) { AddEffects( EF_NODRAW ); } m_bLowered = true; SendWeaponAnim( ACT_VM_IDLE_LOWERED ); return true; } //----------------------------------------------------------------------------- // Purpose: Show/hide weapon and corresponding view model if any // Input : visible - //----------------------------------------------------------------------------- void CTFWeaponBase::SetWeaponVisible( bool visible ) { if ( visible ) { RemoveEffects( EF_NODRAW ); } else { AddEffects( EF_NODRAW ); } #ifdef CLIENT_DLL UpdateVisibility(); #endif } //----------------------------------------------------------------------------- // Purpose: Allows the weapon to choose proper weapon idle animation //----------------------------------------------------------------------------- void CTFWeaponBase::WeaponIdle( void ) { //See if we should idle high or low if ( WeaponShouldBeLowered() ) { // Move to lowered position if we're not there yet if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED && GetActivity() != ACT_TRANSITION ) { SendWeaponAnim( ACT_VM_IDLE_LOWERED ); } else if ( HasWeaponIdleTimeElapsed() ) { // Keep idling low SendWeaponAnim( ACT_VM_IDLE_LOWERED ); } } else { // See if we need to raise immediately if ( GetActivity() == ACT_VM_IDLE_LOWERED ) { SendWeaponAnim( ACT_VM_IDLE ); } else if ( HasWeaponIdleTimeElapsed() ) { if ( !( m_bReloadsSingly && m_iReloadMode != TF_RELOAD_START ) ) { SendWeaponAnim( ACT_VM_IDLE ); m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); } } } } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- const char *CTFWeaponBase::GetMuzzleFlashModel( void ) { const char *pszModel = GetTFWpnData().m_szMuzzleFlashModel; if ( Q_strlen( pszModel ) > 0 ) { return pszModel; } return NULL; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- const char *CTFWeaponBase::GetMuzzleFlashParticleEffect( void ) { const char *pszPEffect = GetTFWpnData().m_szMuzzleFlashParticleEffect; if ( Q_strlen( pszPEffect ) > 0 ) { return pszPEffect; } return NULL; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- float CTFWeaponBase::GetMuzzleFlashModelLifetime( void ) { return GetTFWpnData().m_flMuzzleFlashModelDuration; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *CTFWeaponBase::GetTracerType( void ) { if ( tf_useparticletracers.GetBool() && GetTFWpnData().m_szTracerEffect && GetTFWpnData().m_szTracerEffect[0] ) { if ( !m_szTracerName[0] ) { Q_snprintf( m_szTracerName, MAX_TRACER_NAME, "%s_%s", GetTFWpnData().m_szTracerEffect, (GetOwner() && GetOwner()->GetTeamNumber() == TF_TEAM_RED ) ? "red" : "blue" ); } return m_szTracerName; } if ( GetWeaponID() == TF_WEAPON_MINIGUN ) return "BrightTracer"; return BaseClass::GetTracerType(); } //============================================================================= // // TFWeaponBase functions (Server specific). // #if !defined( CLIENT_DLL ) // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- void CTFWeaponBase::CheckRespawn() { // Do not respawn. return; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- CBaseEntity *CTFWeaponBase::Respawn() { // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code // will decide when to make the weapon visible and touchable. CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() ); if ( pNewWeapon ) { pNewWeapon->AddEffects( EF_NODRAW );// invisible for now pNewWeapon->SetTouch( NULL );// no touch pNewWeapon->SetThink( &CTFWeaponBase::AttemptToMaterialize ); UTIL_DropToFloor( this, MASK_SOLID ); // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, // but when it should respawn is based on conditions belonging to the weapon that was taken. pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) ); } else { Msg( "Respawn failed to create %s!\n", GetClassname() ); } return pNewWeapon; } // ----------------------------------------------------------------------------- // Purpose: Make a weapon visible and tangible. // ----------------------------------------------------------------------------- void CTFWeaponBase::Materialize() { if ( IsEffectActive( EF_NODRAW ) ) { RemoveEffects( EF_NODRAW ); DoMuzzleFlash(); } AddSolidFlags( FSOLID_TRIGGER ); SetThink ( &CTFWeaponBase::SUB_Remove ); SetNextThink( gpGlobals->curtime + 1 ); } // ----------------------------------------------------------------------------- // Purpose: The item is trying to materialize, should it do so now or wait longer? // ----------------------------------------------------------------------------- void CTFWeaponBase::AttemptToMaterialize() { float flTime = g_pGameRules->FlWeaponTryRespawn( this ); if ( flTime == 0 ) { Materialize(); return; } SetNextThink( gpGlobals->curtime + flTime ); } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- void CTFWeaponBase::SetDieThink( bool bDie ) { if( bDie ) { SetContextThink( &CTFWeaponBase::Die, gpGlobals->curtime + 30.0f, "DieContext" ); } else { SetContextThink( NULL, gpGlobals->curtime, "DieContext" ); } } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- void CTFWeaponBase::Die( void ) { UTIL_Remove( this ); } void CTFWeaponBase::WeaponReset( void ) { m_iReloadMode.Set( TF_RELOAD_START ); m_bResetParity = !m_bResetParity; } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- const Vector &CTFWeaponBase::GetBulletSpread( void ) { static Vector cone = VECTOR_CONE_15DEGREES; return cone; } #else void TE_DynamicLight( IRecipientFilter& filter, float delay, const Vector* org, int r, int g, int b, int exponent, float radius, float time, float decay, int nLightIndex = LIGHT_INDEX_TE_DYNAMIC ); //============================================================================= // // TFWeaponBase functions (Client specific). // void CTFWeaponBase::CreateMuzzleFlashEffects( C_BaseEntity *pAttachEnt, int nIndex ) { Vector vecOrigin; QAngle angAngles; int iMuzzleFlashAttachment = pAttachEnt->LookupAttachment( "muzzle" ); const char *pszMuzzleFlashEffect = NULL; const char *pszMuzzleFlashModel = GetMuzzleFlashModel(); const char *pszMuzzleFlashParticleEffect = GetMuzzleFlashParticleEffect(); // Pick the right muzzleflash (3rd / 1st person) CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if ( pLocalPlayer && (GetOwnerEntity() == pLocalPlayer || (pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pLocalPlayer->GetObserverTarget() == GetOwnerEntity())) ) { pszMuzzleFlashEffect = GetMuzzleFlashEffectName_1st(); } else { pszMuzzleFlashEffect = GetMuzzleFlashEffectName_3rd(); } // If we have an attachment, then stick a light on it. if ( iMuzzleFlashAttachment > 0 && (pszMuzzleFlashEffect || pszMuzzleFlashModel || pszMuzzleFlashParticleEffect ) ) { pAttachEnt->GetAttachment( iMuzzleFlashAttachment, vecOrigin, angAngles ); // Muzzleflash light /* CLocalPlayerFilter filter; TE_DynamicLight( filter, 0.0f, &vecOrigin, 255, 192, 64, 5, 70.0f, 0.05f, 70.0f / 0.05f, LIGHT_INDEX_MUZZLEFLASH ); */ if ( pszMuzzleFlashEffect ) { // Using an muzzle flash dispatch effect CEffectData muzzleFlashData; muzzleFlashData.m_vOrigin = vecOrigin; muzzleFlashData.m_vAngles = angAngles; muzzleFlashData.m_hEntity = pAttachEnt->GetRefEHandle(); muzzleFlashData.m_nAttachmentIndex = iMuzzleFlashAttachment; //muzzleFlashData.m_nHitBox = GetDODWpnData().m_iMuzzleFlashType; //muzzleFlashData.m_flMagnitude = GetDODWpnData().m_flMuzzleFlashScale; muzzleFlashData.m_flMagnitude = 0.2; DispatchEffect( pszMuzzleFlashEffect, muzzleFlashData ); } if ( pszMuzzleFlashModel ) { float flEffectLifetime = GetMuzzleFlashModelLifetime(); // Using a model as a muzzle flash. if ( m_hMuzzleFlashModel[nIndex] ) { // Increase the lifetime of the muzzleflash m_hMuzzleFlashModel[nIndex]->SetLifetime( flEffectLifetime ); } else { m_hMuzzleFlashModel[nIndex] = C_MuzzleFlashModel::CreateMuzzleFlashModel( pszMuzzleFlashModel, pAttachEnt, iMuzzleFlashAttachment, flEffectLifetime ); // FIXME: This is an incredibly brutal hack to get muzzle flashes positioned correctly for recording m_hMuzzleFlashModel[nIndex]->SetIs3rdPersonFlash( nIndex == 1 ); } } if ( pszMuzzleFlashParticleEffect ) { DispatchParticleEffect( pszMuzzleFlashParticleEffect, PATTACH_POINT_FOLLOW, pAttachEnt, "muzzle" ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CTFWeaponBase::InternalDrawModel( int flags ) { C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); bool bNotViewModel = ( (pOwner && !pOwner->IsLocalPlayer()) || C_BasePlayer::ShouldDrawLocalPlayer() ); bool bUseInvulnMaterial = (bNotViewModel && pOwner && pOwner->m_Shared.InCond( TF_COND_INVULNERABLE )); if ( bUseInvulnMaterial ) { modelrender->ForcedMaterialOverride( *pOwner->GetInvulnMaterialRef() ); } int ret = BaseClass::InternalDrawModel( flags ); if ( bUseInvulnMaterial ) { modelrender->ForcedMaterialOverride( NULL ); } return ret; } void CTFWeaponBase::ProcessMuzzleFlashEvent( void ) { C_BaseEntity *pAttachEnt; C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); if ( pOwner == NULL ) return; bool bDrawMuzzleFlashOnViewModel = ( pOwner->IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() ) || ( IsLocalPlayerSpectator() && GetSpectatorMode() == OBS_MODE_IN_EYE && GetSpectatorTarget() == pOwner->entindex() ); if ( bDrawMuzzleFlashOnViewModel ) { pAttachEnt = pOwner->GetViewModel(); } else { pAttachEnt = this; } { CRecordEffectOwner recordOwner( pOwner, bDrawMuzzleFlashOnViewModel ); CreateMuzzleFlashEffects( pAttachEnt, 0 ); } // Quasi-evil int nModelIndex = GetModelIndex(); int nWorldModelIndex = GetWorldModelIndex(); bool bInToolRecordingMode = ToolsEnabled() && clienttools->IsInRecordingMode(); if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex && pOwner->IsLocalPlayer() ) { CRecordEffectOwner recordOwner( pOwner, false ); SetModelIndex( nWorldModelIndex ); CreateMuzzleFlashEffects( this, 1 ); SetModelIndex( nModelIndex ); } } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- bool CTFWeaponBase::ShouldPredict() { if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() ) { return true; } return BaseClass::ShouldPredict(); } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- void CTFWeaponBase::WeaponReset( void ) { UpdateVisibility(); } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- void CTFWeaponBase::OnPreDataChanged( DataUpdateType_t type ) { BaseClass::OnPreDataChanged( type ); m_bOldResetParity = m_bResetParity; } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- void CTFWeaponBase::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ( GetPredictable() && !ShouldPredict() ) { ShutdownPredictable(); } //If its a world (held or dropped) model then set the correct skin color here. if ( m_nModelIndex == GetWorldModelIndex() ) { m_nSkin = GetSkin(); } if ( m_bResetParity != m_bOldResetParity ) { WeaponReset(); } //Here we go... //Since we can't get a repro for the invisible weapon thing, I'll fix it right up here: CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); //Our owner is alive if ( pOwner && pOwner->IsAlive() == true ) { //And he is NOT taunting if ( pOwner->m_Shared.InCond ( TF_COND_TAUNTING ) == false ) { //Then why the hell am I NODRAW? if ( pOwner->GetActiveWeapon() == this && IsEffectActive( EF_NODRAW ) ) { RemoveEffects( EF_NODRAW ); UpdateVisibility(); } } } } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- int CTFWeaponBase::CalcOverrideModelIndex( void ) { if ( ShouldDrawUsingViewModel() ) { return m_iViewModelIndex; } return GetWorldModelIndex(); } //----------------------------------------------------------------------------- // Purpose: // ---------------------------------------------------------------------------- int CTFWeaponBase::GetWorldModelIndex( void ) { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( pPlayer ) { // if we're a spy and we're disguised, we also // want to disguise our weapon's world model CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) return 0; int iLocalTeam = pLocalPlayer->GetTeamNumber(); // We only show disguise weapon to the enemy team when owner is disguised bool bUseDisguiseWeapon = ( pPlayer->GetTeamNumber() != iLocalTeam && iLocalTeam > LAST_SHARED_TEAM ); if ( bUseDisguiseWeapon && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) { int iModelIndex = pPlayer->m_Shared.GetDisguiseWeaponModelIndex(); Assert( iModelIndex != -1 ); return iModelIndex; } } return BaseClass::GetWorldModelIndex(); } bool CTFWeaponBase::ShouldDrawCrosshair( void ) { return GetTFWpnData().m_WeaponData[TF_WEAPON_PRIMARY_MODE].m_bDrawCrosshair; } void CTFWeaponBase::Redraw() { if ( ShouldDrawCrosshair() && g_pClientMode->ShouldDrawCrosshair() ) { DrawCrosshair(); } } #endif acttable_t CTFWeaponBase::m_acttablePrimary[] = { { ACT_MP_STAND_IDLE, ACT_MP_STAND_PRIMARY, false }, { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PRIMARY, false }, { ACT_MP_DEPLOYED, ACT_MP_DEPLOYED_PRIMARY, false }, { ACT_MP_RUN, ACT_MP_RUN_PRIMARY, false }, { ACT_MP_WALK, ACT_MP_WALK_PRIMARY, false }, { ACT_MP_AIRWALK, ACT_MP_AIRWALK_PRIMARY, false }, { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PRIMARY, false }, { ACT_MP_JUMP, ACT_MP_JUMP_PRIMARY, false }, { ACT_MP_JUMP_START, ACT_MP_JUMP_START_PRIMARY, false }, { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PRIMARY, false }, { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PRIMARY, false }, { ACT_MP_SWIM, ACT_MP_SWIM_PRIMARY, false }, { ACT_MP_SWIM_DEPLOYED, ACT_MP_SWIM_DEPLOYED_PRIMARY, false }, { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_PRIMARY, false }, { ACT_MP_ATTACK_STAND_PRIMARYFIRE_DEPLOYED, ACT_MP_ATTACK_STAND_PRIMARY_DEPLOYED, false }, { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_PRIMARY, false }, { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE_DEPLOYED, ACT_MP_ATTACK_CROUCH_PRIMARY_DEPLOYED, false }, { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_PRIMARY, false }, { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_PRIMARY, false }, { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_PRIMARY, false }, { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_PRIMARY_LOOP, false }, { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_PRIMARY_END, false }, { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_PRIMARY, false }, { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_PRIMARY_LOOP, false }, { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_PRIMARY_END, false }, { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_PRIMARY, false }, { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_PRIMARY_LOOP, false }, { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_PRIMARY_END, false }, { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_PRIMARY, false }, { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_PRIMARY_LOOP, false }, { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_PRIMARY_END, false }, { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_PRIMARY, false }, { ACT_MP_GRENADE1_DRAW, ACT_MP_PRIMARY_GRENADE1_DRAW, false }, { ACT_MP_GRENADE1_IDLE, ACT_MP_PRIMARY_GRENADE1_IDLE, false }, { ACT_MP_GRENADE1_ATTACK, ACT_MP_PRIMARY_GRENADE1_ATTACK, false }, { ACT_MP_GRENADE2_DRAW, ACT_MP_PRIMARY_GRENADE2_DRAW, false }, { ACT_MP_GRENADE2_IDLE, ACT_MP_PRIMARY_GRENADE2_IDLE, false }, { ACT_MP_GRENADE2_ATTACK, ACT_MP_PRIMARY_GRENADE2_ATTACK, false }, { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_PRIMARY, false }, { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY, false }, { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_PRIMARY, false }, { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_PRIMARY, false }, { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_PRIMARY, false }, { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_PRIMARY, false }, }; acttable_t CTFWeaponBase::m_acttableSecondary[] = { { ACT_MP_STAND_IDLE, ACT_MP_STAND_SECONDARY, false }, { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_SECONDARY, false }, { ACT_MP_RUN, ACT_MP_RUN_SECONDARY, false }, { ACT_MP_WALK, ACT_MP_WALK_SECONDARY, false }, { ACT_MP_AIRWALK, ACT_MP_AIRWALK_SECONDARY, false }, { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_SECONDARY, false }, { ACT_MP_JUMP, ACT_MP_JUMP_SECONDARY, false }, { ACT_MP_JUMP_START, ACT_MP_JUMP_START_SECONDARY, false }, { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_SECONDARY, false }, { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_SECONDARY, false }, { ACT_MP_SWIM, ACT_MP_SWIM_SECONDARY, false }, { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_SECONDARY, false }, { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_SECONDARY, false }, { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_SECONDARY, false }, { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_SECONDARY, false }, { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_SECONDARY, false }, { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_SECONDARY_LOOP, false }, { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_SECONDARY_END, false }, { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_SECONDARY, false }, { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_SECONDARY_LOOP,false }, { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_SECONDARY_END, false }, { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_SECONDARY, false }, { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_SECONDARY_LOOP, false }, { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_SECONDARY_END, false }, { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_SECONDARY, false }, { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_SECONDARY_LOOP, false }, { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_SECONDARY_END,false }, { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_SECONDARY, false }, { ACT_MP_GRENADE1_DRAW, ACT_MP_SECONDARY_GRENADE1_DRAW, false }, { ACT_MP_GRENADE1_IDLE, ACT_MP_SECONDARY_GRENADE1_IDLE, false }, { ACT_MP_GRENADE1_ATTACK, ACT_MP_SECONDARY_GRENADE1_ATTACK, false }, { ACT_MP_GRENADE2_DRAW, ACT_MP_SECONDARY_GRENADE2_DRAW, false }, { ACT_MP_GRENADE2_IDLE, ACT_MP_SECONDARY_GRENADE2_IDLE, false }, { ACT_MP_GRENADE2_ATTACK, ACT_MP_SECONDARY_GRENADE2_ATTACK, false }, { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false }, { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_SECONDARY, false }, { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_SECONDARY, false }, { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_SECONDARY, false }, { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_SECONDARY, false }, { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_SECONDARY, false }, { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_SECONDARY, false }, }; acttable_t CTFWeaponBase::m_acttableMelee[] = { { ACT_MP_STAND_IDLE, ACT_MP_STAND_MELEE, false }, { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_MELEE, false }, { ACT_MP_RUN, ACT_MP_RUN_MELEE, false }, { ACT_MP_WALK, ACT_MP_WALK_MELEE, false }, { ACT_MP_AIRWALK, ACT_MP_AIRWALK_MELEE, false }, { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_MELEE, false }, { ACT_MP_JUMP, ACT_MP_JUMP_MELEE, false }, { ACT_MP_JUMP_START, ACT_MP_JUMP_START_MELEE, false }, { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_MELEE, false }, { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_MELEE, false }, { ACT_MP_SWIM, ACT_MP_SWIM_MELEE, false }, { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_MELEE, false }, { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE, false }, { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_MELEE, false }, { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE, false }, { ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_MELEE_SECONDARY, false }, { ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE_SECONDARY,false }, { ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_MELEE, false }, { ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE, false }, { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_MELEE, false }, { ACT_MP_GRENADE1_DRAW, ACT_MP_MELEE_GRENADE1_DRAW, false }, { ACT_MP_GRENADE1_IDLE, ACT_MP_MELEE_GRENADE1_IDLE, false }, { ACT_MP_GRENADE1_ATTACK, ACT_MP_MELEE_GRENADE1_ATTACK, false }, { ACT_MP_GRENADE2_DRAW, ACT_MP_MELEE_GRENADE2_DRAW, false }, { ACT_MP_GRENADE2_IDLE, ACT_MP_MELEE_GRENADE2_IDLE, false }, { ACT_MP_GRENADE2_ATTACK, ACT_MP_MELEE_GRENADE2_ATTACK, false }, { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_MELEE, false }, { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_MELEE, false }, { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_MELEE, false }, { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_MELEE, false }, { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_MELEE, false }, { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_MELEE, false }, }; acttable_t CTFWeaponBase::m_acttableBuilding[] = { { ACT_MP_STAND_IDLE, ACT_MP_STAND_BUILDING, false }, { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_BUILDING, false }, { ACT_MP_RUN, ACT_MP_RUN_BUILDING, false }, { ACT_MP_WALK, ACT_MP_WALK_BUILDING, false }, { ACT_MP_AIRWALK, ACT_MP_AIRWALK_BUILDING, false }, { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_BUILDING, false }, { ACT_MP_JUMP, ACT_MP_JUMP_BUILDING, false }, { ACT_MP_JUMP_START, ACT_MP_JUMP_START_BUILDING, false }, { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_BUILDING, false }, { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_BUILDING, false }, { ACT_MP_SWIM, ACT_MP_SWIM_BUILDING, false }, { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_BUILDING, false }, { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_MP_ATTACK_CROUCH_BUILDING, false }, { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_BUILDING, false }, { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_MP_ATTACK_AIRWALK_BUILDING, false }, { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false }, { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false }, { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false }, { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE_BUILDING, false }, { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_BUILDING, false }, { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_BUILDING, false }, { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_BUILDING, false }, { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_BUILDING, false }, { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_BUILDING, false }, { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_BUILDING, false }, }; acttable_t CTFWeaponBase::m_acttablePDA[] = { { ACT_MP_STAND_IDLE, ACT_MP_STAND_PDA, false }, { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PDA, false }, { ACT_MP_RUN, ACT_MP_RUN_PDA, false }, { ACT_MP_WALK, ACT_MP_WALK_PDA, false }, { ACT_MP_AIRWALK, ACT_MP_AIRWALK_PDA, false }, { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PDA, false }, { ACT_MP_JUMP, ACT_MP_JUMP_PDA, false }, { ACT_MP_JUMP_START, ACT_MP_JUMP_START_PDA, false }, { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PDA, false }, { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PDA, false }, { ACT_MP_SWIM, ACT_MP_SWIM_PDA, false }, { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_MP_ATTACK_STAND_PDA, false }, { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_MP_ATTACK_SWIM_PDA, false }, { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_PDA, false }, { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_PDA, false }, { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_PDA, false }, { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_PDA, false }, { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_PDA, false }, { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_PDA, false }, }; ConVar mp_forceactivityset( "mp_forceactivityset", "-1", FCVAR_CHEAT|FCVAR_REPLICATED|FCVAR_DEVELOPMENTONLY ); acttable_t *CTFWeaponBase::ActivityList( int &iActivityCount ) { int iWeaponRole = GetTFWpnData().m_iWeaponType; if ( mp_forceactivityset.GetInt() >= 0 ) { iWeaponRole = mp_forceactivityset.GetInt(); } #ifdef CLIENT_DLL // If we're disguised, we always show the primary weapon // even though our actual weapon may not be primary CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pPlayer->IsEnemyPlayer() ) { CTFWeaponInfo *pWeaponInfo = pPlayer->m_Shared.GetDisguiseWeaponInfo(); if ( pWeaponInfo ) { iWeaponRole = pWeaponInfo->m_iWeaponType; } } #endif acttable_t *pTable; switch( iWeaponRole ) { case TF_WPN_TYPE_PRIMARY: default: pTable = m_acttablePrimary; iActivityCount = ARRAYSIZE(m_acttablePrimary); break; case TF_WPN_TYPE_SECONDARY: pTable = m_acttableSecondary; iActivityCount = ARRAYSIZE(m_acttableSecondary); break; case TF_WPN_TYPE_MELEE: pTable = m_acttableMelee; iActivityCount = ARRAYSIZE(m_acttableMelee); break; case TF_WPN_TYPE_BUILDING: pTable = m_acttableBuilding; iActivityCount = ARRAYSIZE(m_acttableBuilding); break; case TF_WPN_TYPE_PDA: pTable = m_acttablePDA; iActivityCount = ARRAYSIZE(m_acttablePDA); break; } return pTable; } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- CBasePlayer *CTFWeaponBase::GetPlayerOwner() const { return dynamic_cast( GetOwner() ); } // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- CTFPlayer *CTFWeaponBase::GetTFPlayerOwner() const { return dynamic_cast( GetOwner() ); } #ifdef CLIENT_DLL // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- C_BaseEntity *CTFWeaponBase::GetWeaponForEffect() { C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) return NULL; #if 0 // This causes many problems! if ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) { C_BasePlayer *pObserverTarget = ToBasePlayer( pLocalPlayer->GetObserverTarget() ); if ( pObserverTarget ) return pObserverTarget->GetViewModel(); } #endif if ( pLocalPlayer == GetTFPlayerOwner() ) return pLocalPlayer->GetViewModel(); return this; } #endif // ----------------------------------------------------------------------------- // Purpose: // ----------------------------------------------------------------------------- bool CTFWeaponBase::CanAttack( void ) { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( pPlayer ) return pPlayer->CanAttack(); return false; } #if defined( CLIENT_DLL ) static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); static ConVar cl_bobup( "cl_bobup","0.5" ); //----------------------------------------------------------------------------- // Purpose: Helper function to calculate head bob //----------------------------------------------------------------------------- float CalcViewModelBobHelper( CBasePlayer *player, BobState_t *pBobState ) { Assert( pBobState ); if ( !pBobState ) return 0; float cycle; //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) { //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) return 0.0f;// just use old value } //Find the speed of the player float speed = player->GetLocalVelocity().Length2D(); float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - pBobState->m_flLastBobTime ) * 320.0f ); // don't allow too big speed changes speed = clamp( speed, pBobState->m_flLastSpeed-flmaxSpeedDelta, pBobState->m_flLastSpeed+flmaxSpeedDelta ); speed = clamp( speed, -320, 320 ); pBobState->m_flLastSpeed = speed; //FIXME: This maximum speed value must come from the server. // MaxSpeed() is not sufficient for dealing with sprinting - jdw float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); pBobState->m_flBobTime += ( gpGlobals->curtime - pBobState->m_flLastBobTime ) * bob_offset; pBobState->m_flLastBobTime = gpGlobals->curtime; //Calculate the vertical bob cycle = pBobState->m_flBobTime - (int)(pBobState->m_flBobTime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat(); cycle /= cl_bobcycle.GetFloat(); if ( cycle < cl_bobup.GetFloat() ) { cycle = M_PI * cycle / cl_bobup.GetFloat(); } else { cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat()); } pBobState->m_flVerticalBob = speed*0.005f; pBobState->m_flVerticalBob = pBobState->m_flVerticalBob*0.3 + pBobState->m_flVerticalBob*0.7*sin(cycle); pBobState->m_flVerticalBob = clamp( pBobState->m_flVerticalBob, -7.0f, 4.0f ); //Calculate the lateral bob cycle = pBobState->m_flBobTime - (int)(pBobState->m_flBobTime/cl_bobcycle.GetFloat()*2)*cl_bobcycle.GetFloat()*2; cycle /= cl_bobcycle.GetFloat()*2; if ( cycle < cl_bobup.GetFloat() ) { cycle = M_PI * cycle / cl_bobup.GetFloat(); } else { cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat()); } pBobState->m_flLateralBob = speed*0.005f; pBobState->m_flLateralBob = pBobState->m_flLateralBob*0.3 + pBobState->m_flLateralBob*0.7*sin(cycle); pBobState->m_flLateralBob = clamp( pBobState->m_flLateralBob, -7.0f, 4.0f ); //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) return 0.0f; } //----------------------------------------------------------------------------- // Purpose: Helper function to add head bob //----------------------------------------------------------------------------- void AddViewModelBobHelper( Vector &origin, QAngle &angles, BobState_t *pBobState ) { Assert( pBobState ); if ( !pBobState ) return; Vector forward, right; AngleVectors( angles, &forward, &right, NULL ); // Apply bob, but scaled down to 40% VectorMA( origin, pBobState->m_flVerticalBob * 0.4f, forward, origin ); // Z bob a bit more origin[2] += pBobState->m_flVerticalBob * 0.1f; // bob the angles angles[ ROLL ] += pBobState->m_flVerticalBob * 0.5f; angles[ PITCH ] -= pBobState->m_flVerticalBob * 0.4f; angles[ YAW ] -= pBobState->m_flLateralBob * 0.3f; VectorMA( origin, pBobState->m_flLateralBob * 0.2f, right, origin ); } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float CTFWeaponBase::CalcViewmodelBob( void ) { CBasePlayer *player = ToBasePlayer( GetOwner() ); //Assert( player ); BobState_t *pBobState = GetBobState(); if ( pBobState ) return ::CalcViewModelBobHelper( player, pBobState ); else return 0; } //----------------------------------------------------------------------------- // Purpose: // Input : &origin - // &angles - // viewmodelindex - //----------------------------------------------------------------------------- void CTFWeaponBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) { // call helper functions to do the calculation BobState_t *pBobState = GetBobState(); if ( pBobState ) { CalcViewmodelBob(); ::AddViewModelBobHelper( origin, angles, pBobState ); } } //----------------------------------------------------------------------------- // Purpose: Returns the head bob state for this weapon, which is stored // in the view model. Note that this this function can return // NULL if the player is dead or the view model is otherwise not present. //----------------------------------------------------------------------------- BobState_t *CTFWeaponBase::GetBobState() { // get the view model for this weapon CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner == NULL ) return NULL; CBaseViewModel *baseViewModel = pOwner->GetViewModel( m_nViewModelIndex ); if ( baseViewModel == NULL ) return NULL; CTFViewModel *viewModel = dynamic_cast(baseViewModel); Assert( viewModel ); // get the bob state out of the view model return &( viewModel->GetBobState() ); } //----------------------------------------------------------------------------- // Purpose: Used for spy invisiblity material //----------------------------------------------------------------------------- int CTFWeaponBase::GetSkin() { int nSkin = 0; CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( pPlayer ) { CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) return 0; int iLocalTeam = pLocalPlayer->GetTeamNumber(); int iTeamNumber = pPlayer->GetTeamNumber(); bool bHasTeamSkins = false; // We only show disguise weapon to the enemy team when owner is disguised bool bUseDisguiseWeapon = ( iTeamNumber != iLocalTeam && iLocalTeam > LAST_SHARED_TEAM ); if ( bUseDisguiseWeapon && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) { CTFWeaponInfo *pInfo = pPlayer->m_Shared.GetDisguiseWeaponInfo(); if ( pInfo ) { bHasTeamSkins = pInfo->m_bHasTeamSkins_Worldmodel; } if ( pLocalPlayer != pPlayer ) { iTeamNumber = pPlayer->m_Shared.GetDisguiseTeam(); } } else { bHasTeamSkins = GetTFWpnData().m_bHasTeamSkins_Worldmodel; } if ( bHasTeamSkins ) { switch( iTeamNumber ) { case TF_TEAM_RED: nSkin = 0; break; case TF_TEAM_BLUE: nSkin = 1; break; } } } return nSkin; } bool CTFWeaponBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) { if( event == 6002 ) { CEffectData data; pViewModel->GetAttachment( atoi(options), data.m_vOrigin, data.m_vAngles ); data.m_nHitBox = GetWeaponID(); DispatchEffect( "TF_EjectBrass", data ); return true; } if ( event == AE_WPN_INCREMENTAMMO ) { CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( pPlayer && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 && !m_bReloadedThroughAnimEvent ) { m_iClip1 = MIN( ( m_iClip1 + 1 ), GetMaxClip1() ); pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType ); } m_bReloadedThroughAnimEvent = true; return true; } return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); } ShadowType_t CTFWeaponBase::ShadowCastType( void ) { if ( IsEffectActive( EF_NODRAW | EF_NOSHADOW ) ) return SHADOWS_NONE; if ( m_iState == WEAPON_IS_CARRIED_BY_PLAYER ) return SHADOWS_NONE; return BaseClass::ShadowCastType(); } //----------------------------------------------------------------------------- // Purpose: Used for spy invisiblity material //----------------------------------------------------------------------------- class CWeaponInvisProxy : public CEntityMaterialProxy { public: CWeaponInvisProxy( void ); virtual ~CWeaponInvisProxy( void ); virtual bool Init( IMaterial *pMaterial, KeyValues* pKeyValues ); virtual void OnBind( C_BaseEntity *pC_BaseEntity ); virtual IMaterial * GetMaterial(); private: IMaterialVar *m_pPercentInvisible; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CWeaponInvisProxy::CWeaponInvisProxy( void ) { m_pPercentInvisible = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CWeaponInvisProxy::~CWeaponInvisProxy( void ) { } //----------------------------------------------------------------------------- // Purpose: Get pointer to the color value // Input : *pMaterial - //----------------------------------------------------------------------------- bool CWeaponInvisProxy::Init( IMaterial *pMaterial, KeyValues* pKeyValues ) { Assert( pMaterial ); // Need to get the material var bool bFound; m_pPercentInvisible = pMaterial->FindVar( "$cloakfactor", &bFound ); return bFound; } extern ConVar tf_teammate_max_invis; //----------------------------------------------------------------------------- // Purpose: // Input : //----------------------------------------------------------------------------- void CWeaponInvisProxy::OnBind( C_BaseEntity *pEnt ) { if( !m_pPercentInvisible ) return; if ( !pEnt ) return; C_BaseEntity *pMoveParent = pEnt->GetMoveParent(); if ( !pMoveParent || !pMoveParent->IsPlayer() ) { m_pPercentInvisible->SetFloatValue( 0.0f ); return; } CTFPlayer *pPlayer = ToTFPlayer( pMoveParent ); Assert( pPlayer ); m_pPercentInvisible->SetFloatValue( pPlayer->GetEffectiveInvisibilityLevel() ); } IMaterial *CWeaponInvisProxy::GetMaterial() { if ( !m_pPercentInvisible ) return NULL; return m_pPercentInvisible->GetOwningMaterial(); } EXPOSE_INTERFACE( CWeaponInvisProxy, IMaterialProxy, "weapon_invis" IMATERIAL_PROXY_INTERFACE_VERSION ); #endif // CLIENT_DLL