Files
monstermod-redo-repo-copy/src/dlls/islave.cpp
Giegue e1e2a48f89 Fix Alien Grunt's TraceAttack method.
Fix extra cfg file crashing the server.
Added custom blood color for monsters.
2023-04-24 13:17:49 -03:00

757 lines
20 KiB
C++

/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// Alien slave monster
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "schedule.h"
#include "effects.h"
#include "weapons.h"
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define ISLAVE_AE_CLAW ( 1 )
#define ISLAVE_AE_CLAWRAKE ( 2 )
#define ISLAVE_AE_ZAP_POWERUP ( 3 )
#define ISLAVE_AE_ZAP_SHOOT ( 4 )
#define ISLAVE_AE_ZAP_DONE ( 5 )
const char *CMISlave::pAttackHitSounds[] =
{
"zombie/claw_strike1.wav",
"zombie/claw_strike2.wav",
"zombie/claw_strike3.wav",
};
const char *CMISlave::pAttackMissSounds[] =
{
"zombie/claw_miss1.wav",
"zombie/claw_miss2.wav",
};
const char *CMISlave::pPainSounds[] =
{
"aslave/slv_pain1.wav",
"aslave/slv_pain2.wav",
};
const char *CMISlave::pDeathSounds[] =
{
"aslave/slv_die1.wav",
"aslave/slv_die2.wav",
};
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CMISlave :: Classify ( void )
{
if ( m_iClassifyOverride == -1 ) // helper
return CLASS_NONE;
else if ( m_iClassifyOverride > 0 )
return m_iClassifyOverride; // override
return CLASS_ALIEN_MILITARY;
}
int CMISlave::IRelationship( CMBaseEntity *pTarget )
{
if ( (pTarget->IsPlayer()) )
if ( (pev->spawnflags & SF_MONSTER_WAIT_UNTIL_PROVOKED ) && ! (m_afMemory & bits_MEMORY_PROVOKED ))
return R_NO;
return CMBaseMonster::IRelationship( pTarget );
}
//=========================================================
// ALertSound - scream
//=========================================================
void CMISlave :: AlertSound( void )
{
if ( m_hEnemy != NULL )
{
SENTENCEG_PlayRndSz(ENT(pev), "SLV_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch);
}
}
//=========================================================
// IdleSound
//=========================================================
void CMISlave :: IdleSound( void )
{
if (RANDOM_LONG( 0, 2 ) == 0)
{
SENTENCEG_PlayRndSz(ENT(pev), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch);
}
#if 0
int side = RANDOM_LONG( 0, 1 ) * 2 - 1;
ClearBeams( );
ArmBeam( side );
UTIL_MakeAimVectors( pev->angles );
Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side;
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
WRITE_BYTE(TE_DLIGHT);
WRITE_COORD(vecSrc.x); // X
WRITE_COORD(vecSrc.y); // Y
WRITE_COORD(vecSrc.z); // Z
WRITE_BYTE( 8 ); // radius * 0.1
WRITE_BYTE( 255 ); // r
WRITE_BYTE( 180 ); // g
WRITE_BYTE( 96 ); // b
WRITE_BYTE( 10 ); // time * 10
WRITE_BYTE( 0 ); // decay * 0.1
MESSAGE_END( );
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap1.wav", 1, ATTN_NORM, 0, 100 );
#endif
}
//=========================================================
// PainSound
//=========================================================
void CMISlave :: PainSound( void )
{
if (RANDOM_LONG( 0, 2 ) == 0)
{
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
}
}
//=========================================================
// DieSound
//=========================================================
void CMISlave :: DeathSound( void )
{
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pDeathSounds[ RANDOM_LONG(0,ARRAYSIZE(pDeathSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
}
//=========================================================
// ISoundMask - returns a bit mask indicating which types
// of sounds this monster regards.
//=========================================================
int CMISlave :: ISoundMask ( void)
{
return 0;
}
void CMISlave::Killed( entvars_t *pevAttacker, int iGib )
{
ClearBeams( );
CMBaseMonster::Killed( pevAttacker, iGib );
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CMISlave :: SetYawSpeed ( void )
{
int ys;
switch ( m_Activity )
{
case ACT_WALK:
ys = 50;
break;
case ACT_RUN:
ys = 70;
break;
case ACT_IDLE:
ys = 50;
break;
default:
ys = 90;
break;
}
pev->yaw_speed = ys;
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CMISlave :: HandleAnimEvent( MonsterEvent_t *pEvent )
{
// ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
switch( pEvent->event )
{
case ISLAVE_AE_CLAW:
{
// SOUND HERE!
edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClaw, DMG_SLASH );
if ( pHurt )
{
if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) )
{
pHurt->v.punchangle.z = -18;
pHurt->v.punchangle.x = 5;
}
// Play a random attack hit sound
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
}
else
{
// Play a random attack miss sound
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
}
}
break;
case ISLAVE_AE_CLAWRAKE:
{
edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClawrake, DMG_SLASH );
if ( pHurt )
{
if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) )
{
pHurt->v.punchangle.z = -18;
pHurt->v.punchangle.x = 5;
}
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
}
else
{
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch );
}
}
break;
case ISLAVE_AE_ZAP_POWERUP:
{
UTIL_MakeAimVectors( pev->angles );
if (m_iBeams == 0)
{
Vector vecSrc = pev->origin + gpGlobals->v_forward * 2;
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc );
WRITE_BYTE(TE_DLIGHT);
WRITE_COORD(vecSrc.x); // X
WRITE_COORD(vecSrc.y); // Y
WRITE_COORD(vecSrc.z); // Z
WRITE_BYTE( 12 ); // radius * 0.1
WRITE_BYTE( 255 ); // r
WRITE_BYTE( 180 ); // g
WRITE_BYTE( 96 ); // b
WRITE_BYTE( 20 / pev->framerate ); // time * 10
WRITE_BYTE( 0 ); // decay * 0.1
MESSAGE_END( );
}
/*jlb
if (m_hDead != NULL)
{
WackBeam( -1, m_hDead );
WackBeam( 1, m_hDead );
}
else
jlb*/
{
ArmBeam( -1 );
ArmBeam( 1 );
BeamGlow( );
}
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 + m_iBeams * 10 );
pev->skin = m_iBeams / 2;
}
break;
case ISLAVE_AE_ZAP_SHOOT:
{
ClearBeams( );
/*jlb
if (m_hDead != NULL)
{
Vector vecDest = m_hDead->v.origin + Vector( 0, 0, 38 );
TraceResult trace;
UTIL_TraceHull( vecDest, vecDest, dont_ignore_monsters, human_hull, m_hDead, &trace );
if ( !trace.fStartSolid )
{
CMBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->pev->origin, m_hDead->pev->angles );
CMBaseMonster *pNewMonster = pNew->MyMonsterPointer( );
pNew->pev->spawnflags |= 1;
WackBeam( -1, pNew );
WackBeam( 1, pNew );
UTIL_Remove( m_hDead );
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) );
break;
}
}
jlb*/
ClearMultiDamage();
UTIL_MakeAimVectors( pev->angles );
ZapBeam( -1 );
ZapBeam( 1 );
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) );
// STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" );
ApplyMultiDamage(pev, pev);
m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 );
}
break;
case ISLAVE_AE_ZAP_DONE:
{
ClearBeams( );
}
break;
default:
CMBaseMonster::HandleAnimEvent( pEvent );
break;
}
}
//=========================================================
// CheckRangeAttack1 - normal beam attack
//=========================================================
BOOL CMISlave :: CheckRangeAttack1 ( float flDot, float flDist )
{
if (m_flNextAttack > gpGlobals->time)
{
return FALSE;
}
return CMBaseMonster::CheckRangeAttack1( flDot, flDist );
}
//=========================================================
// CheckRangeAttack2 - check bravery and try to resurect dead comrades
//=========================================================
BOOL CMISlave :: CheckRangeAttack2 ( float flDot, float flDist )
{
return FALSE;
if (m_flNextAttack > gpGlobals->time)
{
return FALSE;
}
//jlb m_hDead = NULL;
m_iBravery = 0;
edict_t *pEntity = NULL;
while ((pEntity = UTIL_FindEntityByClassname( pEntity, "monster_alien_slave" )) != NULL)
{
TraceResult tr;
UTIL_TraceLine( EyePosition( ), UTIL_EyePosition(pEntity), ignore_monsters, ENT(pev), &tr );
if (tr.flFraction == 1.0 || tr.pHit == pEntity)
{
if (pEntity->v.deadflag == DEAD_DEAD)
{
float d = (pev->origin - pEntity->v.origin).Length();
if (d < flDist)
{
//jlb m_hDead = pEntity;
flDist = d;
}
m_iBravery--;
}
else
{
m_iBravery++;
}
}
}
//jlb if (m_hDead != NULL)
//jlb return TRUE;
//jlb else
return FALSE;
}
//=========================================================
// StartTask
//=========================================================
void CMISlave :: StartTask ( Task_t *pTask )
{
ClearBeams( );
CMBaseMonster :: StartTask ( pTask );
}
//=========================================================
// Spawn
//=========================================================
void CMISlave :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/islave.mdl"));
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_STEP;
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
pev->effects = 0;
pev->health = gSkillData.slaveHealth;
pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin.
m_flFieldOfView = 0.5;
m_MonsterState = MONSTERSTATE_NONE;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP;
m_voicePitch = RANDOM_LONG( 85, 110 );
for (int i = 0; i < ISLAVE_MAX_BEAMS; i++)
m_pBeam[i] = NULL;
m_iBravery = 0;
m_flNextAttack = 0.0f;
MonsterInit();
pev->classname = MAKE_STRING( "monster_alien_slave" );
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
{
// default name
m_szMonsterName = MAKE_STRING( "Alien Slave" );
}
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CMISlave :: Precache()
{
PRECACHE_MODEL("models/islave.mdl");
PRECACHE_MODEL("sprites/lgtning.spr");
PRECACHE_SOUND("debris/zap1.wav");
PRECACHE_SOUND("debris/zap4.wav");
PRECACHE_SOUND("weapons/electro4.wav");
PRECACHE_SOUND("hassault/hw_shoot1.wav");
PRECACHE_SOUND("zombie/zo_pain2.wav");
PRECACHE_SOUND("headcrab/hc_headbite.wav");
PRECACHE_SOUND("weapons/cbar_miss1.wav");
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
PRECACHE_SOUND_ARRAY(pPainSounds);
PRECACHE_SOUND_ARRAY(pDeathSounds);
}
//=========================================================
// TakeDamage - get provoked when injured
//=========================================================
int CMISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
{
// don't slash one of your own
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pevAttacker));
if (pMonster != NULL)
{
if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( pMonster ) < R_DL)
return 0;
}
m_afMemory |= bits_MEMORY_PROVOKED;
return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
}
void CMISlave::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
{
if (bitsDamageType & DMG_SHOCK)
return;
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
// primary range attack
Task_t tlSlaveAttack1[] =
{
{ TASK_STOP_MOVING, 0 },
{ TASK_FACE_IDEAL, (float)0 },
{ TASK_RANGE_ATTACK1, (float)0 },
};
Schedule_t slSlaveAttack1[] =
{
{
tlSlaveAttack1,
ARRAYSIZE ( tlSlaveAttack1 ),
bits_COND_CAN_MELEE_ATTACK1 |
bits_COND_HEAR_SOUND |
bits_COND_HEAVY_DAMAGE,
0,
"Slave Range Attack1"
},
};
DEFINE_CUSTOM_SCHEDULES( CMISlave )
{
slSlaveAttack1,
};
IMPLEMENT_CUSTOM_SCHEDULES( CMISlave, CMBaseMonster );
//=========================================================
//=========================================================
Schedule_t *CMISlave :: GetSchedule( void )
{
ClearBeams( );
/*
if (pev->spawnflags)
{
pev->spawnflags = 0;
return GetScheduleOfType( SCHED_RELOAD );
}
*/
switch (m_MonsterState)
{
case MONSTERSTATE_COMBAT:
// dead enemy
if ( HasConditions( bits_COND_ENEMY_DEAD ) )
{
// call base class, all code to handle dead enemies is centralized there.
return CMBaseMonster :: GetSchedule();
}
if (pev->health < 20 || m_iBravery < 0)
{
if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
{
m_failSchedule = SCHED_CHASE_ENEMY;
if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
{
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
}
if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) )
{
// ALERT( at_console, "exposed\n");
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
}
}
}
break;
}
return CMBaseMonster::GetSchedule( );
}
Schedule_t *CMISlave :: GetScheduleOfType ( int Type )
{
switch ( Type )
{
case SCHED_FAIL:
if (HasConditions( bits_COND_CAN_MELEE_ATTACK1 ))
{
return CMBaseMonster :: GetScheduleOfType( SCHED_MELEE_ATTACK1 ); ;
}
break;
case SCHED_RANGE_ATTACK1:
return slSlaveAttack1;
case SCHED_RANGE_ATTACK2:
return slSlaveAttack1;
}
return CMBaseMonster :: GetScheduleOfType( Type );
}
//=========================================================
// ArmBeam - small beam from arm to nearby geometry
//=========================================================
void CMISlave :: ArmBeam( int side )
{
TraceResult tr;
float flDist = 1.0;
if (m_iBeams >= ISLAVE_MAX_BEAMS)
return;
UTIL_MakeAimVectors( pev->angles );
Vector vecSrc = pev->origin + gpGlobals->v_up * 36 + gpGlobals->v_right * side * 16 + gpGlobals->v_forward * 32;
for (int i = 0; i < 3; i++)
{
Vector vecAim = gpGlobals->v_right * side * RANDOM_FLOAT( 0, 1 ) + gpGlobals->v_up * RANDOM_FLOAT( -1, 1 );
TraceResult tr1;
UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, dont_ignore_monsters, ENT( pev ), &tr1);
if (flDist > tr1.flFraction)
{
tr = tr1;
flDist = tr.flFraction;
}
}
// Couldn't find anything close enough
if ( flDist == 1.0 )
return;
DecalGunshot( &tr, BULLET_PLAYER_CROWBAR );
m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
if (!m_pBeam[m_iBeams])
return;
m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) );
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
// m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
m_pBeam[m_iBeams]->SetBrightness( 64 );
m_pBeam[m_iBeams]->SetNoise( 80 );
m_iBeams++;
}
//=========================================================
// BeamGlow - brighten all beams
//=========================================================
void CMISlave :: BeamGlow( )
{
int b = m_iBeams * 32;
if (b > 255)
b = 255;
for (int i = 0; i < m_iBeams; i++)
{
if (m_pBeam[i]->GetBrightness() != 255)
{
m_pBeam[i]->SetBrightness( b );
}
}
}
//=========================================================
// WackBeam - regenerate dead colleagues
//=========================================================
void CMISlave :: WackBeam( int side, edict_t *pEntity )
{
Vector vecDest;
float flDist = 1.0;
if (m_iBeams >= ISLAVE_MAX_BEAMS)
return;
if (pEntity == NULL)
return;
m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 );
if (!m_pBeam[m_iBeams])
return;
m_pBeam[m_iBeams]->PointEntInit( UTIL_Center(pEntity), entindex( ) );
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
m_pBeam[m_iBeams]->SetBrightness( 255 );
m_pBeam[m_iBeams]->SetNoise( 80 );
m_iBeams++;
}
//=========================================================
// ZapBeam - heavy damage directly forward
//=========================================================
void CMISlave :: ZapBeam( int side )
{
Vector vecSrc, vecAim;
TraceResult tr;
edict_t *pEntity;
if (m_iBeams >= ISLAVE_MAX_BEAMS)
return;
vecSrc = pev->origin + gpGlobals->v_up * 36;
vecAim = ShootAtEnemy( vecSrc );
float deflection = 0.01;
vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection );
UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr);
m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 50 );
if (!m_pBeam[m_iBeams])
return;
m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) );
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
m_pBeam[m_iBeams]->SetBrightness( 255 );
m_pBeam[m_iBeams]->SetNoise( 20 );
m_iBeams++;
pEntity = tr.pHit;
if (pEntity != NULL && pEntity->v.takedamage)
{
if (UTIL_IsPlayer(pEntity))
UTIL_TraceAttack( pEntity, pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK );
else if (pEntity->v.euser4 != NULL)
{
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
pMonster->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK );
}
else
UTIL_TraceAttack( pEntity, pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK );
}
UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) );
}
//=========================================================
// ClearBeams - remove all beams
//=========================================================
void CMISlave :: ClearBeams( )
{
for (int i = 0; i < ISLAVE_MAX_BEAMS; i++)
{
if (m_pBeam[i])
{
UTIL_Remove( m_pBeam[i]->edict() );
m_pBeam[i] = NULL;
}
}
m_iBeams = 0;
pev->skin = 0;
STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" );
}