//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFFlameThrower::RestartParticleEffect( void ) { CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() ); if ( !pOwner ) return; if ( m_pFlameEffect && m_hFlameEffectHost.Get() ) { m_hFlameEffectHost->ParticleProp()->StopEmission( m_pFlameEffect ); } m_iParticleWaterLevel = pOwner->GetWaterLevel(); int iTeam = pOwner->GetTeamNumber(); // Start the appropriate particle effect const char *pszParticleEffect; if ( pOwner->GetWaterLevel() == WL_Eyes ) { pszParticleEffect = "flamethrower_underwater"; } else { if ( m_bCritFire ) { pszParticleEffect = ConstructTeamParticle( "flamethrower_crit_%s", iTeam, true ); } else { pszParticleEffect = iTeam == TF_TEAM_RED ? "flamethrower" : ConstructTeamParticle( "flamethrower_%s", pOwner->GetTeamNumber(), true ); } } // Start the effect on the viewmodel if our owner is the local player C_BaseEntity *pModel = GetWeaponForEffect(); if ( pModel ) { m_pFlameEffect = pModel->ParticleProp()->Create( pszParticleEffect, PATTACH_POINT_FOLLOW, "muzzle" ); m_hFlameEffectHost = pModel; } pOwner->m_Shared.SetParticleToMercColor( m_pFlameEffect ); }
//---------------------------------------------------------------------------------------- // Purpose: moves the player //---------------------------------------------------------------------------------------- void CTFGameMovement::PlayerMove() { // call base class to do movement BaseClass::PlayerMove(); // handle player's interaction with water int nNewWaterLevel = m_pTFPlayer->GetWaterLevel(); if ( m_nOldWaterLevel != nNewWaterLevel ) { if ( WL_NotInWater == m_nOldWaterLevel ) { // The player has just entered the water. Determine if we should play a splash sound. bool bPlaySplash = false; Vector vecVelocity = m_pTFPlayer->GetAbsVelocity(); if ( vecVelocity.z <= -200.0f ) { // If the player has significant downward velocity, play a splash regardless of water depth. (e.g. Jumping hard into a puddle) bPlaySplash = true; } else { // Look at the water depth below the player. If it's significantly deep, play a splash to accompany the sinking that's about to happen. Vector vecStart = m_pTFPlayer->GetAbsOrigin(); Vector vecEnd = vecStart; vecEnd.z -= 20; // roughly thigh deep trace_t tr; // see if we hit anything solid a little bit below the player UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID,m_pTFPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction >= 1.0f ) { // some amount of water below the player, play a splash bPlaySplash = true; } } if ( bPlaySplash ) { m_pTFPlayer->EmitSound( "Physics.WaterSplash" ); } } } }
//----------------------------------------------------------------------------- // 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 }