source-engine/game/shared/hl1/hl1mp_weapon_egon.cpp
2022-04-16 12:05:19 +03:00

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 );
}