//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFWeaponBaseMelee::CalcIsAttackCriticalHelper( void ) { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return false; int nCvarValue = tf_weapon_criticals_melee.GetInt(); if ( nCvarValue == 0 ) return false; if ( nCvarValue == 1 && !tf_weapon_criticals.GetBool() ) return false; float flPlayerCritMult = pPlayer->GetCritMult(); float flCritChance = TF_DAMAGE_CRIT_CHANCE_MELEE * flPlayerCritMult; CALL_ATTRIB_HOOK_FLOAT( flCritChance, mult_crit_chance ); // If the chance is 0, just bail. if ( flCritChance == 0.0f ) return false; return ( RandomInt( 0, WEAPON_RANDOM_RANGE-1 ) <= flCritChance * WEAPON_RANDOM_RANGE ); }
//----------------------------------------------------------------------------- // Purpose: // Input : *pPlayer - //----------------------------------------------------------------------------- void CTFWeaponBaseMelee::Swing( CTFPlayer *pPlayer ) { CalcIsAttackCritical(); // Play the melee swing and miss (whoosh) always. SendPlayerAnimEvent( pPlayer ); DoViewModelAnimation(); // Set next attack times. float flFireDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; CALL_ATTRIB_HOOK_FLOAT( flFireDelay, mult_postfiredelay ); m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; SetWeaponIdleTime( m_flNextPrimaryAttack + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeIdleEmpty ); if ( IsCurrentAttackACrit() ) { WeaponSound( BURST ); } else { WeaponSound( MELEE_MISS ); } m_flSmackTime = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flSmackDelay; }
// ---------------------------------------------------------------------------- - // Purpose: //----------------------------------------------------------------------------- void CTFCompoundBow::FireArrow( void ) { // Get the player owning the weapon. CTFPlayer *pPlayer = GetTFPlayerOwner(); if ( !pPlayer ) return; CalcIsAttackCritical(); SendWeaponAnim( ACT_VM_PRIMARYATTACK ); pPlayer->SetAnimation( PLAYER_ATTACK1 ); pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); pPlayer->m_Shared.RemoveCond( TF_COND_AIMING ); pPlayer->TeamFortress_SetSpeed(); FireProjectile( pPlayer ); #if !defined( CLIENT_DLL ) pPlayer->SpeakWeaponFire(); CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); #endif // Set next attack times. float flDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; CALL_ATTRIB_HOOK_FLOAT( flDelay, mult_postfiredelay ); m_flNextPrimaryAttack = gpGlobals->curtime + flDelay; SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); m_flChargeBeginTime = 0.0f; }
//----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float CTFWeaponBaseMelee::GetMeleeDamage( CBaseEntity *pTarget, int &iCustomDamage ) { float flDamage = (float)m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage; CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg ); return flDamage; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlameThrower::ItemPostFrame() { if ( m_bLowered ) return; // Get the player owning the weapon. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); if ( !pOwner ) return; int iAmmo = pOwner->GetAmmoCount( m_iPrimaryAmmoType ); bool bFired = false; if ( ( pOwner->m_nButtons & IN_ATTACK2 ) && m_flNextSecondaryAttack <= gpGlobals->curtime ) { float flAmmoPerSecondaryAttack = TF_FLAMETHROWER_AMMO_PER_SECONDARY_ATTACK; CALL_ATTRIB_HOOK_FLOAT( flAmmoPerSecondaryAttack, mult_airblast_cost ); if ( iAmmo >= flAmmoPerSecondaryAttack ) { SecondaryAttack(); bFired = true; } } else if ( ( pOwner->m_nButtons & IN_ATTACK ) && iAmmo > 0 && m_iWeaponState != FT_STATE_AIRBLASTING ) { PrimaryAttack(); bFired = true; } if ( !bFired ) { if ( m_iWeaponState > FT_STATE_IDLE ) { SendWeaponAnim( ACT_MP_ATTACK_STAND_POSTFIRE ); pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_POST ); m_iWeaponState = FT_STATE_IDLE; m_bCritFire = false; m_bHitTarget = false; } if ( !ReloadOrSwitchWeapons() ) { WeaponIdle(); } } //BaseClass::ItemPostFrame(); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPipebombLauncher::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 ); FireProjectile( pPlayer ); #if !defined( CLIENT_DLL ) pPlayer->SpeakWeaponFire(); CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); #endif // Set next attack times. float flDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; CALL_ATTRIB_HOOK_FLOAT( flDelay, mult_postfiredelay ); m_flNextPrimaryAttack = gpGlobals->curtime + flDelay; m_flLastDenySoundTime = gpGlobals->curtime; SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); // Check the reload mode and behave appropriately. if ( m_bReloadsSingly ) { m_iReloadMode.Set( TF_RELOAD_START ); } m_flChargeBeginTime = 0; }
float CTFGrenadeLauncher::GetProjectileSpeed( void ) { float flVelocity = TF_GRENADE_LAUNCER_VEL; CALL_ATTRIB_HOOK_FLOAT( flVelocity, mult_projectile_speed ); return flVelocity; }
//----------------------------------------------------------------------------- // Purpose: Accessor for damage, so sniper etc can modify damage //----------------------------------------------------------------------------- float CTFWeaponBaseGun::GetProjectileDamage( void ) { float flDamage = (float)m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage; CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg ); return flDamage; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CTFWeaponBaseGun::GetWeaponSpread( void ) { float flSpread = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flSpread; CALL_ATTRIB_HOOK_FLOAT( flSpread, mult_spread_scale ); return flSpread; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFWeaponBaseGun::PrimaryAttack( void ) { // Check for ammunition. if ( m_iClip1 <= 0 && UsesClipsForAmmo1() ) return; // Get the player owning the weapon. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return; if ( !CanAttack() ) return; if ( m_pWeaponInfo->GetWeaponData( TF_WEAPON_PRIMARY_MODE ).m_nBurstSize > 0 && m_iBurstSize == 0 ) { // Start the burst. m_iBurstSize = m_pWeaponInfo->GetWeaponData( TF_WEAPON_PRIMARY_MODE ).m_nBurstSize; } if ( m_iBurstSize > 0 ) { m_iBurstSize--; } CalcIsAttackCritical(); #ifndef CLIENT_DLL pPlayer->RemoveInvisibility(); pPlayer->RemoveDisguise(); // Minigun has custom handling if ( GetWeaponID() != TF_WEAPON_MINIGUN ) { pPlayer->SpeakWeaponFire(); } CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); #endif // Set the weapon mode. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; SendWeaponAnim( ACT_VM_PRIMARYATTACK ); pPlayer->SetAnimation( PLAYER_ATTACK1 ); FireProjectile( pPlayer ); m_flLastFireTime = gpGlobals->curtime; // Set next attack times. float flFireDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; CALL_ATTRIB_HOOK_FLOAT( flFireDelay, mult_postfiredelay ); m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay; // 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 = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; // Set the idle animation times based on the sequence duration, so that we play full fire animations // that last longer than the refire rate may allow. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); AbortReload(); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlameThrower::SecondaryAttack() { if ( !tf2c_airblast.GetBool() ) return; int iNoAirblast = 0; CALL_ATTRIB_HOOK_FLOAT( iNoAirblast, set_flamethrower_push_disabled ); if ( iNoAirblast ) return; // Get the player owning the weapon. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); if ( !pOwner ) return; if ( !CanAttack() ) { m_iWeaponState = FT_STATE_IDLE; return; } #ifdef CLIENT_DLL StopFlame(); #endif m_iWeaponState = FT_STATE_AIRBLASTING; SendWeaponAnim( ACT_VM_SECONDARYATTACK ); WeaponSound( WPN_DOUBLE ); #ifdef CLIENT_DLL if ( prediction->IsFirstTimePredicted() ) { StartFlame(); } #else // Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation. pOwner->NoteWeaponFired(); pOwner->SpeakWeaponFire(); CTF_GameStats.Event_PlayerFiredWeapon( pOwner, false ); // Move other players back to history positions based on local player's lag lagcompensation->StartLagCompensation( pOwner, pOwner->GetCurrentCommand() ); Vector vecDir; QAngle angDir = pOwner->EyeAngles(); AngleVectors( angDir, &vecDir ); const Vector vecBlastSize = Vector( 128, 128, 64 ); // Picking max out of length, width, height for airblast distance. float flBlastDist = max( max( vecBlastSize.x, vecBlastSize.y ), vecBlastSize.z ); Vector vecOrigin = pOwner->Weapon_ShootPosition() + vecDir * flBlastDist; CBaseEntity *pList[64]; int count = UTIL_EntitiesInBox( pList, 64, vecOrigin - vecBlastSize, vecOrigin + vecBlastSize, 0 ); if ( tf2c_debug_airblast.GetBool() ) { NDebugOverlay::Box( vecOrigin, -vecBlastSize, vecBlastSize, 0, 0, 255, 100, 2.0 ); } for ( int i = 0; i < count; i++ ) { CBaseEntity *pEntity = pList[i]; if ( !pEntity ) continue; if ( pEntity == pOwner ) continue; if ( !pEntity->IsDeflectable() ) continue; // Make sure we can actually see this entity so we don't hit anything through walls. trace_t tr; UTIL_TraceLine( pOwner->Weapon_ShootPosition(), pEntity->WorldSpaceCenter(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &tr ); if ( tr.fraction != 1.0f ) continue; if ( pEntity->IsPlayer() ) { if ( !pEntity->IsAlive() ) continue; CTFPlayer *pTFPlayer = ToTFPlayer( pEntity ); Vector vecPushDir; QAngle angPushDir = angDir; // Push them at least 45 degrees up. angPushDir[PITCH] = min( -45, angPushDir[PITCH] ); AngleVectors( angPushDir, &vecPushDir ); DeflectPlayer( pTFPlayer, pOwner, vecPushDir ); } else { // Deflect projectile to the point that we're aiming at, similar to rockets. Vector vecPos = pEntity->GetAbsOrigin(); Vector vecDeflect; GetProjectileReflectSetup( GetTFPlayerOwner(), vecPos, &vecDeflect, false ); DeflectEntity( pEntity, pOwner, vecDeflect ); } } lagcompensation->FinishLagCompensation( pOwner ); #endif float flAmmoPerSecondaryAttack = TF_FLAMETHROWER_AMMO_PER_SECONDARY_ATTACK; CALL_ATTRIB_HOOK_FLOAT( flAmmoPerSecondaryAttack, mult_airblast_cost ); pOwner->RemoveAmmo( flAmmoPerSecondaryAttack, m_iPrimaryAmmoType ); // Don't allow firing immediately after airblasting. m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75f; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlameThrower::PrimaryAttack() { // Are we capable of firing again? if ( m_flNextPrimaryAttack > gpGlobals->curtime ) return; // Get the player owning the weapon. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); if ( !pOwner ) return; if ( !CanAttack() ) { #if defined ( CLIENT_DLL ) StopFlame(); #endif m_iWeaponState = FT_STATE_IDLE; return; } CalcIsAttackCritical(); // Because the muzzle is so long, it can stick through a wall if the player is right up against it. // Make sure the weapon can't fire in this condition by tracing a line between the eye point and the end of the muzzle. trace_t trace; Vector vecEye = pOwner->EyePosition(); Vector vecMuzzlePos = GetVisualMuzzlePos(); CTraceFilterIgnoreObjects traceFilter( this, COLLISION_GROUP_NONE ); UTIL_TraceLine( vecEye, vecMuzzlePos, MASK_SOLID, &traceFilter, &trace ); if ( trace.fraction < 1.0 && ( !trace.m_pEnt || trace.m_pEnt->m_takedamage == DAMAGE_NO ) ) { // there is something between the eye and the end of the muzzle, most likely a wall, don't fire, and stop firing if we already are if ( m_iWeaponState > FT_STATE_IDLE ) { #if defined ( CLIENT_DLL ) StopFlame(); #endif m_iWeaponState = FT_STATE_IDLE; } return; } switch ( m_iWeaponState ) { case FT_STATE_IDLE: case FT_STATE_AIRBLASTING: { // Just started, play PRE and start looping view model anim pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRE ); SendWeaponAnim( ACT_VM_PRIMARYATTACK ); m_flStartFiringTime = gpGlobals->curtime + 0.16; // 5 frames at 30 fps m_iWeaponState = FT_STATE_STARTFIRING; } break; case FT_STATE_STARTFIRING: { // if some time has elapsed, start playing the looping third person anim if ( gpGlobals->curtime > m_flStartFiringTime ) { m_iWeaponState = FT_STATE_FIRING; m_flNextPrimaryAttackAnim = gpGlobals->curtime; } } break; case FT_STATE_FIRING: { if ( gpGlobals->curtime >= m_flNextPrimaryAttackAnim ) { pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); m_flNextPrimaryAttackAnim = gpGlobals->curtime + 1.4; // fewer than 45 frames! } } break; default: break; } #ifdef CLIENT_DLL // Restart our particle effect if we've transitioned across water boundaries if ( m_iParticleWaterLevel != -1 && pOwner->GetWaterLevel() != m_iParticleWaterLevel ) { if ( m_iParticleWaterLevel == WL_Eyes || pOwner->GetWaterLevel() == WL_Eyes ) { RestartParticleEffect(); } } #endif #ifdef CLIENT_DLL // Handle the flamethrower light if (tf2c_muzzlelight.GetBool()) { dlight_t *dl = effects->CL_AllocDlight(LIGHT_INDEX_MUZZLEFLASH + index); dl->origin = vecMuzzlePos; dl->color.r = 255; dl->color.g = 100; dl->color.b = 10; dl->die = gpGlobals->curtime + 0.01f; dl->radius = 240.f; dl->decay = 512.0f; dl->style = 5; } #endif #if !defined (CLIENT_DLL) // Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation. pOwner->NoteWeaponFired(); pOwner->SpeakWeaponFire(); CTF_GameStats.Event_PlayerFiredWeapon( pOwner, m_bCritFire ); // Move other players back to history positions based on local player's lag lagcompensation->StartLagCompensation( pOwner, pOwner->GetCurrentCommand() ); #endif float flFiringInterval = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; CALL_ATTRIB_HOOK_FLOAT( flFiringInterval, mult_postfiredelay ); // Don't attack if we're underwater if ( pOwner->GetWaterLevel() != WL_Eyes ) { // Find eligible entities in a cone in front of us. Vector vOrigin = pOwner->Weapon_ShootPosition(); Vector vForward, vRight, vUp; QAngle vAngles = pOwner->EyeAngles() + pOwner->GetPunchAngle(); AngleVectors( vAngles, &vForward, &vRight, &vUp ); #define NUM_TEST_VECTORS 30 #ifdef CLIENT_DLL bool bWasCritical = m_bCritFire; #endif // Burn & Ignite 'em int iDmgType = g_aWeaponDamageTypes[ GetWeaponID() ]; m_bCritFire = IsCurrentAttackACrit(); if ( m_bCritFire ) { iDmgType |= DMG_CRITICAL; } #ifdef CLIENT_DLL if ( bWasCritical != m_bCritFire ) { RestartParticleEffect(); } #endif #ifdef GAME_DLL // create the flame entity int iDamagePerSec = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage; float flDamage = (float)iDamagePerSec * flFiringInterval; CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg ); CTFFlameEntity::Create( GetFlameOriginPos(), pOwner->EyeAngles(), this, iDmgType, flDamage ); #endif } #ifdef GAME_DLL // Figure how much ammo we're using per shot and add it to our remainder to subtract. (We may be using less than 1.0 ammo units // per frame, depending on how constants are tuned, so keep an accumulator so we can expend fractional amounts of ammo per shot.) // Note we do this only on server and network it to client. If we predict it on client, it can get slightly out of sync w/server // and cause ammo pickup indicators to appear float flAmmoPerSecond = TF_FLAMETHROWER_AMMO_PER_SECOND_PRIMARY_ATTACK; CALL_ATTRIB_HOOK_FLOAT( flAmmoPerSecond, mult_flame_ammopersec ); m_flAmmoUseRemainder += flAmmoPerSecond * flFiringInterval; // take the integer portion of the ammo use accumulator and subtract it from player's ammo count; any fractional amount of ammo use // remains and will get used in the next shot int iAmmoToSubtract = (int) m_flAmmoUseRemainder; if ( iAmmoToSubtract > 0 ) { pOwner->RemoveAmmo( iAmmoToSubtract, m_iPrimaryAmmoType ); m_flAmmoUseRemainder -= iAmmoToSubtract; // round to 2 digits of precision m_flAmmoUseRemainder = (float) ( (int) (m_flAmmoUseRemainder * 100) ) / 100.0f; } #endif m_flNextPrimaryAttack = gpGlobals->curtime + flFiringInterval; m_flTimeWeaponIdle = gpGlobals->curtime + flFiringInterval; #if !defined (CLIENT_DLL) lagcompensation->FinishLagCompensation( pOwner ); #endif }