source-engine/game/shared/swarm/asw_weapon_tesla_gun_shared.cpp

788 lines
21 KiB
C++
Raw Normal View History

2023-10-03 14:23:56 +00:00
#include "cbase.h"
#include "npcevent.h"
#include "Sprite.h"
#include "beam_shared.h"
#include "takedamageinfo.h"
#include "gamerules.h"
#include "in_buttons.h"
#include "asw_weapon_parse.h"
#include "asw_marine_skills.h"
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
#include "c_asw_player.h"
#include "c_asw_marine.h"
#include "c_te_effect_dispatch.h"
#include "c_asw_alien.h"
#include "fx.h"
#else
#include "player.h"
#include "asw_player.h"
#include "asw_marine.h"
#include "soundent.h"
#include "game.h"
#include "te_effect_dispatch.h"
#include "asw_marine_speech.h"
#include "asw_weapon_ammo_bag_shared.h"
#include "asw_alien.h"
#include "ndebugoverlay.h"
#endif
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "IEffects.h"
#include "asw_trace_filter_shot.h"
#include "particle_parse.h"
#include "asw_weapon_tesla_gun_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const float ASW_TG_RANGE = 240.0f; // Range of electrical discharge - TODO: make this an attribute?
static const float ASW_TG_TRACE_SIZE = 10.0f; // Size of the laser's trace attack
static const float ASW_TG_FIELD_DURATION = 2.0f; // Duration of electrical field left behind
//static const float ASW_TG_SHOCK_RATE = 0.25f; // Rate at which we shock entities we're latched on
static const float ASW_TG_SEARCH_DIAMETER = 48.0f; // How far past the range to search for enemies to latch on to
static const float SQRT3 = 1.732050807569; // for computing max extents inside a box
static const Vector ASW_TG_SEARCH_BOX_EXTENTS( ASW_TG_SEARCH_DIAMETER/2.0f, ASW_TG_SEARCH_DIAMETER/2.0f, ASW_TG_SEARCH_DIAMETER/2.0f );
static const int ASW_TG_MAX_SHOCK_ALIENS = 6; // Max # of aliens to search for a shock target
extern int g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
IMPLEMENT_NETWORKCLASS_ALIASED( ASW_Weapon_Tesla_Gun, DT_ASW_Weapon_Tesla_Gun );
BEGIN_NETWORK_TABLE( CASW_Weapon_Tesla_Gun, DT_ASW_Weapon_Tesla_Gun )
#ifdef CLIENT_DLL
// recvprops
RecvPropTime( RECVINFO( m_fSlowTime ) ),
RecvPropInt( RECVINFO( m_FireState ) ),
RecvPropEHandle( RECVINFO( m_hShockEntity ) ),
RecvPropVector( RECVINFO( m_vecShockPos ) ),
#else
// sendprops
SendPropTime( SENDINFO( m_fSlowTime ) ),
SendPropInt( SENDINFO( m_FireState ), Q_log2(ASW_TG_NUM_FIRE_STATES)+1, SPROP_UNSIGNED ),
SendPropEHandle( SENDINFO( m_hShockEntity ) ),
SendPropVector( SENDINFO( m_vecShockPos ) ),
#endif
END_NETWORK_TABLE()
LINK_ENTITY_TO_CLASS( asw_weapon_tesla_gun, CASW_Weapon_Tesla_Gun );
PRECACHE_WEAPON_REGISTER( asw_weapon_tesla_gun );
#if defined( CLIENT_DLL )
BEGIN_PREDICTION_DATA( CASW_Weapon_Tesla_Gun )
DEFINE_PRED_FIELD_TOL( m_fSlowTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
DEFINE_PRED_FIELD( m_vecShockPos, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CASW_Weapon_Tesla_Gun::CASW_Weapon_Tesla_Gun( void )
{
m_bReloadsSingly = false;
m_bFiresUnderwater = true;
#ifdef GAME_DLL
m_fLastForcedFireTime = 0; // last time we forced an AI marine to push its fire button
#endif
m_flLastShockTime = 0.0f;
m_flLastDischargeTime = 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_Weapon_Tesla_Gun::Precache( void )
{
PrecacheScriptSound("ASW_MiningLaser.ReloadA");
PrecacheScriptSound("ASW_MiningLaser.ReloadB");
PrecacheScriptSound("ASW_MiningLaser.ReloadC");
PrecacheScriptSound( "ASW_Tesla_Laser.Damage" );
PrecacheScriptSound( "ASW_Tesla_Laser.Loop" );
PrecacheScriptSound( "ASW_Tesla_Laser.Stop" );
PrecacheParticleSystem( "mining_laser_exhaust" );
BaseClass::Precache();
}
bool CASW_Weapon_Tesla_Gun::HasAmmo()
{
return m_iClip1 > 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CASW_Weapon_Tesla_Gun::PrimaryAttack( void )
{
CASW_Player *pPlayer = GetCommander();
CASW_Marine *pMarine = GetMarine();
if ( !pMarine || !pMarine->IsAlive() )
{
EndAttack();
return;
}
// don't fire underwater
if ( pMarine->GetWaterLevel() == 3 )
{
WeaponSound( EMPTY );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
return;
}
Vector vecSrc = pMarine->Weapon_ShootPosition( );
Vector vecAiming = vec3_origin;
if ( pPlayer && pMarine->IsInhabited() )
{
vecAiming = pPlayer->GetAutoaimVectorForMarine(pMarine, GetAutoAimAmount(), GetVerticalAdjustOnlyAutoAimAmount()); // 45 degrees = 0.707106781187
}
else
{
#ifndef CLIENT_DLL
vecAiming = pMarine->GetActualShootTrajectory( vecSrc );
#endif
}
#ifdef GAME_DLL
m_bIsFiring = true;
switch ( m_FireState )
{
case ASW_TG_FIRE_OFF:
{
Fire( vecSrc, vecAiming );
m_flNextPrimaryAttack = gpGlobals->curtime;
break;
}
case ASW_TG_FIRE_DISCHARGE:
{
Fire( vecSrc, vecAiming );
EHANDLE hShockEntity = m_hShockEntity.Get();
// Search for nearby entities to shock
if ( !hShockEntity )
{
CBaseEntity *pList[ASW_TG_MAX_SHOCK_ALIENS];
int nCount = UTIL_EntitiesInBox( pList, ASW_TG_MAX_SHOCK_ALIENS, m_vecShockPos.Get() - ASW_TG_SEARCH_BOX_EXTENTS, m_vecShockPos.Get() + ASW_TG_SEARCH_BOX_EXTENTS, 0 );
for ( int i = 0; i < nCount; i++ )
{
if ( ShockAttach( pList[ i ] ) )
{
SetFiringState( ASW_TG_FIRE_ATTACHED );
m_flLastShockTime = 0.0f;
break;
}
}
}
if ( gpGlobals->curtime > m_flLastDischargeTime + (GetFireRate()*3) )
{
m_iClip1 = MAX( 0, m_iClip1 - 1 );
m_flLastDischargeTime = gpGlobals->curtime;
}
m_flNextPrimaryAttack = gpGlobals->curtime;
break;
}
case ASW_TG_FIRE_ATTACHED:
{
Fire( vecSrc, vecAiming );
EHANDLE hShockEntity = m_hShockEntity.Get();
if ( !hShockEntity || hShockEntity->GetHealth() <= 0 )
{
SetFiringState( ASW_TG_FIRE_DISCHARGE );
break;
}
// detach if...
// too far
float flShockDistance = (hShockEntity->GetAbsOrigin() - pMarine->GetAbsOrigin()).LengthSqr();
if ( flShockDistance > (GetWeaponRange() + SQRT3*ASW_TG_SEARCH_DIAMETER)*(GetWeaponRange() + SQRT3*ASW_TG_SEARCH_DIAMETER) )
{
ShockDetach();
SetFiringState( ASW_TG_FIRE_DISCHARGE );
break;
}
// dead
if ( hShockEntity->m_iHealth <= 0 )
{
ShockDetach();
SetFiringState( ASW_TG_FIRE_DISCHARGE );
break;
}
// facing another direction
Vector vecAttach = hShockEntity->GetAbsOrigin() - pMarine->GetAbsOrigin();
Vector vecForward;
CASW_Player *pPlayer = pMarine->GetCommander();
if ( pPlayer )
{
AngleVectors( pPlayer->EyeAngles(), &vecForward );
if ( DotProduct( vecForward, vecAttach ) < 0.0f )
{
ShockDetach();
SetFiringState( ASW_TG_FIRE_DISCHARGE );
break;
}
}
if ( gpGlobals->curtime >= m_flLastShockTime + GetFireRate() )
{
ShockEntity();
m_flLastShockTime = gpGlobals->curtime;
pMarine->OnWeaponFired( this, 1 );
}
//if (m_iClip1 > 0) // only force the fire wait time if we have ammo for another shot
// m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
//else
m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
break;
}
}
SendWeaponAnim( GetPrimaryAttackActivity() );
SetWeaponIdleTime( gpGlobals->curtime + GetFireRate() );
#endif
m_fSlowTime = gpGlobals->curtime + GetFireRate();
}
float CASW_Weapon_Tesla_Gun::GetWeaponDamage()
{
float flDamage = GetWeaponInfo()->m_flBaseDamage;
if ( GetMarine() )
{
flDamage += MarineSkills()->GetSkillBasedValueByMarine(GetMarine(), ASW_MARINE_SKILL_ACCURACY, ASW_MARINE_SUBSKILL_ACCURACY_TESLA_CANNON_DMG);
}
return flDamage;
}
void CASW_Weapon_Tesla_Gun::ShockEntity()
{
CASW_Marine *pMarine = GetMarine();
EHANDLE hShockEntity = m_hShockEntity.Get();
Assert( hShockEntity && hShockEntity->m_takedamage != DAMAGE_NO && pMarine );
Vector vecDir = hShockEntity->GetAbsOrigin() - pMarine->GetAbsOrigin();
ClearMultiDamage();
float flBaseDamage = GetWeaponDamage() * g_pGameRules->GetDamageMultiplier();
CTakeDamageInfo info( this, pMarine, flBaseDamage, DMG_SHOCK );
info.SetWeapon(this);
CalculateMeleeDamageForce( &info, vecDir, m_AttackTrace.endpos );
#ifdef GAME_DLL
DoArcingShock( flBaseDamage, hShockEntity.Get() );
#endif
// Whatever damage didn't get used for electricity arcing gets applied to a single target
info.SetDamage( info.GetDamage() + flBaseDamage );
hShockEntity->DispatchTraceAttack( info, vecDir, &m_AttackTrace );
ApplyMultiDamage();
//Msg( "%f - Shocking target for %f\n", gpGlobals->curtime, info.GetDamage() + flBaseDamage );
#ifdef GAME_DLL
CASW_Alien *pTargetAlien = dynamic_cast<CASW_Alien*>( hShockEntity.Get() );
if ( pTargetAlien )
{
pTargetAlien->ForceFlinch( pMarine->GetAbsOrigin() );
}
#endif
// decrement ammo
m_iClip1 = MAX( 0, m_iClip1 - 1 );
CPASFilter filter( hShockEntity->GetAbsOrigin() );
CBaseEntity::EmitSound( filter, SOUND_FROM_WORLD, "ASW_Tesla_Laser.Damage", &hShockEntity->GetAbsOrigin() );
}
#ifdef GAME_DLL
void CASW_Weapon_Tesla_Gun::DoArcingShock( float flBaseDamage, CBaseEntity *pLastShocked )
{
if ( !GetOwner() )
return;
// search other enemies near this one
int iMaxArcs = 2; // TODO: max arc should be attribute of the weapon? if we can combine two attribs into one somehow
CUtlVector<CBaseEntity*> shocked;
int iNumShocked = 0; // TODO: Use size of shocked vector for this?
shocked.AddToTail( pLastShocked );
float flArcDistSqr = (GetWeaponRange()/1.5f) * (GetWeaponRange()/1.5f);
float flShockDamage = flBaseDamage; // TODO: base on attribute?
float flDamageReduction = flShockDamage / (float)iMaxArcs; // how much damage each arc loses
Vector vecShockSrc = pLastShocked->WorldSpaceCenter();
//CBaseEntity *pLastShocked = pTarget;
//pLastShocked->EmitSound( "Electricity.Zap" );
//CASW_Alien *pAlien = dynamic_cast<CASW_Alien*>( pTarget );
//if ( pAlien )
// pAlien->ForceFlinch( vecShockPos );
for ( int k = 0; k < iMaxArcs; k++ )
{
CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs();
int nAIs = g_AI_Manager.NumAIs();
bool bShocked = false;
for ( int i = 0; i < nAIs; i++ )
{
if ( shocked.Find( ppAIs[i] ) != shocked.InvalidIndex() ) // already shocked this guy
continue;
if ( GetOwner()->IRelationType( ppAIs[i] ) >= D_LI ) // friendly
continue;
if ( ppAIs[i]->GetAbsOrigin().DistToSqr( vecShockSrc ) > flArcDistSqr ) // too far away
continue;
// trace from the last shock position to this guy
trace_t shockTR;
Vector vecAIPos = ppAIs[i]->WorldSpaceCenter();
CASWTraceFilterShot traceFilter( this, pLastShocked, COLLISION_GROUP_NONE );
AI_TraceLine( vecShockSrc, vecAIPos, MASK_SHOT, &traceFilter, &shockTR );
if ( shockTR.fraction != 1.0 && shockTR.m_pEnt )
{
if ( GetOwner()->IRelationType( shockTR.m_pEnt ) >= D_LI ) // friendly
continue;
// shock this thing
vecAIPos = shockTR.endpos;
CTakeDamageInfo shockDmgInfo( this, this, flShockDamage, DMG_SHOCK );
Vector vecDir = vecAIPos - vecShockSrc;
VectorNormalize( vecDir );
ClearMultiDamage();
shockDmgInfo.SetDamagePosition( shockTR.endpos );
shockDmgInfo.SetDamageForce( vecDir );
shockDmgInfo.ScaleDamageForce( 1.0 );
shockDmgInfo.SetWeapon( this );
shockTR.m_pEnt->DispatchTraceAttack( shockDmgInfo, vecDir, &shockTR );
CASW_Alien *pAlien = dynamic_cast<CASW_Alien*>( shockTR.m_pEnt );
if ( pAlien )
pAlien->ForceFlinch( pLastShocked->WorldSpaceCenter() );
// spawn a shock effect
CRecipientFilter filter;
filter.AddAllPlayers();
UserMessageBegin( filter, "ASWEnemyTeslaGunArcShock" );
WRITE_SHORT( shockTR.m_pEnt->entindex() );
WRITE_SHORT( pLastShocked->entindex() );
MessageEnd();
vecShockSrc = vecAIPos;
pLastShocked = shockTR.m_pEnt;
bShocked = true;
iNumShocked++;
// reduce shock damage as energy arcs
flShockDamage = MAX( flShockDamage - flDamageReduction, 0.0f );
break;
}
}
if ( !bShocked )
break;
}
/*
if ( iNumShocked > 0 )
{
// Return reduced shock damage if we arced
info.SetDamage( flShockDamage );
}*/
}
#endif //GAME_DLL
void CASW_Weapon_Tesla_Gun::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
{
CASW_Marine *pMarine = GetMarine();
if ( !pMarine )
{
return;
}
Vector vecDest = vecOrigSrc + (vecDir * GetWeaponRange());
Vector mins = Vector( -ASW_TG_TRACE_SIZE, -ASW_TG_TRACE_SIZE, 0.0f);
Vector maxs = Vector( ASW_TG_TRACE_SIZE, ASW_TG_TRACE_SIZE, 12.0f );
trace_t tr;
//UTIL_TraceLine( vecOrigSrc, vecDest, MASK_SHOT, pMarine, COLLISION_GROUP_NONE, &tr );
UTIL_TraceHull( vecOrigSrc, vecDest, mins, maxs, MASK_SHOT, pMarine, COLLISION_GROUP_NONE, &tr );
#ifndef CLIENT_DLL
//QAngle angles;
//VectorAngles( vecDir, angles );
//if ( tr.DidHit() && tr.m_pEnt && tr.m_pEnt->IsNPC() )
//{
// NDebugOverlay::SweptBox( vecOrigSrc, tr.endpos, mins, maxs, angles, 0, 255, 0, 128, 5.0f );
//}
//else
//{
// //NDebugOverlay::Line( vecOrigSrc, tr.endpos, 0, 255, 0, true, 5.0f );
// NDebugOverlay::SweptBox( vecOrigSrc, (tr.m_pEnt && tr.m_pEnt->IsNPC()) ? tr.endpos : vecDest, mins, maxs, angles, 255, 0, 0, 128, 5.0f );
//}
#endif
if ( tr.allsolid )
return;
CBaseEntity *pEntity = tr.m_pEnt;
CASW_Marine *pTargetMarine = NULL;
if ( pEntity )
{
pTargetMarine = CASW_Marine::AsMarine( pEntity );
}
Vector vecUp, vecRight;
QAngle angDir;
VectorAngles( vecDir, angDir );
AngleVectors( angDir, NULL, &vecRight, &vecUp );
m_AttackTrace = tr;
if( ShockAttach( pEntity ) )
{
SetFiringState( ASW_TG_FIRE_ATTACHED );
}
else if ( !m_hShockEntity.Get() )
{
// If we fail to hit something, and we're not shocking someone, we're just discharging onto the ground
SetFiringState( ASW_TG_FIRE_DISCHARGE );
}
#ifdef GAME_DLL
if (pMarine && m_iClip1 <= 0 && pMarine->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
{
// check he doesn't have ammo in an ammo bay
CASW_Weapon_Ammo_Bag* pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>(pMarine->GetASWWeapon(0));
if (!pAmmoBag)
pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>(pMarine->GetASWWeapon(1));
if (!pAmmoBag || !pAmmoBag->CanGiveAmmoToWeapon(this))
pMarine->OnWeaponOutOfAmmo(true);
}
#endif
if ( tr.DidHit() )
{
vecDest = tr.endpos;
}
else
{
// Arc down to the ground with a bit of randomness
Vector down = 0.9f * Vector( 0.0f, 0.0f, -1.0f ) + 0.1f * RandomVector( -1.0, 1.0 );
// Trace down far enough to hit lower levels
UTIL_TraceLine( vecDest, vecDest + GetWeaponRange() * down * 2.0f, MASK_NPCSOLID_BRUSHONLY, pMarine, COLLISION_GROUP_NONE, &tr );
// FIXME: if we really can't hit anything, just leave vecDest alone
if( tr.DidHit() )
{
vecDest = tr.endpos;
}
}
m_vecShockPos = vecDest;
}
bool CASW_Weapon_Tesla_Gun::Reload( void )
{
EndAttack();
return BaseClass::Reload();
}
void CASW_Weapon_Tesla_Gun::EndAttack( void )
{
/*
Vector vecEjectPos, vecEjectPos2;
QAngle angEject, angEject2;
int iAttachment = LookupAttachment( "eject1" );
GetAttachment( iAttachment, vecEjectPos, angEject );
unsigned char color[3];
color[0] = 50;
color[1] = 128;
color[2] = 50;
iAttachment = LookupAttachment( "eject2" );
GetAttachment( iAttachment, vecEjectPos2, angEject2 );
*/
#ifdef CLIENT_DLL
//FX_Smoke( vecEjectPos, angEject, 0.5, 5, &color[0], 192 );
//FX_Smoke( vecEjectPos2, angEject2, 0.5, 5, &color[0], 192 );
if ( m_pDischargeEffect )
{
m_pDischargeEffect->StopEmission();
}
#else
//CPVSFilter filter( vecEjectPos );
//te->Smoke( filter, 0.0, &vecEjectPos, g_sModelIndexSmoke, 0.5f, 10 );
//te->Smoke( filter, 0.0, &vecEjectPos2, g_sModelIndexSmoke, 0.5f, 10 );
#endif
SetWeaponIdleTime( gpGlobals->curtime + 2.0 );
SetFiringState( ASW_TG_FIRE_OFF );
ShockDetach();
ClearIsFiring();
m_bIsFiring = false;
}
void CASW_Weapon_Tesla_Gun::Drop( const Vector &vecVelocity )
{
EndAttack();
BaseClass::Drop( vecVelocity );
}
bool CASW_Weapon_Tesla_Gun::Holster( CBaseCombatWeapon *pSwitchingTo )
{
EndAttack();
return BaseClass::Holster( pSwitchingTo );
}
void CASW_Weapon_Tesla_Gun::WeaponIdle( void )
{
if ( !HasWeaponIdleTimeElapsed() )
return;
if ( m_FireState != ASW_TG_FIRE_OFF )
{
EndAttack();
}
float flIdleTime = gpGlobals->curtime + 5.0;
SetWeaponIdleTime( flIdleTime );
}
bool CASW_Weapon_Tesla_Gun::ShouldMarineMoveSlow()
{
return m_fSlowTime > gpGlobals->curtime || IsReloading();
}
#ifdef CLIENT_DLL
void CASW_Weapon_Tesla_Gun::ClientThink()
{
BaseClass::ClientThink();
UpdateEffects();
}
const char* CASW_Weapon_Tesla_Gun::GetPartialReloadSound(int iPart)
{
switch (iPart)
{
case 1: return "ASW_MiningLaser.ReloadB"; break;
case 2: return "ASW_MiningLaser.ReloadC"; break;
default: break;
};
return "ASW_MiningLaser.ReloadA";
}
void CASW_Weapon_Tesla_Gun::UpdateEffects()
{
switch( m_FireState )
{
case ASW_TG_FIRE_OFF:
{
if ( m_pDischargeEffect )
{
m_pDischargeEffect->StopEmission();
m_pDischargeEffect = NULL;
DispatchParticleEffect( "mining_laser_exhaust", PATTACH_POINT_FOLLOW, this, "eject1" );
}
break;
}
case ASW_TG_FIRE_DISCHARGE:
{
if ( m_pDischargeEffect && m_pDischargeEffect->GetControlPointEntity( 1 ) != NULL )
{
// Still attach, detach us
m_pDischargeEffect->StopEmission();
m_pDischargeEffect = NULL;
}
if ( !m_pDischargeEffect )
{
m_pDischargeEffect = ParticleProp()->Create( "electric_weapon_shot_continuous_off", PATTACH_POINT_FOLLOW, "muzzle" );
}
m_pDischargeEffect->SetControlPoint( 1, m_vecShockPos );
Vector vecPos = m_vecShockPos.Get();
//FX_MicroExplosion( vecPos, Vector(0,0,1) );
break;
}
case ASW_TG_FIRE_ATTACHED:
{
EHANDLE hTarget = m_hShockEntity.Get();
if ( !hTarget )
{
break;
}
if ( m_pDischargeEffect )
{
if ( m_pDischargeEffect->GetControlPointEntity( 1 ) != m_hShockEntity.Get() )
{
m_pDischargeEffect->StopEmission();
m_pDischargeEffect = NULL;
}
}
if ( !m_pDischargeEffect )
{
m_pDischargeEffect = ParticleProp()->Create( "electric_weapon_shot_continuous", PATTACH_POINT_FOLLOW, "muzzle" );
}
Assert( m_pDischargeEffect );
if ( m_pDischargeEffect->GetControlPointEntity( 1 ) == NULL )
{
float flHeight = hTarget->BoundingRadius();
Vector vOffset( 0.0f, 0.0f, flHeight * 0.25 );
C_ASW_Alien *pAlien = dynamic_cast<C_ASW_Alien*>(hTarget.Get());
// TODO: get some standardization in the attachment naming
if ( pAlien && pAlien->LookupAttachment( "eyes" ) )
{
ParticleProp()->AddControlPoint( m_pDischargeEffect, 1, pAlien, PATTACH_POINT_FOLLOW, "eyes" );
}
else
{
ParticleProp()->AddControlPoint( m_pDischargeEffect, 1, hTarget, PATTACH_ABSORIGIN_FOLLOW, NULL, vOffset );
}
}
break;
}
}
}
#endif
#ifdef GAME_DLL
void CASW_Weapon_Tesla_Gun::GetButtons(bool& bAttack1, bool& bAttack2, bool& bReload, bool& bOldReload, bool& bOldAttack1 )
{
CASW_Marine *pMarine = GetMarine();
// make AI fire this weapon whenever they have an enemy within a certain range
if (pMarine && !pMarine->IsInhabited())
{
bool bHasEnemy = (pMarine->GetEnemy() && pMarine->GetEnemy()->GetAbsOrigin().DistTo(pMarine->GetAbsOrigin()) < 250.0f);
if (bHasEnemy)
m_fLastForcedFireTime = gpGlobals->curtime;
bAttack1 = (bHasEnemy || gpGlobals->curtime < m_fLastForcedFireTime + 1.0f); // fire for 2 seconds after killing our enemy
bAttack2 = false;
bReload = false;
bOldReload = false;
return;
}
BaseClass::GetButtons(bAttack1, bAttack2, bReload, bOldReload, bOldAttack1);
}
#endif
void CASW_Weapon_Tesla_Gun::SetFiringState(ASW_Weapon_TeslaGunFireState_t state)
{
#ifdef CLIENT_DLL
//Msg("[C] SetFiringState %d\n", state);
#else
//Msg("[C] SetFiringState %d\n", state);
#endif
// Check for transitions
if ( m_FireState != state )
{
if ( state == ASW_TG_FIRE_DISCHARGE || state == ASW_TG_FIRE_ATTACHED )
{
EmitSound( "ASW_Tesla_Laser.Loop" );
}
else if ( state == ASW_TG_FIRE_OFF )
{
StopSound( "ASW_Tesla_Laser.Loop" );
EmitSound( "ASW_Tesla_Laser.Stop" );
}
}
m_FireState = state;
}
bool CASW_Weapon_Tesla_Gun::ShockAttach( CBaseEntity *pEntity )
{
#ifdef CLIENT_DLL
return false;
#else
if ( !pEntity || (pEntity->m_takedamage == DAMAGE_NO) ||
(pEntity->Classify() == CLASS_ASW_MARINE) || (pEntity->Classify() == CLASS_ASW_SENTRY_BASE) ||
(pEntity == m_hShockEntity.Get()) || (pEntity->m_iHealth <= 0) ||
(pEntity == GetMarine()) )
{
return false;
}
if ( m_hShockEntity.Get() )
{
ShockDetach();
}
m_hShockEntity.Set( pEntity );
// we need a trace_t struct representing the entity we're shocking, but if we
// attached based on proximity to the end of the beam, we might not have actually performed a trace - for now just update m_AttackTrace
// but it'd be nice to decouple attribute damage from requiring a trace
if ( m_AttackTrace.m_pEnt != pEntity )
{
m_AttackTrace.endpos = pEntity->GetAbsOrigin();
m_AttackTrace.m_pEnt = pEntity;
}
ASW_WPN_DEV_MSG( "Attached to %d:%s\n", m_hShockEntity.Get()->entindex(), m_hShockEntity.Get()->GetClassname() );
return true;
#endif
}
void CASW_Weapon_Tesla_Gun::ShockDetach()
{
#ifdef GAME_DLL
ASW_WPN_DEV_MSG( "Detaching from %d:%s\n", m_hShockEntity.Get() ? m_hShockEntity.Get()->entindex() : -1, m_hShockEntity.Get() ? m_hShockEntity.Get()->GetClassname() : "(null)" );
m_hShockEntity.Set( NULL );
#endif
}