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.
529 lines
12 KiB
529 lines
12 KiB
//========= 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 ); |
|
}
|
|
|