mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-29 09:33:00 +00:00
530 lines
12 KiB
C++
530 lines
12 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Egon
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "npcevent.h"
|
|
#include "hl1mp_basecombatweapon_shared.h"
|
|
#include "Sprite.h"
|
|
#include "beam_shared.h"
|
|
#include "takedamageinfo.h"
|
|
//#include "basecombatcharacter.h"
|
|
//#include "AI_BaseNPC.h"
|
|
|
|
#ifdef CLIENT_DLL
|
|
#include "c_baseplayer.h"
|
|
#include "hl1/hl1_c_player.h"
|
|
#else
|
|
#include "player.h"
|
|
#include "hl1_player.h"
|
|
#endif
|
|
|
|
//#include "player.h"
|
|
#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 "IEffects.h"
|
|
#ifdef CLIENT_DLL
|
|
#include "c_te_effect_dispatch.h"
|
|
#else
|
|
#include "te_effect_dispatch.h"
|
|
#endif
|
|
|
|
|
|
enum EGON_FIRESTATE { FIRE_OFF, FIRE_STARTUP, FIRE_CHARGE };
|
|
|
|
#define EGON_PULSE_INTERVAL 0.1
|
|
#define EGON_DISCHARGE_INTERVAL 0.1
|
|
|
|
#define EGON_BEAM_SPRITE "sprites/xbeam1.vmt"
|
|
#define EGON_FLARE_SPRITE "sprites/XSpark1.vmt"
|
|
|
|
extern ConVar sk_plr_dmg_egon_wide;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CWeaponEgon
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef CLIENT_DLL
|
|
#define CWeaponEgon C_WeaponEgon
|
|
#endif
|
|
|
|
class CWeaponEgon : public CBaseHL1MPCombatWeapon
|
|
{
|
|
DECLARE_CLASS( CWeaponEgon, CBaseHL1MPCombatWeapon );
|
|
public:
|
|
|
|
DECLARE_NETWORKCLASS();
|
|
DECLARE_PREDICTABLE();
|
|
|
|
CWeaponEgon(void);
|
|
|
|
virtual bool Deploy( void );
|
|
void PrimaryAttack( void );
|
|
virtual void Precache( void );
|
|
|
|
void SecondaryAttack( void )
|
|
{
|
|
PrimaryAttack();
|
|
}
|
|
|
|
void WeaponIdle( void );
|
|
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
|
|
|
|
// DECLARE_SERVERCLASS();
|
|
// DECLARE_DATADESC();
|
|
|
|
private:
|
|
bool HasAmmo( void );
|
|
void UseAmmo( int count );
|
|
void Attack( void );
|
|
void EndAttack( void );
|
|
void Fire( const Vector &vecOrigSrc, const Vector &vecDir );
|
|
void UpdateEffect( const Vector &startPoint, const Vector &endPoint );
|
|
void CreateEffect( void );
|
|
void DestroyEffect( void );
|
|
|
|
EGON_FIRESTATE m_fireState;
|
|
float m_flAmmoUseTime; // since we use < 1 point of ammo per update, we subtract ammo on a timer.
|
|
float m_flShakeTime;
|
|
float m_flStartFireTime;
|
|
float m_flDmgTime;
|
|
CHandle<CSprite> m_hSprite;
|
|
CHandle<CBeam> m_hBeam;
|
|
CHandle<CBeam> m_hNoise;
|
|
};
|
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponEgon, DT_WeaponEgon );
|
|
|
|
BEGIN_NETWORK_TABLE( CWeaponEgon, DT_WeaponEgon )
|
|
END_NETWORK_TABLE()
|
|
|
|
BEGIN_PREDICTION_DATA( CWeaponEgon )
|
|
END_PREDICTION_DATA()
|
|
|
|
LINK_ENTITY_TO_CLASS( weapon_egon, CWeaponEgon );
|
|
PRECACHE_WEAPON_REGISTER( weapon_egon );
|
|
|
|
/*
|
|
IMPLEMENT_SERVERCLASS_ST( CWeaponEgon, DT_WeaponEgon )
|
|
END_SEND_TABLE()
|
|
*/
|
|
|
|
/*
|
|
BEGIN_DATADESC( CWeaponEgon )
|
|
DEFINE_FIELD( m_fireState, FIELD_INTEGER ),
|
|
DEFINE_FIELD( m_flAmmoUseTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flShakeTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flStartFireTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
|
|
DEFINE_FIELD( m_hSprite, FIELD_EHANDLE ),
|
|
DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
|
|
DEFINE_FIELD( m_hNoise, FIELD_EHANDLE ),
|
|
END_DATADESC()
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CWeaponEgon::CWeaponEgon( void )
|
|
{
|
|
m_bReloadsSingly = false;
|
|
m_bFiresUnderwater = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWeaponEgon::Precache( void )
|
|
{
|
|
PrecacheScriptSound( "Weapon_Gluon.Start" );
|
|
PrecacheScriptSound( "Weapon_Gluon.Run" );
|
|
PrecacheScriptSound( "Weapon_Gluon.Off" );
|
|
|
|
PrecacheModel( EGON_BEAM_SPRITE );
|
|
PrecacheModel( EGON_FLARE_SPRITE );
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
bool CWeaponEgon::Deploy( void )
|
|
{
|
|
m_fireState = FIRE_OFF;
|
|
|
|
return BaseClass::Deploy();
|
|
}
|
|
|
|
bool CWeaponEgon::HasAmmo( void )
|
|
{
|
|
CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CWeaponEgon::UseAmmo( int count )
|
|
{
|
|
CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) >= count )
|
|
pPlayer->RemoveAmmo( count, m_iPrimaryAmmoType );
|
|
else
|
|
pPlayer->RemoveAmmo( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ), m_iPrimaryAmmoType );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWeaponEgon::PrimaryAttack( void )
|
|
{
|
|
CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// don't fire underwater
|
|
if ( pPlayer->GetWaterLevel() == 3 )
|
|
{
|
|
if ( m_fireState != FIRE_OFF || m_hBeam )
|
|
{
|
|
EndAttack();
|
|
}
|
|
else
|
|
{
|
|
WeaponSound( EMPTY );
|
|
}
|
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
|
|
return;
|
|
}
|
|
|
|
Vector vecAiming = pPlayer->GetAutoaimVector( 0 );
|
|
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
|
|
|
|
switch( m_fireState )
|
|
{
|
|
case FIRE_OFF:
|
|
{
|
|
if ( !HasAmmo() )
|
|
{
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.25;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.25;
|
|
WeaponSound( EMPTY );
|
|
return;
|
|
}
|
|
|
|
m_flAmmoUseTime = gpGlobals->curtime;// start using ammo ASAP.
|
|
|
|
EmitSound( "Weapon_Gluon.Start" );
|
|
|
|
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
|
|
|
|
m_flShakeTime = 0;
|
|
m_flStartFireTime = gpGlobals->curtime;
|
|
|
|
SetWeaponIdleTime( gpGlobals->curtime + 0.1 );
|
|
|
|
m_flDmgTime = gpGlobals->curtime + EGON_PULSE_INTERVAL;
|
|
m_fireState = FIRE_STARTUP;
|
|
}
|
|
break;
|
|
|
|
case FIRE_STARTUP:
|
|
{
|
|
Fire( vecSrc, vecAiming );
|
|
|
|
if ( gpGlobals->curtime >= ( m_flStartFireTime + 2.0 ) )
|
|
{
|
|
EmitSound( "Weapon_Gluon.Run" );
|
|
|
|
m_fireState = FIRE_CHARGE;
|
|
}
|
|
|
|
if ( !HasAmmo() )
|
|
{
|
|
EndAttack();
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
|
|
}
|
|
}
|
|
case FIRE_CHARGE:
|
|
{
|
|
Fire( vecSrc, vecAiming );
|
|
|
|
if ( !HasAmmo() )
|
|
{
|
|
EndAttack();
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CWeaponEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
|
|
{
|
|
CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 450, 0.1 );
|
|
WeaponSound( SINGLE );
|
|
|
|
Vector vecDest = vecOrigSrc + (vecDir * MAX_TRACE_LENGTH);
|
|
|
|
trace_t tr;
|
|
UTIL_TraceLine( vecOrigSrc, vecDest, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );
|
|
|
|
if ( tr.allsolid )
|
|
return;
|
|
|
|
CBaseEntity *pEntity = tr.m_pEnt;
|
|
if ( pEntity == NULL )
|
|
return;
|
|
|
|
if ( g_pGameRules->IsMultiplayer() )
|
|
{
|
|
if ( m_hSprite )
|
|
{
|
|
if ( pEntity->m_takedamage != DAMAGE_NO )
|
|
{
|
|
m_hSprite->TurnOn();
|
|
}
|
|
else
|
|
{
|
|
m_hSprite->TurnOff();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_flDmgTime < gpGlobals->curtime )
|
|
{
|
|
// wide mode does damage to the ent, and radius damage
|
|
if ( pEntity->m_takedamage != DAMAGE_NO )
|
|
{
|
|
ClearMultiDamage();
|
|
CTakeDamageInfo info( this, pPlayer, sk_plr_dmg_egon_wide.GetFloat() * g_pGameRules->GetDamageMultiplier(), DMG_ENERGYBEAM | DMG_ALWAYSGIB );
|
|
CalculateMeleeDamageForce( &info, vecDir, tr.endpos );
|
|
pEntity->DispatchTraceAttack( info, vecDir, &tr );
|
|
ApplyMultiDamage();
|
|
}
|
|
|
|
if ( g_pGameRules->IsMultiplayer() )
|
|
{
|
|
// radius damage a little more potent in multiplayer.
|
|
#ifndef CLIENT_DLL
|
|
RadiusDamage( CTakeDamageInfo( this, pPlayer, sk_plr_dmg_egon_wide.GetFloat() * g_pGameRules->GetDamageMultiplier() / 4, DMG_ENERGYBEAM | DMG_BLAST | DMG_ALWAYSGIB ), tr.endpos, 128, CLASS_NONE, NULL );
|
|
#endif
|
|
}
|
|
|
|
if ( !pPlayer->IsAlive() )
|
|
return;
|
|
|
|
if ( g_pGameRules->IsMultiplayer() )
|
|
{
|
|
//multiplayer uses 5 ammo/second
|
|
if ( gpGlobals->curtime >= m_flAmmoUseTime )
|
|
{
|
|
UseAmmo( 1 );
|
|
m_flAmmoUseTime = gpGlobals->curtime + 0.2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Wide mode uses 10 charges per second in single player
|
|
if ( gpGlobals->curtime >= m_flAmmoUseTime )
|
|
{
|
|
UseAmmo( 1 );
|
|
m_flAmmoUseTime = gpGlobals->curtime + 0.1;
|
|
}
|
|
}
|
|
|
|
m_flDmgTime = gpGlobals->curtime + EGON_DISCHARGE_INTERVAL;
|
|
if ( m_flShakeTime < gpGlobals->curtime )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
UTIL_ScreenShake( tr.endpos, 5.0, 150.0, 0.75, 250.0, SHAKE_START );
|
|
#endif
|
|
m_flShakeTime = gpGlobals->curtime + 1.5;
|
|
}
|
|
}
|
|
|
|
Vector vecUp, vecRight;
|
|
QAngle angDir;
|
|
|
|
VectorAngles( vecDir, angDir );
|
|
AngleVectors( angDir, NULL, &vecRight, &vecUp );
|
|
|
|
Vector tmpSrc = vecOrigSrc + (vecUp * -8) + (vecRight * 3);
|
|
UpdateEffect( tmpSrc, tr.endpos );
|
|
}
|
|
|
|
void CWeaponEgon::UpdateEffect( const Vector &startPoint, const Vector &endPoint )
|
|
{
|
|
if ( !m_hBeam )
|
|
{
|
|
CreateEffect();
|
|
}
|
|
|
|
if ( m_hBeam )
|
|
{
|
|
m_hBeam->SetStartPos( endPoint );
|
|
}
|
|
|
|
if ( m_hSprite )
|
|
{
|
|
m_hSprite->SetAbsOrigin( endPoint );
|
|
|
|
m_hSprite->m_flFrame += 8 * gpGlobals->frametime;
|
|
if ( m_hSprite->m_flFrame > m_hSprite->Frames() )
|
|
m_hSprite->m_flFrame = 0;
|
|
}
|
|
|
|
if ( m_hNoise )
|
|
{
|
|
m_hNoise->SetStartPos( endPoint );
|
|
}
|
|
}
|
|
|
|
void CWeaponEgon::CreateEffect( void )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
|
|
if ( !pPlayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DestroyEffect();
|
|
|
|
m_hBeam = CBeam::BeamCreate( EGON_BEAM_SPRITE, 3.5 );
|
|
m_hBeam->PointEntInit( GetAbsOrigin(), this );
|
|
m_hBeam->SetBeamFlags( FBEAM_SINENOISE );
|
|
m_hBeam->SetEndAttachment( 1 );
|
|
m_hBeam->AddSpawnFlags( SF_BEAM_TEMPORARY ); // Flag these to be destroyed on save/restore or level transition
|
|
m_hBeam->SetOwnerEntity( pPlayer );
|
|
m_hBeam->SetScrollRate( 10 );
|
|
m_hBeam->SetBrightness( 200 );
|
|
m_hBeam->SetColor( 50, 50, 255 );
|
|
m_hBeam->SetNoise( 0.2 );
|
|
|
|
m_hNoise = CBeam::BeamCreate( EGON_BEAM_SPRITE, 5.0 );
|
|
m_hNoise->PointEntInit( GetAbsOrigin(), this );
|
|
m_hNoise->SetEndAttachment( 1 );
|
|
m_hNoise->AddSpawnFlags( SF_BEAM_TEMPORARY );
|
|
m_hNoise->SetOwnerEntity( pPlayer );
|
|
m_hNoise->SetScrollRate( 25 );
|
|
m_hNoise->SetBrightness( 200 );
|
|
m_hNoise->SetColor( 50, 50, 255 );
|
|
m_hNoise->SetNoise( 0.8 );
|
|
|
|
m_hSprite = CSprite::SpriteCreate( EGON_FLARE_SPRITE, GetAbsOrigin(), false );
|
|
m_hSprite->SetScale( 1.0 );
|
|
m_hSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
|
|
m_hSprite->AddSpawnFlags( SF_SPRITE_TEMPORARY );
|
|
m_hSprite->SetOwnerEntity( pPlayer );
|
|
#endif
|
|
}
|
|
|
|
|
|
void CWeaponEgon::DestroyEffect( void )
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
if ( m_hBeam )
|
|
{
|
|
UTIL_Remove( m_hBeam );
|
|
m_hBeam = NULL;
|
|
}
|
|
if ( m_hNoise )
|
|
{
|
|
UTIL_Remove( m_hNoise );
|
|
m_hNoise = NULL;
|
|
}
|
|
if ( m_hSprite )
|
|
{
|
|
m_hSprite->Expand( 10, 500 );
|
|
m_hSprite = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CWeaponEgon::EndAttack( void )
|
|
{
|
|
StopSound( "Weapon_Gluon.Run" );
|
|
|
|
if ( m_fireState != FIRE_OFF )
|
|
{
|
|
EmitSound( "Weapon_Gluon.Off" );
|
|
}
|
|
|
|
SetWeaponIdleTime( gpGlobals->curtime + 2.0 );
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
|
|
|
|
m_fireState = FIRE_OFF;
|
|
|
|
DestroyEffect();
|
|
}
|
|
|
|
bool CWeaponEgon::Holster( CBaseCombatWeapon *pSwitchingTo )
|
|
{
|
|
EndAttack();
|
|
|
|
return BaseClass::Holster( pSwitchingTo );
|
|
}
|
|
|
|
void CWeaponEgon::WeaponIdle( void )
|
|
{
|
|
if ( !HasWeaponIdleTimeElapsed() )
|
|
return;
|
|
|
|
if ( m_fireState != FIRE_OFF )
|
|
EndAttack();
|
|
|
|
int iAnim;
|
|
|
|
float flRand = random->RandomFloat( 0,1 );
|
|
float flIdleTime;
|
|
if ( flRand <= 0.5 )
|
|
{
|
|
iAnim = ACT_VM_IDLE;
|
|
flIdleTime = gpGlobals->curtime + random->RandomFloat( 10, 15 );
|
|
}
|
|
else
|
|
{
|
|
iAnim = ACT_VM_FIDGET;
|
|
flIdleTime = gpGlobals->curtime + 3.0;
|
|
}
|
|
|
|
SendWeaponAnim( iAnim );
|
|
|
|
SetWeaponIdleTime( flIdleTime );
|
|
}
|