diff options
Diffstat (limited to 'game/shared/hl1/hl1mp_weapon_gauss.cpp')
| -rw-r--r-- | game/shared/hl1/hl1mp_weapon_gauss.cpp | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/game/shared/hl1/hl1mp_weapon_gauss.cpp b/game/shared/hl1/hl1mp_weapon_gauss.cpp new file mode 100644 index 0000000..e40f95d --- /dev/null +++ b/game/shared/hl1/hl1mp_weapon_gauss.cpp @@ -0,0 +1,719 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Gauss +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "hl1mp_basecombatweapon_shared.h" +//#include "basecombatcharacter.h" +//#include "AI_BaseNPC.h" +#include "takedamageinfo.h" +#ifdef CLIENT_DLL +#include "hl1/hl1_c_player.h" +#else +#include "hl1_player.h" +#endif +#include "gamerules.h" +#include "in_buttons.h" +#ifdef CLIENT_DLL +#else +#include "soundent.h" +#include "game.h" +#endif +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "soundenvelope.h" +//#include "hl1_player.h" +#include "shake.h" +#include "effect_dispatch_data.h" +#ifdef CLIENT_DLL +#include "c_te_effect_dispatch.h" +#else +#include "te_effect_dispatch.h" +#endif +#include "SoundEmitterSystem/isoundemittersystembase.h" + +#define GAUSS_GLOW_SPRITE "sprites/hotglow.vmt" +#define GAUSS_BEAM_SPRITE "sprites/smoke.vmt" + + +extern ConVar sk_plr_dmg_gauss; + +#ifdef CLIENT_DLL +#define CWeaponGauss C_WeaponGauss +#endif + +//----------------------------------------------------------------------------- +// CWeaponGauss +//----------------------------------------------------------------------------- + + +class CWeaponGauss : public CBaseHL1MPCombatWeapon +{ + DECLARE_CLASS( CWeaponGauss, CBaseHL1MPCombatWeapon ); +public: + + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + CWeaponGauss( void ); + + void Precache( void ); + void PrimaryAttack( void ); + void SecondaryAttack( void ); + void WeaponIdle( void ); + void AddViewKick( void ); + bool Deploy( void ); + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + +// DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + +private: + void StopSpinSound( void ); + float GetFullChargeTime( void ); + void StartFire( void ); + void Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ); + +private: +// int m_nAttackState; +// bool m_bPrimaryFire; + CNetworkVar( int, m_nAttackState); + CNetworkVar( bool, m_bPrimaryFire); + + CSoundPatch *m_sndCharge; +}; + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGauss, DT_WeaponGauss ); + +BEGIN_NETWORK_TABLE( CWeaponGauss, DT_WeaponGauss ) +#ifdef CLIENT_DLL + RecvPropInt( RECVINFO( m_nAttackState ) ), + RecvPropBool( RECVINFO( m_bPrimaryFire ) ), +#else + SendPropInt( SENDINFO( m_nAttackState ) ), + SendPropBool( SENDINFO( m_bPrimaryFire ) ), +#endif +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponGauss ) +#ifdef CLIENT_DLL + DEFINE_PRED_FIELD( m_nAttackState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), + DEFINE_PRED_FIELD( m_bPrimaryFire, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), +#endif +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_gauss, CWeaponGauss ); + +PRECACHE_WEAPON_REGISTER( weapon_gauss ); + +//IMPLEMENT_SERVERCLASS_ST( CWeaponGauss, DT_WeaponGauss ) +//END_SEND_TABLE() + +BEGIN_DATADESC( CWeaponGauss ) + DEFINE_FIELD( m_nAttackState, FIELD_INTEGER ), + DEFINE_FIELD( m_bPrimaryFire, FIELD_BOOLEAN ), + DEFINE_SOUNDPATCH( m_sndCharge ), +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CWeaponGauss::CWeaponGauss( void ) +{ + m_bReloadsSingly = false; + m_bFiresUnderwater = false; + + m_bPrimaryFire = false; + m_nAttackState = 0; + m_sndCharge = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponGauss::Precache( void ) +{ + PrecacheModel( GAUSS_GLOW_SPRITE ); + PrecacheModel( GAUSS_BEAM_SPRITE ); + + PrecacheScriptSound( "Weapon_Gauss.Zap1" ); + PrecacheScriptSound( "Weapon_Gauss.Zap2" ); + PrecacheScriptSound( "Weapon_Gauss.Fire" ); + PrecacheScriptSound( "Weapon_Gauss.StaticDischarge" ); + PrecacheScriptSound( "Weapon_Gauss.Spin" ); + + BaseClass::Precache(); +} + +float CWeaponGauss::GetFullChargeTime( void ) +{ + if ( g_pGameRules->IsMultiplayer() ) + { + return 1.5; + } + else + { + return 4; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponGauss::PrimaryAttack( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if ( !pPlayer ) + { + return; + } + + if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) < 2 ) + { + WeaponSound( EMPTY ); + pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 ); + return; + } + +//FIXME pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME; + m_bPrimaryFire = true; + + pPlayer->RemoveAmmo( 2, m_iPrimaryAmmoType ); + + StartFire(); + m_nAttackState = 0; + SetWeaponIdleTime( gpGlobals->curtime + 1.0 ); + pPlayer->SetNextAttack( gpGlobals->curtime + 0.2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponGauss::SecondaryAttack( void ) +{ + CHL1_Player *pPlayer = ToHL1Player( GetOwner() ); + if ( !pPlayer ) + { + return; + } + + // don't fire underwater + if ( pPlayer->GetWaterLevel() == 3 ) + { + if ( m_nAttackState != 0 ) + { + EmitSound( "Weapon_Gauss.Zap1" ); + SendWeaponAnim( ACT_VM_IDLE ); + m_nAttackState = 0; + } + else + { + WeaponSound( EMPTY ); + } + + m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; + return; + } + + if ( m_nAttackState == 0 ) + { + if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) + { + WeaponSound( EMPTY ); + pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 ); + return; + } + + m_bPrimaryFire = false; + + pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType ); // take one ammo just to start the spin + pPlayer->m_flNextAmmoBurn = gpGlobals->curtime; + + // spin up +//FIXME pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME; + + SendWeaponAnim( ACT_GAUSS_SPINUP ); + m_nAttackState = 1; + SetWeaponIdleTime( gpGlobals->curtime + 0.5 ); + pPlayer->m_flStartCharge = gpGlobals->curtime; + pPlayer->m_flAmmoStartCharge = gpGlobals->curtime + GetFullChargeTime(); + + //Start looping sound + if ( m_sndCharge == NULL ) + { + CPASAttenuationFilter filter( this ); + m_sndCharge = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_WEAPON, "Weapon_Gauss.Spin", ATTN_NORM ); + } + + if ( m_sndCharge != NULL ) + { + (CSoundEnvelopeController::GetController()).Play( m_sndCharge, 1.0f, 110 ); + } + } + else if (m_nAttackState == 1) + { + if ( HasWeaponIdleTimeElapsed() ) + { + SendWeaponAnim( ACT_GAUSS_SPINCYCLE ); + m_nAttackState = 2; + } + } + else + { + // during the charging process, eat one bit of ammo every once in a while + if ( gpGlobals->curtime >= pPlayer->m_flNextAmmoBurn && pPlayer->m_flNextAmmoBurn != 1000 ) + { + pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType ); + + if ( g_pGameRules->IsMultiplayer() ) + { + pPlayer->m_flNextAmmoBurn = gpGlobals->curtime + 0.1; + } + else + { + pPlayer->m_flNextAmmoBurn = gpGlobals->curtime + 0.3; + } + } + + if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) + { + // out of ammo! force the gun to fire + StartFire(); + m_nAttackState = 0; + SetWeaponIdleTime( gpGlobals->curtime + 1.0 ); + pPlayer->SetNextAttack( gpGlobals->curtime + 1 ); + return; + } + + if ( gpGlobals->curtime >= pPlayer->m_flAmmoStartCharge ) + { + // don't eat any more ammo after gun is fully charged. + pPlayer->m_flNextAmmoBurn = 1000; + } + + int pitch = ( gpGlobals->curtime - pPlayer->m_flStartCharge ) * ( 150 / GetFullChargeTime() ) + 100; + if ( pitch > 250 ) + pitch = 250; + + // ALERT( at_console, "%d %d %d\n", m_nAttackState, m_iSoundState, pitch ); + +// if ( m_iSoundState == 0 ) +// ALERT( at_console, "sound state %d\n", m_iSoundState ); + + if ( m_sndCharge != NULL ) + { + (CSoundEnvelopeController::GetController()).SoundChangePitch( m_sndCharge, pitch, 0 ); + } + +//FIXME m_pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_CHARGE_VOLUME; + + // m_flTimeWeaponIdle = gpGlobals->curtime + 0.1; + if ( pPlayer->m_flStartCharge < gpGlobals->curtime - 10 ) + { + // Player charged up too long. Zap him. + EmitSound( "Weapon_Gauss.Zap1" ); + EmitSound( "Weapon_Gauss.Zap2" ); + + m_nAttackState = 0; + SetWeaponIdleTime( gpGlobals->curtime + 1.0 ); + pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 ); + +#if !defined(CLIENT_DLL ) + // Add DMG_CRUSH because we don't want any physics force + pPlayer->TakeDamage( CTakeDamageInfo( this, this, 50, DMG_SHOCK | DMG_CRUSH ) ); + + color32 gaussDamage = {255,128,0,128}; + UTIL_ScreenFade( pPlayer, gaussDamage, 2, 0.5, FFADE_IN ); +#endif + + SendWeaponAnim( ACT_VM_IDLE ); + + StopSpinSound(); + // Player may have been killed and this weapon dropped, don't execute any more code after this! + return; + } + } +} + +//========================================================= +// StartFire- since all of this code has to run and then +// call Fire(), it was easier at this point to rip it out +// of weaponidle() and make its own function then to try to +// merge this into Fire(), which has some identical variable names +//========================================================= +void CWeaponGauss::StartFire( void ) +{ + float flDamage; + + CHL1_Player *pPlayer = ToHL1Player( GetOwner() ); + if ( !pPlayer ) + { + return; + } + + Vector vecAiming = pPlayer->GetAutoaimVector( 0 ); + Vector vecSrc = pPlayer->Weapon_ShootPosition( ); + + if ( gpGlobals->curtime - pPlayer->m_flStartCharge > GetFullChargeTime() ) + { + flDamage = 200; + } + else + { + flDamage = 200 * (( gpGlobals->curtime - pPlayer->m_flStartCharge) / GetFullChargeTime() ); + } + + if ( m_bPrimaryFire ) + { + flDamage = sk_plr_dmg_gauss.GetFloat() * g_pGameRules->GetDamageMultiplier(); + } + + //ALERT ( at_console, "Time:%f Damage:%f\n", gpGlobals->curtime - m_pPlayer->m_flStartCharge, flDamage ); + Vector vecNewVel = pPlayer->GetAbsVelocity(); + float flZVel = vecNewVel.z; + + if ( !m_bPrimaryFire ) + { + vecNewVel = vecNewVel - vecAiming * flDamage * 5; + pPlayer->SetAbsVelocity( vecNewVel ); + } + + if ( !g_pGameRules->IsMultiplayer() ) + { + // in deathmatch, gauss can pop you up into the air. Not in single play. + vecNewVel.z = flZVel; + pPlayer->SetAbsVelocity( vecNewVel ); + } + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + // time until aftershock 'static discharge' sound + pPlayer->m_flPlayAftershock = gpGlobals->curtime + random->RandomFloat( 0.3, 0.8 ); + + Fire( vecSrc, vecAiming, flDamage ); +} + +void CWeaponGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) +{ + CBaseEntity *pIgnore; + Vector vecSrc = vecOrigSrc; + Vector vecDest = vecSrc + vecDir * MAX_TRACE_LENGTH; + bool fFirstBeam = true; + bool fHasPunched = false; + float flMaxFrac = 1.0; + int nMaxHits = 10; + + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if ( !pPlayer ) + { + return; + } + +//FIXME pPlayer->m_iWeaponVolume = GAUSS_PRIMARY_FIRE_VOLUME; + + StopSpinSound(); + + pIgnore = pPlayer; + +// ALERT( at_console, "%f %f\n", tr.flFraction, flMaxFrac ); + + while ( flDamage > 10 && nMaxHits > 0 ) + { + trace_t tr; + + nMaxHits--; + + // ALERT( at_console, "." ); + UTIL_TraceLine( vecSrc, vecDest, MASK_SHOT, pIgnore, COLLISION_GROUP_NONE, &tr ); + + if ( tr.allsolid ) + break; + + CBaseEntity *pEntity = tr.m_pEnt; + if (pEntity == NULL) + break; + + CBroadcastRecipientFilter filter; + CEffectData data6; + if ( fFirstBeam ) + { + pPlayer->DoMuzzleFlash(); + fFirstBeam = false; + + data6.m_vOrigin = tr.endpos; +// data6.m_nEntIndex = pPlayer->GetViewModel()->entindex(); +#ifdef CLIENT_DLL + data6.m_hEntity = pPlayer; +#else + data6.m_nEntIndex = pPlayer->entindex(); +#endif + data6.m_fFlags = m_bPrimaryFire; + te->DispatchEffect( filter, 0.0, data6.m_vOrigin, "HL1GaussBeam", data6 ); + } + else + { + data6.m_vOrigin = tr.endpos; + data6.m_vStart = vecSrc; + data6.m_fFlags = m_bPrimaryFire; + te->DispatchEffect( filter, 0.0, data6.m_vOrigin, "HL1GaussBeamReflect", data6 ); + } + + bool fShouldDamageEntity = ( pEntity->m_takedamage != DAMAGE_NO ); + + if ( fShouldDamageEntity ) + { + ClearMultiDamage(); + CTakeDamageInfo info( this, pPlayer, flDamage, DMG_ENERGYBEAM ); + CalculateMeleeDamageForce( &info, vecDir, tr.endpos ); + pEntity->DispatchTraceAttack( info, vecDir, &tr ); + ApplyMultiDamage(); + } + + if ( pEntity->IsBSPModel() && !fShouldDamageEntity ) + { + float n; + + pIgnore = NULL; + + n = -DotProduct( tr.plane.normal, vecDir ); + + if ( n < 0.5 ) // 60 degrees + { + // ALERT( at_console, "reflect %f\n", n ); + // reflect + Vector vecReflect; + + vecReflect = 2.0 * tr.plane.normal * n + vecDir; + flMaxFrac = flMaxFrac - tr.fraction; + vecDir = vecReflect; + vecSrc = tr.endpos;// + vecDir * 8; + vecDest = vecSrc + vecDir * MAX_TRACE_LENGTH; + +#if !defined(CLIENT_DLL) + // explode a bit + RadiusDamage( CTakeDamageInfo( this, pPlayer, flDamage * n, DMG_BLAST ), tr.endpos, flDamage * n * 2.5, CLASS_NONE, NULL ); +#endif + + CEffectData data1; + data1.m_vOrigin = tr.endpos; + data1.m_vNormal = tr.plane.normal; + data1.m_flMagnitude = flDamage * n; + DispatchEffect( "HL1GaussReflect", data1 ); + + // lose energy + if (n == 0) + n = 0.1; + + flDamage = flDamage * (1 - n); + } + else + { + // tunnel + UTIL_ImpactTrace( &tr, DMG_ENERGYBEAM ); + + CEffectData data4; + data4.m_vOrigin = tr.endpos; + data4.m_flMagnitude = flDamage; + DispatchEffect( "HL1GaussWallImpact1", data4 ); + + // limit it to one hole punch + if ( fHasPunched ) + break; + + fHasPunched = true; + + // try punching through wall if secondary attack (primary is incapable of breaking through) + if ( !m_bPrimaryFire ) + { + trace_t punch_tr; + + UTIL_TraceLine( tr.endpos + vecDir * 8, vecDest, MASK_SHOT, pIgnore, COLLISION_GROUP_NONE, &punch_tr); + if ( !punch_tr.allsolid ) + { + trace_t exit_tr; + // trace backwards to find exit point + UTIL_TraceLine( punch_tr.endpos, tr.endpos, MASK_SHOT, pIgnore, COLLISION_GROUP_NONE, &exit_tr); + + float n = (exit_tr.endpos - tr.endpos).Length( ); + + if ( n < flDamage ) + { + if (n == 0) + n = 1; + + flDamage -= n; + + CEffectData data2; + data2.m_vOrigin = tr.endpos; + data2.m_vNormal = vecDir; + DispatchEffect( "HL1GaussWallPunchEnter", data2 ); + + UTIL_ImpactTrace( &exit_tr, DMG_ENERGYBEAM ); + + CEffectData data3; + data3.m_vOrigin = exit_tr.endpos; + data3.m_vNormal = vecDir; + data3.m_flMagnitude = flDamage; + DispatchEffect( "HL1GaussWallPunchExit", data3 ); + + // ALERT( at_console, "punch %f\n", n ); + + // exit blast damage + float flDamageRadius; + + if ( g_pGameRules->IsMultiplayer() ) + { + flDamageRadius = flDamage * 1.75; // Old code == 2.5 + } + else + { + flDamageRadius = flDamage * 2.5; + } + +#if !defined( CLIENT_DLL) + RadiusDamage( CTakeDamageInfo( this, pPlayer, flDamage, DMG_BLAST ), exit_tr.endpos + vecDir * 8, flDamageRadius, CLASS_NONE, NULL ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1024, 3.0 ); +#endif + + vecSrc = exit_tr.endpos + vecDir; + } + } + else + { + //ALERT( at_console, "blocked %f\n", n ); + flDamage = 0; + } + } + else + { + //ALERT( at_console, "blocked solid\n" ); + if ( m_bPrimaryFire ) + { + // slug doesn't punch through ever with primary + // fire, so leave a little glowy bit and make some balls + CEffectData data5; + data5.m_vOrigin = tr.endpos; + data5.m_vNormal = tr.plane.normal; + DispatchEffect( "HL1GaussWallImpact2", data5 ); +#if !defined( CLIENT_DLL) + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.5 ); +#endif + } + + flDamage = 0; + } + + } + } + else + { + vecSrc = tr.endpos + vecDir; + pIgnore = pEntity; + } + } + + pPlayer->ViewPunch( QAngle( -2, 0, 0 ) ); + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + CPASAttenuationFilter filter( this ); + + CSoundParameters params; + if ( GetParametersForSound( "Weapon_Gauss.Fire", params, NULL ) ) + { + EmitSound_t ep( params ); + ep.m_flVolume = 0.5 + flDamage * (1.0 / 400.0); + EmitSound( filter, entindex(), ep ); + } +} + +void CWeaponGauss::WeaponIdle( void ) +{ + CHL1_Player *pPlayer = ToHL1Player( GetOwner() ); + if ( !pPlayer ) + { + return; + } + + // play aftershock static discharge + if ( pPlayer->m_flPlayAftershock && pPlayer->m_flPlayAftershock < gpGlobals->curtime ) + { + EmitSound( "Weapon_Gauss.StaticDischarge" ); + pPlayer->m_flPlayAftershock = 0.0; + } + + if ( !HasWeaponIdleTimeElapsed() ) + return; + + if ( m_nAttackState != 0 ) + { + StartFire(); + m_nAttackState = 0; + SetWeaponIdleTime( gpGlobals->curtime + 2.0 ); + } + else + { + float flRand = random->RandomFloat( 0, 1 ); + if ( flRand <= 0.75 ) + { + SendWeaponAnim( ACT_VM_IDLE ); + SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) ); + } + else + { + SendWeaponAnim( ACT_VM_FIDGET ); + SetWeaponIdleTime( gpGlobals->curtime + 3 ); + } + } +} + +/* +================================================== +AddViewKick +================================================== +*/ + +void CWeaponGauss::AddViewKick( void ) +{ +} + +bool CWeaponGauss::Deploy( void ) +{ + if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() ) ) + { + CHL1_Player *pPlayer = ToHL1Player( GetOwner() ); + if ( pPlayer ) + { + pPlayer->m_flPlayAftershock = 0.0; + } + + return true; + } + else + { + return false; + } +} + +bool CWeaponGauss::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + + StopSpinSound(); + m_nAttackState = 0; + + return BaseClass::Holster(pSwitchingTo); +} + +void CWeaponGauss::StopSpinSound( void ) +{ + if ( m_sndCharge != NULL ) + { + (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndCharge ); + m_sndCharge = NULL; + } +} |