Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

559 lines
16 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Chargeable Plasma & Shield combo
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "basetfplayer_shared.h"
#include "weapon_combatshield.h"
#include "in_buttons.h"
#include "weapon_combat_usedwithshieldbase.h"
#include "plasmaprojectile.h"
#include "in_buttons.h"
#include "tf_shareddefs.h"
#if defined( CLIENT_DLL )
#include "iefx.h"
#include "dlight.h"
#include "clienteffectprecachesystem.h"
#include "beamdraw.h"
#define CWeaponCombatPlasmaRifle C_WeaponCombatPlasmaRifle
#define CWeaponCombatPlasmaRifleHuman C_WeaponCombatPlasmaRifleHuman
#define CWeaponCombatPlasmaRifleAlien C_WeaponCombatPlasmaRifleAlien
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#if defined( CLIENT_DLL )
class CChargeBall;
// Precache the effects
CLIENTEFFECT_REGISTER_BEGIN( PrecacheWeaponCombatPlasmaRifle )
CLIENTEFFECT_MATERIAL( "sprites/chargeball_team1" )
CLIENTEFFECT_MATERIAL( "sprites/chargeball_team2" )
CLIENTEFFECT_REGISTER_END()
#endif
// Damage CVars
ConVar weapon_combat_plasmarifle_damage( "weapon_combat_plasmarifle_damage","10", FCVAR_REPLICATED, "Plasma Rifle maximum damage" );
ConVar weapon_combat_plasmarifle_range( "weapon_combat_plasmarifle_range","500", FCVAR_REPLICATED, "Plasma Rifle maximum range" );
ConVar weapon_combat_plasmarifle_radius( "weapon_combat_plasmarifle_radius","90", FCVAR_REPLICATED, "Plasma Rifle explosion radius when charged" );
ConVar weapon_combat_plasmarifle_ducking_mod( "weapon_combat_plasmarifle_ducking_mod", "0.6f", FCVAR_REPLICATED, "Plasma Rifle ducking speed modifier" );
//-----------------------------------------------------------------------------
// Purpose: Shared viersion of CWeaponCombatPlasmaRifle
//-----------------------------------------------------------------------------
class CWeaponCombatPlasmaRifle : public CWeaponCombatUsedWithShieldBase
{
DECLARE_CLASS( CWeaponCombatPlasmaRifle, CWeaponCombatUsedWithShieldBase );
public:
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
#if !defined( CLIENT_DLL )
DECLARE_DATADESC();
#endif
CWeaponCombatPlasmaRifle( void ) {}
virtual void ItemPostFrame( void );
virtual void PrimaryAttack( void );
virtual float GetFireRate( void );
virtual void Spawn();
virtual bool Deploy( void );
virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
void ChargeThink( void );
virtual float GetDefaultAnimSpeed( void );
// All predicted weapons need to implement and return true
virtual bool IsPredicted( void ) const
{
return true;
}
private:
CWeaponCombatPlasmaRifle( const CWeaponCombatPlasmaRifle & );
public:
#if defined( CLIENT_DLL )
virtual bool ShouldPredict( void )
{
if ( GetOwner() &&
GetOwner() == C_BasePlayer::GetLocalPlayer() )
return true;
return BaseClass::ShouldPredict();
}
virtual void OnDataChanged( DataUpdateType_t updateType );
virtual int DrawModel( int flags );
virtual void ViewModelDrawn( CBaseViewModel *pBaseViewModel );
virtual void ClientThink( );
virtual bool IsTransparent( );
private:
// Purpose: Draws the charging effect
void DrawChargingEffect( float flSize, CBaseAnimating *pAttachedEnt );
CMaterialReference m_hMaterial;
#endif
private:
CNetworkVar( float, m_flPower );
CNetworkVar( bool, m_bCharging );
};
//-----------------------------------------------------------------------------
// Spawn weapon
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::Spawn()
{
BaseClass::Spawn();
m_flPower = 1;
m_bCharging = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::ItemPostFrame( void )
{
ChargeThink();
CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
if (!pOwner)
return;
if ( UsesClipsForAmmo1() )
{
CheckReload();
}
// Handle charge firing
if ( GetShieldState() == SS_DOWN && !m_bInReload )
{
if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
{
if ( m_iClip1 > 0 )
{
// Fire the plasma shot
PrimaryAttack();
m_flPower = 1.0;
}
else
{
Reload();
}
}
// Reload button (or fire button when we're out of ammo)
if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
{
if ( pOwner->m_nButtons & IN_RELOAD )
{
Reload();
}
else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
{
if ( !m_iClip1 && HasPrimaryAmmo() )
{
Reload();
}
}
}
}
// Prevent shield post frame if we're not ready to attack, or we're charging
AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::PrimaryAttack( void )
{
CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
if (!pPlayer)
return;
WeaponSound(SINGLE);
// Fire the bullets
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
Vector vecAiming;
pPlayer->EyeVectors( &vecAiming );
PlayAttackAnimation( GetPrimaryAttackActivity() );
// Shift it down a bit so the firer can see it
Vector right, forward;
AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, &forward, &right, NULL );
Vector vecStartSpot = vecSrc;
Vector gunOffset = Vector(0,0,-8) + right * 12 + forward * 16;
CPowerPlasmaProjectile *pPlasma = CPowerPlasmaProjectile::CreatePredicted( vecStartSpot, vecAiming, gunOffset, DMG_ENERGYBEAM, pPlayer );
if ( pPlasma )
{
pPlasma->SetDamage( m_flPower * weapon_combat_plasmarifle_damage.GetFloat() );
pPlasma->m_hOwner = pPlayer;
pPlasma->SetPower( m_flPower );
// Calculate range based upon charge power
float flRange = weapon_combat_plasmarifle_range.GetFloat() + RemapVal( m_flPower, 1.0, MAX_RIFLE_POWER, 0, weapon_combat_plasmarifle_range.GetFloat() * 0.75 );
pPlasma->SetMaxRange( flRange );
pPlasma->Activate();
}
// Go explosive if fully charged
// if ( m_flPower >= MAX_RIFLE_POWER )
// {
// pPlasma->SetExplosive( weapon_combat_plasmarifle_radius.GetFloat() );
// pPlasma->SetPlasmaType( PLASMATYPE_PLASMABALL_EXPLOSIVE );
// }
m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
m_iClip1 = m_iClip1 - 1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CWeaponCombatPlasmaRifle::GetFireRate( void )
{
float flFireRate = ( SequenceDuration() * 0.4 ) + SHARED_RANDOMFLOAT( 0.0, 0.035f );
// Get the player.
CBaseTFPlayer *pPlayer = ( CBaseTFPlayer* )GetOwner();
if ( pPlayer )
{
// Fire more rapidly when we are ducking.
if ( pPlayer->GetFlags() & FL_DUCKING )
{
flFireRate *= weapon_combat_plasmarifle_ducking_mod.GetFloat();
}
}
return flFireRate;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CWeaponCombatPlasmaRifle::Deploy( void )
{
if ( BaseClass::Deploy() )
{
m_bCharging = true;
GainedNewTechnology(NULL);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Our player just died
//-----------------------------------------------------------------------------
bool CWeaponCombatPlasmaRifle::Holster( CBaseCombatWeapon *pSwitchingTo )
{
if ( BaseClass::Holster(pSwitchingTo) )
{
m_bCharging = false;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Match the anim speed to the weapon speed while crouching
//-----------------------------------------------------------------------------
float CWeaponCombatPlasmaRifle::GetDefaultAnimSpeed( void )
{
if ( GetOwner() && GetOwner()->IsPlayer() )
{
if ( GetOwner()->GetFlags() & FL_DUCKING )
return (1.0 + (1.0 - weapon_combat_plasmarifle_ducking_mod.GetFloat()) );
}
return 1.0;
}
//-----------------------------------------------------------------------------
// Purpose: Charge up over time
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::ChargeThink( void )
{
if ( !m_bCharging )
return;
if ( IsOwnerEMPed() )
{
m_flPower = 1;
}
else if ( m_iClip1 > 0 && m_flPower < MAX_RIFLE_POWER )
{
m_flPower = MIN( MAX_RIFLE_POWER, m_flPower + (((MAX_RIFLE_POWER-1.0) / RIFLE_CHARGE_TIME) * gpGlobals->frametime ) );
}
}
#if defined( CLIENT_DLL )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::OnDataChanged( DataUpdateType_t updateType )
{
SetPredictionEligible( true );
BaseClass::OnDataChanged( updateType );
if (updateType == DATA_UPDATE_CREATED)
{
if ( GetTeamNumber() == 1 )
m_hMaterial.Init( "sprites/chargeball_team1", TEXTURE_GROUP_CLIENT_EFFECTS );
else
m_hMaterial.Init( "sprites/chargeball_team2", TEXTURE_GROUP_CLIENT_EFFECTS );
}
if (WeaponState() == WEAPON_IS_ACTIVE)
{
// Start thinking so we can manipulate the light
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
else
{
SetNextClientThink( CLIENT_THINK_NEVER );
}
}
//-----------------------------------------------------------------------------
// Deal with dynamic lighting
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::ClientThink( )
{
BaseClass::ClientThink();
if (!inv_demo.GetInt())
{
C_BaseTFPlayer *pPlayer = (C_BaseTFPlayer *)GetOwner();
if ( !pPlayer || (pPlayer->GetHealth() <= 0) || !IsDormant() )
{
SetNextClientThink( CLIENT_THINK_NEVER );
return;
}
// FIXME: dl->origin should be based on the attachment point
dlight_t *dl = effects->CL_AllocDlight( entindex() );
dl->origin = GetRenderOrigin();
if (GetTeamNumber() == 1)
{
dl->color.r = 40;
dl->color.g = 60;
dl->color.b = 250;
}
else
{
dl->color.r = 250;
dl->color.g = 60;
dl->color.b = 40;
}
dl->color.exponent = 7;
dl->radius = 20 * m_flPower + 10;
dl->die = gpGlobals->curtime + 0.01;
}
}
//-----------------------------------------------------------------------------
// Purpose: Draws the charging effect
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::DrawChargingEffect( float flSize, CBaseAnimating *pAttachedEnt )
{
if (!pAttachedEnt)
return;
Vector vecMuzzleOrigin, vecBarrelOrigin;
QAngle angMuzzleAngles, angBarrelAngles;
int iMuzzle = pAttachedEnt->LookupAttachment( "muzzle" );
//int iBarrel = pAttachedEnt->LookupAttachment( "barrel" );
if ( pAttachedEnt->GetAttachment( iMuzzle, vecMuzzleOrigin, angMuzzleAngles ) )
{
// View model attachments are modified so you can place entities at the attachment
// point and when they are rendered (with a different FOV than the view model itself uses)
// they will render in the right spot when the view model is drawn.
//
// In this case though, we are rendering at the same time as the view model, so we want
// the attachment point before the correction has been applied.
pAttachedEnt->UncorrectViewModelAttachment( vecMuzzleOrigin );
// If I'm fully charged, put funky effects on the ball
materials->Bind( m_hMaterial, this );
if ( m_flPower >= MAX_RIFLE_POWER )
{
float frac = fmod( gpGlobals->curtime, 1.0 );
frac *= 2 * M_PI;
frac = sin( frac );
flSize += (frac * 2) - 1.5;
int colorFade = 190 + (int)( frac * 32.0f );
color32 color = { 0, 0, 0, 255 };
if ( GetTeamNumber() == 1 )
{
color.r = colorFade;
color.g = colorFade;
}
else
{
color.g = colorFade;
}
DrawSprite( vecMuzzleOrigin, flSize, flSize, color );
}
else
{
color32 color = { 255, 255, 255, 255 };
DrawSprite( vecMuzzleOrigin, flSize, flSize, color );
}
}
}
//-----------------------------------------------------------------------------
// We're transparent because we draw a transparent charging effect
//-----------------------------------------------------------------------------
bool CWeaponCombatPlasmaRifle::IsTransparent( )
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Draws the model
//-----------------------------------------------------------------------------
int CWeaponCombatPlasmaRifle::DrawModel( int flags )
{
int retval = BaseClass::DrawModel( flags );
if (retval == 0)
return 0;
if (IsCarrierAlive())
{
// FIXME: Maybe do some client-side simulation on the size?
// It may get jerky otherwise
// Draw the charging effect
float flSize = 10 * m_flPower + 5;
DrawChargingEffect( flSize, this );
}
return retval;
}
//-----------------------------------------------------------------------------
// Purpose: Draws the model
//-----------------------------------------------------------------------------
void CWeaponCombatPlasmaRifle::ViewModelDrawn( CBaseViewModel *pBaseViewModel )
{
// Draw the charging effect
float flSize = 4 * m_flPower + 1;
if ( m_iClip1 > 0 )
{
DrawChargingEffect( flSize, pBaseViewModel );
}
}
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaRifle, DT_WeaponCombatPlasmaRifle )
BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaRifle, DT_WeaponCombatPlasmaRifle )
#if !defined( CLIENT_DLL )
SendPropFloat( SENDINFO( m_flPower ), 14, SPROP_ROUNDUP, 1.0f, MAX_RIFLE_POWER ),
SendPropInt( SENDINFO( m_bCharging ), 1, SPROP_UNSIGNED ),
#else
RecvPropFloat( RECVINFO( m_flPower ) ),
RecvPropInt( RECVINFO( m_bCharging ) ),
#endif
END_NETWORK_TABLE()
LINK_ENTITY_TO_CLASS( weapon_combat_plasmarifle_base, CWeaponCombatPlasmaRifle );
BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaRifle )
DEFINE_PRED_FIELD_TOL( m_flPower, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.05f ),
DEFINE_PRED_FIELD( m_bCharging, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#if !defined( CLIENT_DLL )
BEGIN_DATADESC( CWeaponCombatPlasmaRifle )
// Function Pointers
DEFINE_FUNCTION( ChargeThink ),
END_DATADESC()
// PRECACHE_WEAPON_REGISTER(weapon_combat_plasmarifle_base);
#endif
//-----------------------------------------------------------------------------
// Purpose: Need to do different art on client vs server
//-----------------------------------------------------------------------------
class CWeaponCombatPlasmaRifleHuman : public CWeaponCombatPlasmaRifle
{
DECLARE_CLASS( CWeaponCombatPlasmaRifleHuman, CWeaponCombatPlasmaRifle );
public:
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
CWeaponCombatPlasmaRifleHuman( void ) {}
private:
CWeaponCombatPlasmaRifleHuman( const CWeaponCombatPlasmaRifleHuman & );
};
//-----------------------------------------------------------------------------
// Purpose: Need to do different art on client vs server
//-----------------------------------------------------------------------------
class CWeaponCombatPlasmaRifleAlien : public CWeaponCombatPlasmaRifle
{
DECLARE_CLASS( CWeaponCombatPlasmaRifleAlien, CWeaponCombatPlasmaRifle );
public:
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
CWeaponCombatPlasmaRifleAlien( void ) {}
private:
CWeaponCombatPlasmaRifleAlien( const CWeaponCombatPlasmaRifleAlien & );
};
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaRifleHuman, DT_WeaponCombatPlasmaRifleHuman )
BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaRifleHuman, DT_WeaponCombatPlasmaRifleHuman )
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaRifleHuman )
END_PREDICTION_DATA()
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaRifleAlien, DT_WeaponCombatPlasmaRifleAlien )
BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaRifleAlien, DT_WeaponCombatPlasmaRifleAlien )
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaRifleAlien )
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( weapon_combat_plasmarifle, CWeaponCombatPlasmaRifleHuman );
LINK_ENTITY_TO_CLASS( weapon_combat_plasmarifle_alien, CWeaponCombatPlasmaRifleAlien );
PRECACHE_WEAPON_REGISTER(weapon_combat_plasmarifle);
PRECACHE_WEAPON_REGISTER(weapon_combat_plasmarifle_alien);