void CTFGameMovement::AirDash( void ) { // Apply approx. the jump velocity added to an air dash. Assert( sv_gravity.GetFloat() == 800.0f ); float flDashZ = 268.3281572999747f; // Get the wish direction. Vector vecForward, vecRight; AngleVectors( mv->m_vecViewAngles, &vecForward, &vecRight, NULL ); vecForward.z = 0.0f; vecRight.z = 0.0f; VectorNormalize( vecForward ); VectorNormalize( vecRight ); // Copy movement amounts float flForwardMove = mv->m_flForwardMove; float flSideMove = mv->m_flSideMove; // Find the direction,velocity in the x,y plane. Vector vecWishDirection( ( ( vecForward.x * flForwardMove ) + ( vecRight.x * flSideMove ) ), ( ( vecForward.y * flForwardMove ) + ( vecRight.y * flSideMove ) ), 0.0f ); // Update the velocity on the scout. mv->m_vecVelocity = vecWishDirection; mv->m_vecVelocity.z += flDashZ; m_pTFPlayer->m_Shared.SetAirDash( true ); // Play the gesture. m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_DOUBLEJUMP ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlareGun::LaunchProjectile(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. m_flNextPrimaryAttack = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData(m_iWeaponMode).m_flTimeFireDelay; SetWeaponIdleTime(gpGlobals->curtime + SequenceDuration()); // Check the reload mode and behave appropriately. if (m_bReloadsSingly) { m_iReloadMode.Set(TF_RELOAD_START); } }
// ---------------------------------------------------------------------------- - // 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: Attempt to heal any player within range of the medikit //----------------------------------------------------------------------------- void CWeaponMedigun::PrimaryAttack( void ) { CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); if ( !pOwner ) return; if ( !CanAttack() ) return; #ifdef GAME_DLL /* // Start boosting ourself if we're not if ( m_bChargeRelease && !m_bHealingSelf ) { pOwner->m_Shared.Heal( pOwner, GetHealRate() * 2 ); m_bHealingSelf = true; } */ #endif #if !defined (CLIENT_DLL) if ( tf_medigun_lagcomp.GetBool() ) lagcompensation->StartLagCompensation( pOwner, pOwner->GetCurrentCommand() ); #endif if ( FindAndHealTargets() ) { // Start the animation if ( !m_bHealing ) { #ifdef GAME_DLL pOwner->SpeakWeaponFire(); #endif SendWeaponAnim( ACT_MP_ATTACK_STAND_PREFIRE ); pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRE ); } m_bHealing = true; } else { RemoveHealingTarget(); } #if !defined (CLIENT_DLL) if ( tf_medigun_lagcomp.GetBool() ) lagcompensation->FinishLagCompensation( pOwner ); #endif }
//----------------------------------------------------------------------------- // 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 CWeaponKritzkrieg::RemoveHealingTarget( bool bStopHealingSelf ) { CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); if ( !pOwner ) return; #ifdef GAME_DLL if ( m_hHealingTarget ) { // HACK: For now, just deal with players if ( m_hHealingTarget->IsPlayer() ) { CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); CTFPlayer *pTFPlayer = ToTFPlayer( m_hHealingTarget ); pTFPlayer->m_Shared.StopHealing( pOwner ); pTFPlayer->m_Shared.RecalculateInvuln( false ); pTFPlayer->m_Shared.RecalculateCrits(false); pOwner->SpeakConceptIfAllowed( MP_CONCEPT_MEDIC_STOPPEDHEALING, pTFPlayer->IsAlive() ? "healtarget:alive" : "healtarget:dead" ); pTFPlayer->SpeakConceptIfAllowed( MP_CONCEPT_HEALTARGET_STOPPEDHEALING ); } } // Stop thinking - we no longer have a heal target. SetContextThink( NULL, 0, s_pszKritzkriegHealTargetThink ); #endif m_hHealingTarget.Set( NULL ); // Stop the welding animation if ( m_bHealing ) { SendWeaponAnim( ACT_MP_ATTACK_STAND_POSTFIRE ); pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_POST ); } #ifndef CLIENT_DLL m_DamageModifier.RemoveModifier(); #endif m_bHealing = false; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPipebombLauncher::LaunchGrenade( void ) { // Get the player owning the weapon. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return; StopSound("Weapon_StickyBombLauncher.ChargeUp"); CalcIsAttackCritical(); SendWeaponAnim( ACT_VM_PRIMARYATTACK ); pPlayer->SetAnimation( PLAYER_ATTACK1 ); pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); CTFGrenadePipebombProjectile *pProjectile = static_cast<CTFGrenadePipebombProjectile*>( FireProjectile( pPlayer ) ); if ( pProjectile ) { // Save the charge time to scale the detonation timer. pProjectile->SetChargeTime( gpGlobals->curtime - m_flChargeBeginTime ); } #if !defined( CLIENT_DLL ) pPlayer->SpeakWeaponFire(); CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); #endif // Set next attack times. m_flNextPrimaryAttack = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay; 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; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFMinigun::WeaponIdle() { if ( gpGlobals->curtime < m_flTimeWeaponIdle ) return; // Always wind down if we've hit here, because it only happens when the player has stopped firing/spinning if ( m_iWeaponState != AC_STATE_IDLE ) { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( pPlayer ) { pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_POST ); } WindDown(); return; } BaseClass::WeaponIdle(); m_flTimeWeaponIdle = gpGlobals->curtime + 12.5;// how long till we do this again. }
//----------------------------------------------------------------------------- // 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; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFMinigun::SharedAttack() { CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); if ( !pPlayer ) return; if ( !CanAttack() ) { WeaponIdle(); return; } if ( pPlayer->m_nButtons & IN_ATTACK ) { m_iWeaponMode = TF_WEAPON_PRIMARY_MODE; } else if ( pPlayer->m_nButtons & IN_ATTACK2 ) { m_iWeaponMode = TF_WEAPON_SECONDARY_MODE; } switch ( m_iWeaponState ) { default: case AC_STATE_IDLE: { // Removed the need for cells to powerup the AC WindUp(); m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; m_flTimeWeaponIdle = gpGlobals->curtime + 1.0; m_flStartedFiringAt = -1; pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRE ); break; } case AC_STATE_STARTFIRING: { // Start playing the looping fire sound if ( m_flNextPrimaryAttack <= gpGlobals->curtime ) { if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE ) { m_iWeaponState = AC_STATE_SPINNING; #ifdef GAME_DLL pPlayer->SpeakWeaponFire( MP_CONCEPT_WINDMINIGUN ); #endif } else { m_iWeaponState = AC_STATE_FIRING; #ifdef GAME_DLL pPlayer->SpeakWeaponFire( MP_CONCEPT_FIREMINIGUN ); #endif } m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flTimeWeaponIdle = gpGlobals->curtime + 0.1; } break; } case AC_STATE_FIRING: { if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE ) { #ifdef GAME_DLL pPlayer->ClearWeaponFireScene(); pPlayer->SpeakWeaponFire( MP_CONCEPT_WINDMINIGUN ); #endif m_iWeaponState = AC_STATE_SPINNING; m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flTimeWeaponIdle = gpGlobals->curtime + 0.1; } else if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) { m_iWeaponState = AC_STATE_DRYFIRE; } else { if ( m_flStartedFiringAt < 0 ) { m_flStartedFiringAt = gpGlobals->curtime; } #ifdef GAME_DLL if ( m_flNextFiringSpeech < gpGlobals->curtime ) { m_flNextFiringSpeech = gpGlobals->curtime + 5.0; pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_MINIGUN_FIREWEAPON ); } #endif // Only fire if we're actually shooting BaseClass::PrimaryAttack(); // fire and do timers CalcIsAttackCritical(); m_bCritShot = IsCurrentAttackACrit(); pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY ); m_flTimeWeaponIdle = gpGlobals->curtime + 0.2; } break; } case AC_STATE_DRYFIRE: { m_flStartedFiringAt = -1; if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) > 0 ) { m_iWeaponState = AC_STATE_FIRING; } else if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE ) { m_iWeaponState = AC_STATE_SPINNING; } SendWeaponAnim( ACT_VM_SECONDARYATTACK ); break; } case AC_STATE_SPINNING: { m_flStartedFiringAt = -1; if ( m_iWeaponMode == TF_WEAPON_PRIMARY_MODE ) { if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) > 0 ) { #ifdef GAME_DLL pPlayer->ClearWeaponFireScene(); pPlayer->SpeakWeaponFire( MP_CONCEPT_FIREMINIGUN ); #endif m_iWeaponState = AC_STATE_FIRING; } else { m_iWeaponState = AC_STATE_DRYFIRE; } } SendWeaponAnim( ACT_VM_SECONDARYATTACK ); break; } } }
//----------------------------------------------------------------------------- // Purpose: Start placing or building the currently selected object //----------------------------------------------------------------------------- void CTFWeaponBuilder::PrimaryAttack( void ) { CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); if ( !pOwner ) return; if ( !CanAttack() ) return; // Necessary so that we get the latest building position for the test, otherwise // we are one frame behind. UpdatePlacementState(); // What state should we move to? switch( m_iBuildState ) { case BS_IDLE: { // Idle state starts selection SetCurrentState( BS_SELECTING ); } break; case BS_SELECTING: { // Do nothing, client handles selection return; } break; case BS_PLACING: { if ( m_hObjectBeingBuilt ) { int iFlags = m_hObjectBeingBuilt->GetObjectFlags(); // Tricky, because this can re-calc the object position and change whether its a valid // pos or not. Best not to do this only in debug, but we can be pretty sure that this // will give the same result as was calculated in UpdatePlacementState() above. Assert( IsValidPlacement() ); // If we're placing an attachment, like a sapper, play a placement animation on the owner if ( m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() ) { pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE ); } StartBuilding(); // Should we switch away? if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT ) { // Start placing another SetCurrentState( BS_PLACING ); StartPlacement(); } else { SwitchOwnersWeaponToLast(); } } } break; case BS_PLACING_INVALID: { if ( m_flNextDenySound < gpGlobals->curtime ) { CSingleUserRecipientFilter filter( pOwner ); EmitSound( filter, entindex(), "Player.DenyWeaponSelection" ); m_flNextDenySound = gpGlobals->curtime + 0.5; } } break; } m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f; }
bool CTFGameMovement::CheckJumpButton() { // Are we dead? Then we cannot jump. if ( player->pl.deadflag ) return false; // Check to see if we are in water. if ( !CheckWaterJumpButton() ) return false; // Cannot jump while taunting if ( m_pTFPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ) return false; // Check to see if the player is a scout. bool bScout = m_pTFPlayer->GetPlayerClass()->IsClass( TF_CLASS_SCOUT ); bool bAirDash = false; bool bOnGround = ( player->GetGroundEntity() != NULL ); // Cannot jump will ducked. if ( player->GetFlags() & FL_DUCKING ) { // Let a scout do it. bool bAllow = (bScout && !bOnGround) || tf2c_duckjump.GetBool() || TFGameRules()->IsDeathmatch(); if ( !bAllow ) return false; } // Cannot jump while in the unduck transition. if ( ( player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ) ) || ( player->m_Local.m_flDuckJumpTime > 0.0f ) && !tf2c_duckjump.GetBool() ) return false; // Cannot jump again until the jump button has been released. if ( mv->m_nOldButtons & IN_JUMP && !(tf2c_autojump.GetBool() || TFGameRules()->IsDeathmatch()) ) return false; // In air, so ignore jumps (unless you are a scout). if ( !bOnGround ) { if ( bScout && !m_pTFPlayer->m_Shared.IsAirDashing() ) { bAirDash = true; } else { mv->m_nOldButtons |= IN_JUMP; return false; } } // Check for an air dash. if ( bAirDash ) { AirDash(); return true; } PreventBunnyJumping(); // Start jump animation and player sound (specific TF animation and flags). m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_JUMP ); player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true ); m_pTFPlayer->m_Shared.SetJumping( true ); // Set the player as in the air. SetGroundEntity( NULL ); // Check the surface the player is standing on to see if it impacts jumping. float flGroundFactor = 1.0f; if ( player->m_pSurfaceData ) { flGroundFactor = player->m_pSurfaceData->game.jumpFactor; } // fMul = sqrt( 2.0 * gravity * jump_height (21.0units) ) * GroundFactor Assert( sv_gravity.GetFloat() == 800.0f ); float flMul = 268.3281572999747f * flGroundFactor; // Save the current z velocity. float flStartZ = mv->m_vecVelocity[2]; // Acclerate upward if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) { // If we are ducking... // d = 0.5 * g * t^2 - distance traveled with linear accel // t = sqrt(2.0 * 45 / g) - how long to fall 45 units // v = g * t - velocity at the end (just invert it to jump up that high) // v = g * sqrt(2.0 * 45 / g ) // v^2 = g * g * 2.0 * 45 / g // v = sqrt( g * 2.0 * 45 ) mv->m_vecVelocity[2] = flMul; // 2 * gravity * jump_height * ground_factor } else { mv->m_vecVelocity[2] += flMul; // 2 * gravity * jump_height * ground_factor } // Apply gravity. FinishGravity(); // Save the output data for the physics system to react to if need be. mv->m_outJumpVel.z += mv->m_vecVelocity[2] - flStartZ; mv->m_outStepHeight += 0.15f; // Flag that we jumped and don't jump again until it is released. mv->m_nOldButtons |= IN_JUMP; return true; }
//----------------------------------------------------------------------------- // 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 }
//----------------------------------------------------------------------------- // Purpose: Start placing or building the currently selected object //----------------------------------------------------------------------------- void CTFWeaponBuilder::PrimaryAttack( void ) { CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); if ( !pOwner ) return; if ( !CanAttack() ) return; // Necessary so that we get the latest building position for the test, otherwise // we are one frame behind. UpdatePlacementState(); // What state should we move to? switch( m_iBuildState ) { case BS_IDLE: { // Idle state starts selection SetCurrentState( BS_SELECTING ); } break; case BS_SELECTING: { // Do nothing, client handles selection return; } break; case BS_PLACING: { if ( m_hObjectBeingBuilt ) { int iFlags = m_hObjectBeingBuilt->GetObjectFlags(); // Tricky, because this can re-calc the object position and change whether its a valid // pos or not. Best not to do this only in debug, but we can be pretty sure that this // will give the same result as was calculated in UpdatePlacementState() above. Assert( IsValidPlacement() ); // If we're placing an attachment, like a sapper, play a placement animation on the owner if ( m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() ) { pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE ); } // Need to save this for later since StartBuilding will clear m_hObjectBeingBuilt. CBaseObject *pParentObject = m_hObjectBeingBuilt->GetParentObject(); StartBuilding(); // Attaching a sapper to a teleporter automatically saps another end. if ( GetType() == OBJ_ATTACHMENT_SAPPER ) { CObjectTeleporter *pTeleporter = dynamic_cast<CObjectTeleporter *>( pParentObject ); if ( pTeleporter ) { CObjectTeleporter *pMatch = pTeleporter->GetMatchingTeleporter(); // If the other end is not already sapped then place a sapper on it. if ( pMatch && !pMatch->IsPlacing() && !pMatch->HasSapper() ) { SetCurrentState( BS_PLACING ); StartPlacement(); if ( m_hObjectBeingBuilt.Get() ) { m_hObjectBeingBuilt->UpdateAttachmentPlacement( pMatch ); StartBuilding(); } } } } // Should we switch away? if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT ) { // Start placing another SetCurrentState( BS_PLACING ); StartPlacement(); } else { SwitchOwnersWeaponToLast(); } } } break; case BS_PLACING_INVALID: { if ( m_flNextDenySound < gpGlobals->curtime ) { CSingleUserRecipientFilter filter( pOwner ); EmitSound( filter, entindex(), "Player.DenyWeaponSelection" ); m_flNextDenySound = gpGlobals->curtime + 0.5; } } break; } m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f; }