diff options
Diffstat (limited to 'game/shared/dod/weapon_dodbase.cpp')
| -rw-r--r-- | game/shared/dod/weapon_dodbase.cpp | 1416 |
1 files changed, 1416 insertions, 0 deletions
diff --git a/game/shared/dod/weapon_dodbase.cpp b/game/shared/dod/weapon_dodbase.cpp new file mode 100644 index 0000000..3c1fc98 --- /dev/null +++ b/game/shared/dod/weapon_dodbase.cpp @@ -0,0 +1,1416 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "in_buttons.h" +#include "takedamageinfo.h" +#include "weapon_dodbase.h" +#include "ammodef.h" +#include "dod_gamerules.h" + +#ifdef CLIENT_DLL +extern IVModelInfoClient* modelinfo; +#else +extern IVModelInfo* modelinfo; +#include "ilagcompensationmanager.h" +#endif + + +#if defined( CLIENT_DLL ) + + #include "vgui/ISurface.h" + #include "vgui_controls/Controls.h" + #include "c_dod_player.h" + #include "hud_crosshair.h" + #include "SoundEmitterSystem/isoundemittersystembase.h" + +#else + + #include "dod_player.h" + +#endif + +#include "effect_dispatch_data.h" + + +// ----------------------------------------------------------------------------- // +// Global functions. +// ----------------------------------------------------------------------------- // + +bool IsAmmoType( int iAmmoType, const char *pAmmoName ) +{ + return GetAmmoDef()->Index( pAmmoName ) == iAmmoType; +} + +//-------------------------------------------------------------------------------------------------------- +// +// Given a weapon ID, return its alias +// +const char *WeaponIDToAlias( int id ) +{ + if ( (id >= WEAPON_MAX) || (id < 0) ) + return NULL; + + return s_WeaponAliasInfo[id]; +} + +// ----------------------------------------------------------------------------- // +// CWeaponDODBase tables. +// ----------------------------------------------------------------------------- // + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDODBase, DT_WeaponDODBase ) + +BEGIN_NETWORK_TABLE( CWeaponDODBase, DT_WeaponDODBase ) +#ifdef CLIENT_DLL + RecvPropInt( RECVINFO(m_iReloadModelIndex) ), + RecvPropVector( RECVINFO( m_vInitialDropVelocity ) ), + RecvPropTime( RECVINFO( m_flSmackTime ) ) +#else + SendPropVector( SENDINFO( m_vInitialDropVelocity ), + 20, // nbits + 0, // flags + -3000, // low value + 3000 // high value + ), + SendPropModelIndex( SENDINFO(m_iReloadModelIndex) ), + SendPropTime( SENDINFO( m_flSmackTime ) ) +#endif +END_NETWORK_TABLE() + +LINK_ENTITY_TO_CLASS( weapon_dod_base, CWeaponDODBase ); + + +#ifdef GAME_DLL + + BEGIN_DATADESC( CWeaponDODBase ) + + DEFINE_FUNCTION( FallThink ), + DEFINE_FUNCTION( Die ), + + DEFINE_FUNCTION( Smack ) + + END_DATADESC() + +#else + BEGIN_PREDICTION_DATA( CWeaponDODBase ) + DEFINE_PRED_FIELD( m_flSmackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), // for rifle melee attacks + DEFINE_FIELD( m_bInAttack, FIELD_BOOLEAN ) + END_PREDICTION_DATA() +#endif + +Vector head_hull_mins( -16, -16, -18 ); +Vector head_hull_maxs( 16, 16, 18 ); + +void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity ) +{ + int i, j, k; + float distance; + Vector minmaxs[2] = {mins, maxs}; + trace_t tmpTrace; + Vector vecHullEnd = tr.endpos; + Vector vecEnd; + + CTraceFilterSimple filter( pEntity, COLLISION_GROUP_NONE ); + + distance = 1e6f; + + vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2); + UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, &filter, &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, &filter, &tmpTrace ); + if ( tmpTrace.fraction < 1.0 ) + { + float thisDistance = (tmpTrace.endpos - vecSrc).Length(); + if ( thisDistance < distance ) + { + tr = tmpTrace; + distance = thisDistance; + } + } + } + } + } +} + +// ----------------------------------------------------------------------------- // +// CWeaponDODBase implementation. +// ----------------------------------------------------------------------------- // +CWeaponDODBase::CWeaponDODBase() +{ + SetPredictionEligible( true ); + m_bInAttack = false; + m_iAltFireHint = 0; + AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches. + + m_flNextPrimaryAttack = 0; +} + + +bool CWeaponDODBase::IsPredicted() const +{ + return true; +} + +bool CWeaponDODBase::PlayEmptySound() +{ + CPASAttenuationFilter filter( this ); + filter.UsePredictionRules(); + EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" ); + + return false; +} + + +CBasePlayer* CWeaponDODBase::GetPlayerOwner() const +{ + return dynamic_cast< CBasePlayer* >( GetOwner() ); +} + +CDODPlayer* CWeaponDODBase::GetDODPlayerOwner() const +{ + return dynamic_cast< CDODPlayer* >( GetOwner() ); +} + +bool CWeaponDODBase::SendWeaponAnim( int iActivity ) +{ + return BaseClass::SendWeaponAnim( iActivity ); +} + +bool CWeaponDODBase::CanAttack( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + { + return pPlayer->CanAttack(); + } + + return false; +} + +bool CWeaponDODBase::ShouldAutoReload( void ) +{ + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( pPlayer ) + { + return pPlayer->ShouldAutoReload(); + } + + return false; +} + +void CWeaponDODBase::ItemPostFrame() +{ + if ( m_flSmackTime > 0 && gpGlobals->curtime > m_flSmackTime ) + { + Smack(); + m_flSmackTime = -1; + } + + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( !pPlayer ) + return; + +#ifdef _DEBUG + CDODGameRules *mp = DODGameRules(); +#endif + + assert( mp ); + + if ((m_bInReload) && (pPlayer->m_flNextAttack <= gpGlobals->curtime)) + { + // complete the reload. + int j = MIN( GetMaxClip1() - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) ); + + // Add them to the clip + m_iClip1 += j; + pPlayer->RemoveAmmo( j, m_iPrimaryAmmoType ); + + m_bInReload = false; + + FinishReload(); + } + + if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) + { + if ( m_iClip2 != -1 && !pPlayer->GetAmmoCount( GetSecondaryAmmoType() ) ) + { + m_bFireOnEmpty = TRUE; + } + + SecondaryAttack(); + + pPlayer->m_nButtons &= ~IN_ATTACK2; + } + else if ((pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime ) && !m_bInAttack ) + { + if ( (m_iClip1 == 0/* && pszAmmo1()*/) || (GetMaxClip1() == -1 && !pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) ) ) + { + m_bFireOnEmpty = TRUE; + } + + if( CanAttack() ) + PrimaryAttack(); + } + else if ( pPlayer->m_nButtons & IN_RELOAD && GetMaxClip1() != WEAPON_NOCLIP && !m_bInReload && m_flNextPrimaryAttack < gpGlobals->curtime) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + Reload(); + } + else if ( !(pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) ) + { + // no fire buttons down + + m_bFireOnEmpty = false; + + m_bInAttack = false; //reset semi-auto + + if ( !IsUseable() && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + // Intentionally blank -- used to switch weapons here + } + else if( ShouldAutoReload() ) + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + Reload(); + return; + } + } + + WeaponIdle( ); + return; + } +} + +void CWeaponDODBase::WeaponIdle() +{ + if (m_flTimeWeaponIdle > gpGlobals->curtime) + return; + + SendWeaponAnim( GetIdleActivity() ); + + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); +} + +Activity CWeaponDODBase::GetIdleActivity( void ) +{ + return ACT_VM_IDLE; +} + +const CDODWeaponInfo &CWeaponDODBase::GetDODWpnData() const +{ + const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); + const CDODWeaponInfo *pDODInfo; + + #ifdef _DEBUG + pDODInfo = dynamic_cast< const CDODWeaponInfo* >( pWeaponInfo ); + Assert( pDODInfo ); + #else + pDODInfo = static_cast< const CDODWeaponInfo* >( pWeaponInfo ); + #endif + + return *pDODInfo; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CWeaponDODBase::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const +{ + if ( GetPlayerOwner() == NULL ) + { + return BaseClass::GetViewModel(); + } + + return GetWpnData().szViewModel; +} + +void CWeaponDODBase::Precache( void ) +{ + // precache base first, it loads weapon scripts + BaseClass::Precache(); + + PrecacheScriptSound( "Default.ClipEmpty_Rifle" ); + + PrecacheParticleSystem( "muzzle_pistols" ); + PrecacheParticleSystem( "muzzle_fullyautomatic" ); + PrecacheParticleSystem( "muzzle_rifles" ); + PrecacheParticleSystem( "muzzle_rockets" ); + PrecacheParticleSystem( "muzzle_mg42" ); + + PrecacheParticleSystem( "view_muzzle_pistols" ); + PrecacheParticleSystem( "view_muzzle_fullyautomatic" ); + PrecacheParticleSystem( "view_muzzle_rifles" ); + PrecacheParticleSystem( "view_muzzle_rockets" ); + PrecacheParticleSystem( "view_muzzle_mg42" ); + + const CDODWeaponInfo &info = GetDODWpnData(); + + int iWpnNameLen = Q_strlen(info.m_szReloadModel); + +#ifdef DEBUG + // Make sure that if we declare an alt weapon, that we have criteria to show it + // and vice-versa + //Assert( ((info.m_iAltWpnCriteria & (ALTWPN_CRITERIA_RELOADING | ALTWPN_CRITERIA_FIRING)) > 0 ) == + // (iWpnNameLen > 0) ); +#endif + + if( iWpnNameLen > 0 ) + m_iReloadModelIndex = CBaseEntity::PrecacheModel( info.m_szReloadModel ); +} + +bool CWeaponDODBase::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt ) +{ + CBasePlayer *pOwner = GetPlayerOwner(); + if ( !pOwner ) + { + return false; + } + + pOwner->SetAnimationExtension( szAnimExt ); + + SetViewModel(); + SendWeaponAnim( iActivity ); + + pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() ); + m_flNextPrimaryAttack = MAX( m_flNextPrimaryAttack, gpGlobals->curtime ); + m_flNextSecondaryAttack = gpGlobals->curtime; + + SetWeaponVisible( true ); + SetWeaponModelIndex( szWeaponModel ); + + CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); + + Assert( vm ); + + if( vm ) + { + //set sleeves to proper team + switch( pOwner->GetTeamNumber() ) + { + case TEAM_ALLIES: + vm->m_nSkin = SLEEVE_ALLIES; + break; + case TEAM_AXIS: + vm->m_nSkin = SLEEVE_AXIS; + break; + default: + Assert( !"TEAM_UNASSIGNED or spectator getting a view model assigned" ); + break; + } + } + + return true; +} + +void CWeaponDODBase::SetWeaponModelIndex( const char *pName ) +{ + m_iWorldModelIndex = modelinfo->GetModelIndex( pName ); +} + +bool CWeaponDODBase::CanBeSelected( void ) +{ + if ( !VisibleInWeaponSelection() ) + return false; + + return true; +} + +bool CWeaponDODBase::CanDeploy( void ) +{ + return BaseClass::CanDeploy(); +} + +bool CWeaponDODBase::CanHolster( void ) +{ + return BaseClass::CanHolster(); +} + +void CWeaponDODBase::Drop( const Vector &vecVelocity ) +{ +#ifndef CLIENT_DLL + if ( m_iAltFireHint ) + { + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + { + pPlayer->StopHintTimer( m_iAltFireHint ); + } + } +#endif + + // cancel any reload in progress + m_bInReload = false; + + m_flSmackTime = -1; + + m_vInitialDropVelocity = vecVelocity; + + BaseClass::Drop( m_vInitialDropVelocity ); +} + +bool CWeaponDODBase::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->SetFOV( pPlayer, 0 ); // reset the default FOV. + + if ( m_iAltFireHint ) + { + pPlayer->StopHintTimer( m_iAltFireHint ); + } + } +#endif + + m_bInReload = false; + + m_flSmackTime = -1; + + return BaseClass::Holster( pSwitchingTo ); +} + +bool CWeaponDODBase::Deploy() +{ +#ifndef CLIENT_DLL + CDODPlayer *pPlayer = GetDODPlayerOwner(); + + if ( pPlayer ) + { + pPlayer->SetFOV( pPlayer, 0 ); + + if ( m_iAltFireHint ) + { + pPlayer->StartHintTimer( m_iAltFireHint ); + } + } +#endif + + return BaseClass::Deploy(); +} + +#ifdef CLIENT_DLL + + void CWeaponDODBase::PostDataUpdate( DataUpdateType_t updateType ) + { + // We need to do this before the C_BaseAnimating code starts to drive + // clientside animation sequences on this model, which will be using bad sequences for the world model. + int iDesiredModelIndex = 0; + C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer(); + if ( localplayer && localplayer == GetOwner() && !C_BasePlayer::ShouldDrawLocalPlayer() ) // FIXME: use localplayer->ShouldDrawThisPlayer() instead. + { + iDesiredModelIndex = m_iViewModelIndex; + } + else + { + iDesiredModelIndex = GetWorldModelIndex(); + + // Our world models never animate + SetSequence( 0 ); + } + + if ( GetModelIndex() != iDesiredModelIndex ) + { + SetModelIndex( iDesiredModelIndex ); + } + + BaseClass::PostDataUpdate( updateType ); + } + + void CWeaponDODBase::OnDataChanged( DataUpdateType_t type ) + { + if ( m_iState == WEAPON_NOT_CARRIED && m_iOldState != WEAPON_NOT_CARRIED ) + { + // we are being notified of the weapon being dropped + // add an interpolation history so the movement is smoother + + // Now stick our initial velocity into the interpolation history + CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator(); + + interpolator.ClearHistory(); + float changeTime = GetLastChangeTime( LATCH_SIMULATION_VAR ); + + // Add a sample 1 second back. + Vector vCurOrigin = GetLocalOrigin() - m_vInitialDropVelocity; + interpolator.AddToHead( changeTime - 1.0, &vCurOrigin, false ); + + // Add the current sample. + vCurOrigin = GetLocalOrigin(); + interpolator.AddToHead( changeTime, &vCurOrigin, false ); + + Vector estVel; + EstimateAbsVelocity( estVel ); + + /*Msg( "estimated velocity ( %.1f %.1f %.1f ) initial velocity ( %.1f %.1f %.1f )\n", + estVel.x, + estVel.y, + estVel.z, + m_vInitialDropVelocity.m_Value.x, + m_vInitialDropVelocity.m_Value.y, + m_vInitialDropVelocity.m_Value.z );*/ + + OnWeaponDropped(); + } + + BaseClass::OnDataChanged( type ); + + if ( GetPredictable() && !ShouldPredict() ) + ShutdownPredictable(); + } + + int CWeaponDODBase::GetWorldModelIndex( void ) + { + if( m_bUseAltWeaponModel && GetOwner() != NULL ) + return m_iReloadModelIndex; + else + return m_iWorldModelIndex; + } + + bool CWeaponDODBase::ShouldPredict() + { + if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() ) + return true; + + return BaseClass::ShouldPredict(); + } + + void CWeaponDODBase::ProcessMuzzleFlashEvent() + { + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + pPlayer->ProcessMuzzleFlashEvent(); + + BaseClass::ProcessMuzzleFlashEvent(); + } +#else + + + //----------------------------------------------------------------------------- + // Purpose: Get the accuracy derived from weapon and player, and return it + //----------------------------------------------------------------------------- + const Vector& CWeaponDODBase::GetBulletSpread() + { + static Vector cone = VECTOR_CONE_8DEGREES; + return cone; + } + + //----------------------------------------------------------------------------- + // Purpose: + //----------------------------------------------------------------------------- + void CWeaponDODBase::ItemBusyFrame() + { + if( ShouldAutoReload() && !m_bInReload ) + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + Reload(); + } + } + + BaseClass::ItemBusyFrame(); + } + + //----------------------------------------------------------------------------- + // Purpose: Match the anim speed to the weapon speed while crouching + //----------------------------------------------------------------------------- + float CWeaponDODBase::GetDefaultAnimSpeed() + { + return 1.0; + } + + bool CWeaponDODBase::ShouldRemoveOnRoundRestart() + { + if ( GetPlayerOwner() ) + return false; + else + return true; + } + + + //========================================================= + // Materialize - make a CWeaponDODBase visible and tangible + //========================================================= + void CWeaponDODBase::Materialize() + { + if ( IsEffectActive( EF_NODRAW ) ) + { + RemoveEffects( EF_NODRAW ); + DoMuzzleFlash(); + } + + AddSolidFlags( FSOLID_TRIGGER ); + + SetThink (&CWeaponDODBase::SUB_Remove); + SetNextThink( gpGlobals->curtime + 1 ); + } + + //========================================================= + // AttemptToMaterialize - the item is trying to rematerialize, + // should it do so now or wait longer? + //========================================================= + void CWeaponDODBase::AttemptToMaterialize() + { + float time = g_pGameRules->FlWeaponTryRespawn( this ); + + if ( time == 0 ) + { + Materialize(); + return; + } + + SetNextThink( gpGlobals->curtime + time ); + } + + //========================================================= + // CheckRespawn - a player is taking this weapon, should + // it respawn? + //========================================================= + void CWeaponDODBase::CheckRespawn() + { + //GOOSEMAN : Do not respawn weapons! + return; + } + + + //========================================================= + // Respawn- this item is already in the world, but it is + // invisible and intangible. Make it visible and tangible. + //========================================================= + CBaseEntity* CWeaponDODBase::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( &CWeaponDODBase::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; + } + + bool CWeaponDODBase::Reload() + { + return BaseClass::Reload(); + } + + void CWeaponDODBase::Spawn() + { + BaseClass::Spawn(); + + // Set this here to allow players to shoot dropped weapons + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + SetExtraAmmoCount(0); //Start with no additional ammo + + CollisionProp()->UseTriggerBounds( true, 10.0f ); + } + + void CWeaponDODBase::SetDieThink( bool bDie ) + { + if( bDie ) + SetContextThink( &CWeaponDODBase::Die, gpGlobals->curtime + 45.0f, "DieContext" ); + else + SetContextThink( NULL, gpGlobals->curtime, "DieContext" ); + } + + void CWeaponDODBase::Die( void ) + { + UTIL_Remove( this ); + } + +#endif + +void CWeaponDODBase::OnPickedUp( CBaseCombatCharacter *pNewOwner ) +{ + BaseClass::OnPickedUp( pNewOwner ); + +#if !defined( CLIENT_DLL ) + SetDieThink( false ); +#endif +} + +bool CWeaponDODBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity ) +{ + CBaseCombatCharacter *pOwner = GetOwner(); + if (!pOwner) + return false; + + // If I don't have any spare ammo, I can't reload + if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) + return false; + + bool bReload = 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, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); + if ( primary != 0 ) + { + bReload = true; + } + } + + if ( UsesClipsForAmmo2() ) + { + // need to reload secondary clip? + int secondary = min(iClipSize2 - m_iClip2, pOwner->GetAmmoCount(m_iSecondaryAmmoType)); + if ( secondary != 0 ) + { + bReload = true; + } + } + + if ( !bReload ) + return false; + + CDODPlayer *pPlayer = GetDODPlayerOwner(); + if ( pPlayer ) + { +#ifdef CLIENT_DLL + PlayWorldReloadSound( pPlayer ); +#else + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); +#endif + } + + SendWeaponAnim( iActivity ); + + // Play the player's reload animation + if ( pOwner->IsPlayer() ) + { + ( ( CBasePlayer * )pOwner)->SetAnimation( PLAYER_RELOAD ); + } + + float flSequenceEndTime = gpGlobals->curtime + SequenceDuration(); + pOwner->SetNextAttack( flSequenceEndTime ); + m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime; + + m_bInReload = true; + + return true; +} + +#ifdef CLIENT_DLL + void CWeaponDODBase::PlayWorldReloadSound( CDODPlayer *pPlayer ) + { + Assert( pPlayer ); + + const char *shootsound = GetShootSound( RELOAD ); + if ( !shootsound || !shootsound[0] ) + return; + + CSoundParameters params; + + if ( !GetParametersForSound( shootsound, params, NULL ) ) + return; + + // Play weapon sound from the owner + CPASAttenuationFilter filter( pPlayer, params.soundlevel ); + filter.RemoveRecipient( pPlayer ); // no local player, that is done in the model + + EmitSound( filter, pPlayer->entindex(), shootsound, NULL, 0.0 ); + } +#endif + +bool CWeaponDODBase::IsUseable() +{ + CBasePlayer *pPlayer = GetPlayerOwner(); + + if ( Clip1() <= 0 ) + { + if ( pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && GetMaxClip1() != -1 ) + { + // clip is empty (or nonexistant) and the player has no more ammo of this type. + return false; + } + } + + return true; +} + +#ifndef CLIENT_DLL +ConVar dod_meleeattackforcescale( "dod_meleeattackforcescale", "8.0", FCVAR_CHEAT | FCVAR_GAMEDLL ); +#endif + +void CWeaponDODBase::RifleButt( void ) +{ + //MeleeAttack( 60, MELEE_DMG_BUTTSTOCK | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.9f ); +} + +void CWeaponDODBase::Bayonet( void ) +{ + //MeleeAttack( 60, MELEE_DMG_BAYONET | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.9f ); +} + +void CWeaponDODBase::Punch( void ) +{ + MeleeAttack( 60, MELEE_DMG_FIST | MELEE_DMG_SECONDARYATTACK, 0.2f, 0.4f ); +} + +//-------------------------------------------- +// iDamageAmount - how much damage to give +// iDamageType - DMG_ bits +// flDmgDelay - delay between attack and the giving of damage, usually timed to animation +// flAttackDelay - time until we can next attack +//-------------------------------------------- +CBaseEntity *CWeaponDODBase::MeleeAttack( int iDamageAmount, int iDamageType, float flDmgDelay, float flAttackDelay ) +{ + if ( !CanAttack() ) + return NULL; + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + +#if !defined (CLIENT_DLL) + // Move other players back to history positions based on local player's lag + lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() ); +#endif + + Vector vForward, vRight, vUp; + AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp ); + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecEnd = vecSrc + vForward * 48; + + CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE ); + + int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS; + + trace_t tr; + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr ); + + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr ); + + // If the exact forward trace did not hit, try a larger swept box + if ( tr.fraction >= 1.0 ) + { + Vector head_hull_mins( -16, -16, -18 ); + Vector head_hull_maxs( 16, 16, 18 ); + + UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr ); + if ( tr.fraction < 1.0 ) + { + // Calculate the point of intersection of the line (or hull) and the object we hit + // This is and approximation of the "best" intersection + CBaseEntity *pHit = tr.m_pEnt; + if ( !pHit || pHit->IsBSPModel() ) + FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer ); + vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space) + + // Make sure it is in front of us + Vector vecToEnd = vecEnd - vecSrc; + VectorNormalize( vecToEnd ); + + // if zero length, always hit + if ( vecToEnd.Length() > 0 ) + { + float dot = DotProduct( vForward, vecToEnd ); + + // sanity that our hit is within range + if ( abs(dot) < 0.95 ) + { + // fake that we actually missed + tr.fraction = 1.0; + } + } + } + } + + WeaponSound( MELEE_MISS ); + + bool bDidHit = ( tr.fraction < 1.0f ); + + if ( bDidHit ) //if the swing hit + { + // delay the decal a bit + m_trHit = tr; + + // Store the ent in an EHANDLE, just in case it goes away by the time we get into our think function. + m_pTraceHitEnt = tr.m_pEnt; + + m_iSmackDamage = iDamageAmount; + m_iSmackDamageType = iDamageType; + + m_flSmackTime = gpGlobals->curtime + flDmgDelay; + } + + SendWeaponAnim( GetMeleeActivity() ); + + // player animation + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_SECONDARY_ATTACK ); + + m_flNextPrimaryAttack = gpGlobals->curtime + flAttackDelay; + m_flNextSecondaryAttack = gpGlobals->curtime + flAttackDelay; + m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration(); + +#ifndef CLIENT_DLL + IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_weapon_attack" ); + if ( event ) + { + event->SetInt( "attacker", pPlayer->GetUserID() ); + event->SetInt( "weapon", GetAltWeaponID() ); + + gameeventmanager->FireEvent( event ); + } + + lagcompensation->FinishLagCompensation( pPlayer ); +#endif //CLIENT_DLL + + return tr.m_pEnt; +} + +//Think function to delay the impact decal until the animation is finished playing +void CWeaponDODBase::Smack() +{ + Assert( GetPlayerOwner() ); + + if ( !GetPlayerOwner() ) + return; + + CDODPlayer *pPlayer = ToDODPlayer( GetPlayerOwner() ); + + if ( !pPlayer ) + return; + + // Check that we are still facing the victim + Vector vForward, vRight, vUp; + AngleVectors( pPlayer->EyeAngles(), &vForward, &vRight, &vUp ); + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecEnd = vecSrc + vForward * 48; + + CTraceFilterSimple filter( pPlayer, COLLISION_GROUP_NONE ); + + int iTraceMask = MASK_SOLID | CONTENTS_HITBOX | CONTENTS_DEBRIS; + + trace_t tr; + UTIL_TraceLine( vecSrc, vecEnd, iTraceMask, &filter, &tr ); + + const float rayExtension = 40.0f; + UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vForward * rayExtension, iTraceMask, &filter, &tr ); + + if ( tr.fraction >= 1.0 ) + { + Vector head_hull_mins( -16, -16, -18 ); + Vector head_hull_maxs( 16, 16, 18 ); + + UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, &filter, &tr ); + if ( tr.fraction < 1.0 ) + { + // Calculate the point of intersection of the line (or hull) and the object we hit + // This is and approximation of the "best" intersection + CBaseEntity *pHit = tr.m_pEnt; + if ( !pHit || pHit->IsBSPModel() ) + FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer ); + vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space) + } + } + + m_trHit = tr; + + if ( !m_trHit.m_pEnt || (m_trHit.surface.flags & SURF_SKY) ) + return; + + if ( m_trHit.fraction == 1.0 ) + return; + + CPASAttenuationFilter attenuationFilter( this ); + attenuationFilter.UsePredictionRules(); + + if( m_trHit.m_pEnt->IsPlayer() ) + { + if ( m_iSmackDamageType & MELEE_DMG_STRONGATTACK ) + WeaponSound( SPECIAL1 ); + else + WeaponSound( MELEE_HIT ); + } + else + WeaponSound( MELEE_HIT_WORLD ); + + int iDamageType = DMG_CLUB | DMG_NEVERGIB; + +#ifndef CLIENT_DLL + //if they hit the bounding box, just assume a chest hit + if( m_trHit.hitgroup == HITGROUP_GENERIC ) + m_trHit.hitgroup = HITGROUP_CHEST; + + float flDamage = (float)m_iSmackDamage; + + CTakeDamageInfo info( pPlayer, pPlayer, flDamage, iDamageType ); + + if ( m_iSmackDamageType & MELEE_DMG_SECONDARYATTACK ) + info.SetDamageCustom( MELEE_DMG_SECONDARYATTACK ); + + float flScale = (1.0f / flDamage) * dod_meleeattackforcescale.GetFloat(); + + Vector vecForceDir = vForward; + + CalculateMeleeDamageForce( &info, vecForceDir, m_trHit.endpos, flScale ); + + Assert( m_trHit.m_pEnt != GetPlayerOwner() ); + + m_trHit.m_pEnt->DispatchTraceAttack( info, vForward, &m_trHit ); + ApplyMultiDamage(); +#endif + + // We've gotten minidumps where this happened. + if ( !GetPlayerOwner() ) + return; + + CEffectData data; + data.m_vOrigin = m_trHit.endpos; + data.m_vStart = m_trHit.startpos; + data.m_nSurfaceProp = m_trHit.surface.surfaceProps; + data.m_nHitBox = m_trHit.hitbox; +#ifdef CLIENT_DLL + data.m_hEntity = m_trHit.m_pEnt->GetRefEHandle(); +#else + data.m_nEntIndex = m_trHit.m_pEnt->entindex(); +#endif + + CPASFilter effectfilter( data.m_vOrigin ); + +#ifndef CLIENT_DLL + effectfilter.RemoveRecipient( GetPlayerOwner() ); +#endif + + data.m_vAngles = GetPlayerOwner()->GetAbsAngles(); + data.m_fFlags = 0x1; //IMPACT_NODECAL; + data.m_nDamageType = iDamageType; + + bool bHitPlayer = m_trHit.m_pEnt && m_trHit.m_pEnt->IsPlayer(); + + // don't do any impacts if we hit a teammate and ff is off + if ( bHitPlayer && + m_trHit.m_pEnt->GetTeamNumber() == GetPlayerOwner()->GetTeamNumber() && + !friendlyfire.GetBool() ) + return; + + if ( bHitPlayer ) + { + te->DispatchEffect( effectfilter, 0.0, data.m_vOrigin, "Impact", data ); + } + else if ( m_iSmackDamageType & MELEE_DMG_EDGE ) + { + data.m_nDamageType = DMG_SLASH; + te->DispatchEffect( effectfilter, 0.0, data.m_vOrigin, "KnifeSlash", data ); + } +} + +#if defined( CLIENT_DLL ) + + float g_lateralBob = 0; + float g_verticalBob = 0; + + static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); + static ConVar cl_bob( "cl_bob","0.002" ); + static ConVar cl_bobup( "cl_bobup","0.5" ); + + // Register these cvars if needed for easy tweaking + static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_iyaw_level( "v_iyaw_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_iroll_level( "v_iroll_level", "0.1"/*, FCVAR_UNREGISTERED*/ ); + static ConVar v_ipitch_level( "v_ipitch_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); + + //----------------------------------------------------------------------------- + // Purpose: + // Output : float + //----------------------------------------------------------------------------- + float CWeaponDODBase::CalcViewmodelBob( void ) + { + static float bobtime; + static float lastbobtime; + static float lastspeed; + float cycle; + + CBasePlayer *player = ToBasePlayer( GetOwner() ); + //Assert( player ); + + //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 - lastbobtime) * 320.0f ); + + // don't allow too big speed changes + speed = clamp( speed, lastspeed-flmaxSpeedDelta, lastspeed+flmaxSpeedDelta ); + speed = clamp( speed, -320, 320 ); + + lastspeed = 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 ); + + bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; + lastbobtime = gpGlobals->curtime; + + //Calculate the vertical bob + cycle = bobtime - (int)(bobtime/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()); + } + + g_verticalBob = speed*0.005f; + g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); + + g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); + + //Calculate the lateral bob + cycle = bobtime - (int)(bobtime/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()); + } + + g_lateralBob = speed*0.005f; + g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); + g_lateralBob = clamp( g_lateralBob, -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: + // Input : &origin - + // &angles - + // viewmodelindex - + //----------------------------------------------------------------------------- + void CWeaponDODBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) + { + Vector forward, right; + AngleVectors( angles, &forward, &right, NULL ); + + CalcViewmodelBob(); + + // Apply bob, but scaled down to 40% + VectorMA( origin, g_verticalBob * 0.4f, forward, origin ); + + // Z bob a bit more + origin[2] += g_verticalBob * 0.1f; + + // bob the angles + angles[ ROLL ] += g_verticalBob * 0.5f; + angles[ PITCH ] -= g_verticalBob * 0.4f; + + angles[ YAW ] -= g_lateralBob * 0.3f; + + // VectorMA( origin, g_lateralBob * 0.2f, right, origin ); + } + + #include "c_te_effect_dispatch.h" + + #define NUM_MUZZLE_FLASH_TYPES 4 + + bool CWeaponDODBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) + { + if( event == 5001 ) + { + if ( ShouldDrawMuzzleFlash() ) + { + Assert( GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() ); + + const char *pszMuzzleFlashEffect; + + switch( GetDODWpnData().m_iMuzzleFlashType ) + { + case DOD_MUZZLEFLASH_PISTOL: + pszMuzzleFlashEffect = "view_muzzle_pistols"; + break; + case DOD_MUZZLEFLASH_AUTO: + pszMuzzleFlashEffect = "view_muzzle_fullyautomatic"; + break; + case DOD_MUZZLEFLASH_RIFLE: + pszMuzzleFlashEffect = "view_muzzle_rifles"; + break; + case DOD_MUZZLEFLASH_MG: + pszMuzzleFlashEffect = "view_muzzle_miniguns"; + break; + case DOD_MUZZLEFLASH_ROCKET: + pszMuzzleFlashEffect = "view_muzzle_rockets"; + break; + case DOD_MUZZLEFLASH_MG42: + pszMuzzleFlashEffect = "view_muzzle_mg42"; + break; + default: + pszMuzzleFlashEffect = NULL; + break; + } + + if ( pszMuzzleFlashEffect ) + { + pViewModel->ParticleProp()->Create( pszMuzzleFlashEffect, PATTACH_POINT_FOLLOW, 1 ); + } + } + return true; + } + else if( event == 6002 ) + { + CEffectData data; + data.m_nHitBox = atoi( options ); + data.m_hEntity = GetPlayerOwner() ? GetPlayerOwner()->GetRefEHandle() : INVALID_EHANDLE_INDEX; + pViewModel->GetAttachment( 2, data.m_vOrigin, data.m_vAngles ); + + DispatchEffect( "DOD_EjectBrass", data ); + return true; + } + + return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); + } + + bool CWeaponDODBase::ShouldAutoEjectBrass( void ) + { + // Don't eject brass if further than N units from the local player + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pLocalPlayer ) + return true; + + float flMaxDistSqr = 250; + flMaxDistSqr *= flMaxDistSqr; + + float flDistSqr = pLocalPlayer->EyePosition().DistToSqr( GetAbsOrigin() ); + return ( flDistSqr < flMaxDistSqr ); + } + + bool CWeaponDODBase::GetEjectBrassShellType( void ) + { + return 1; + } + + void CWeaponDODBase::SetUseAltModel( bool bUseAltModel ) + { + m_bUseAltWeaponModel = bUseAltModel; + } + + void CWeaponDODBase::CheckForAltWeapon( int iCurrentState ) + { + int iCriteria = GetDODWpnData().m_iAltWpnCriteria; + + bool bUseAltModel = false; + + if( ( iCriteria & iCurrentState ) != 0 ) + bUseAltModel = true; + + SetUseAltModel( bUseAltModel ); + } + + Vector CWeaponDODBase::GetDesiredViewModelOffset( C_DODPlayer *pOwner ) + { + Vector viewOffset = pOwner->GetViewOffset(); + + float flPercent = ( viewOffset.z - VEC_PRONE_VIEW_SCALED( pOwner ).z ) / ( VEC_VIEW_SCALED( pOwner ).z - VEC_PRONE_VIEW_SCALED( pOwner ).z ); + + return ( flPercent * GetDODWpnData().m_vecViewNormalOffset + + ( 1.0 - flPercent ) * GetDODWpnData().m_vecViewProneOffset ); + } + + bool CWeaponDODBase::ShouldDraw( void ) + { + if ( GetModel() == NULL ) + { + // XXX(johns): Removed, doesn't seem to be the proper spot for this warning given that weapons can call + // ShouldDraw before their properties are filled. + + // C_DODPlayer *pPlayer = C_DODPlayer::GetLocalDODPlayer(); + + // Warning( "BADNESS! Tell Matt that the weapon '%s' tried to draw with a null model ( %d, %d, %s ) \n", + // GetDODWpnData().szClassName, + // m_iWorldModelIndex.Get(), m_iReloadModelIndex.Get(), m_bUseAltWeaponModel ? "alt" : "not alt" ); + + return false; + } + + return BaseClass::ShouldDraw(); + } + +#else + + void CWeaponDODBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) + { + + } + + float CWeaponDODBase::CalcViewmodelBob( void ) + { + return 0.0f; + } + + void CWeaponDODBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) + { + if ( CanDrop() == false ) + return; + + CDODPlayer *pPlayer = ToDODPlayer( pActivator ); + + if ( pPlayer ) + { + pPlayer->PickUpWeapon( this ); + } + } + +#endif + |