Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
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.

370 lines
10 KiB

// ---------------------------------------------------------------
// Zap Rift Entity
//
// AUTHOR
// Tyler Lund <halflife@bubblemod.org>
//
// LICENSE
//
// Permission is granted to anyone to use this software for
// any purpose on any computer system, and to redistribute it
// in any way, subject to the following restrictions:
//
// 1. The author is not responsible for the consequences of
// use of this software, no matter how awful, even if they
// arise from defects in it.
// 2. The origin of this software must not be misrepresented,
// either by explicit claim or by omission.
// 3. Altered versions must be plainly marked as such, and
// must not be misrepresented (by explicit claim or
// omission) as being the original software.
// 3a. It would be nice if I got a copy of your improved
// version sent to halflife@bubblemod.org.
// 4. This notice must not be removed or altered.
//
// ---------------------------------------------------------------
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "gamerules.h"
#include "effects.h"
#include "BMOD_zapgunrift.h"
#include "shake.h"
#define RIFTSPR "sprites/cnt1.spr"
#define RIFTSPR2 "sprites/cnt1.spr"
LINK_ENTITY_TO_CLASS( zaprift, CZapRift );
void CZapRift::Precache( void )
{
PRECACHE_SOUND("debris/zap4.wav");
PRECACHE_SOUND("weapons/electro4.wav");
PRECACHE_SOUND("hassault/hw_shoot1.wav");
PRECACHE_MODEL( RIFTSPR );
PRECACHE_MODEL( RIFTSPR2 );
}
void CZapRift::Spawn( void )
{
pev->classname = MAKE_STRING( "zapgun" );
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_NOT;
pev->rendermode = kRenderGlow;
pev->renderamt = 240;
pev->renderfx = kRenderFxNoDissipation;
UTIL_MakeAimVectors( pev->angles );
//pev->origin = pev->origin + gpGlobals->v_forward * 8;
SET_MODEL(ENT(pev), RIFTSPR);
pev->frame = 0;
pev->scale = 2;
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
// Create Inner Animated Sprite
m_pSprite = CSprite::SpriteCreate( RIFTSPR2, pev->origin, TRUE );
m_pSprite->Animate( 1 );
m_pSprite->pev->scale = 1;
m_pSprite->SetTransparency( kRenderGlow, 255, 255, 255, 180, kRenderFxNoDissipation );
m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY;
SetThink ( Animate );
pev->nextthink = gpGlobals->time + 0.1;
m_fLifeSpan = gpGlobals->time + 3;
m_fNextElectrify = gpGlobals->time + 0.1;
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0,100 );
}
void CZapRift::Animate( void )
{
pev->nextthink = gpGlobals->time + 0.1;
if ( pev->frame++ )
{
if ( pev->frame > m_maxFrame )
{
pev->frame = 0;
}
}
m_pSprite->pev->scale = RANDOM_FLOAT(.5, 3);
if ( m_fLifeSpan < gpGlobals->time)
{
pev->effects |= EF_NODRAW;
m_pSprite->Expand( 10, 100 );
SetThink(SUB_Remove);
pev->nextthink = gpGlobals->time + 5;
}
if ( m_fNextElectrify < gpGlobals->time )
{
// Send out electric streamers and do area effect damage
for (int i = 0; i < 3; i++)
{
Vector vecTemp;
Vector vecEnd = Vector (0,0,0);
// Hopefully find a long beam
for (int j = 0; j < 5; j++)
{
vecTemp = FindBeam();
if ( vecTemp.Length() > vecEnd.Length() )
vecEnd = vecTemp;
}
// Draw the streamer
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMPOINTS );
WRITE_COORD(pev->origin.x);
WRITE_COORD(pev->origin.y);
WRITE_COORD(pev->origin.z);
WRITE_COORD( vecEnd.x );
WRITE_COORD( vecEnd.y );
WRITE_COORD( vecEnd.z );
WRITE_SHORT( g_sModelIndexLightning );
WRITE_BYTE( 0 ); // Starting frame
WRITE_BYTE( 0 ); // framerate * 0.1
WRITE_BYTE( 20 ); // life * 0.1
WRITE_BYTE( 80 ); // width
WRITE_BYTE( 50 ); // noise
WRITE_BYTE( 180 ); // color r,g,b
WRITE_BYTE( 255 ); // color r,g,b
WRITE_BYTE( 96 ); // color r,g,b
WRITE_BYTE( 255 ); // brightness
WRITE_BYTE( 0 ); // scroll speed
MESSAGE_END();
}
// Deal damage.
CBaseEntity *pEntity= NULL;
TraceResult tr;
while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, 512 )) != NULL)
{
// Only look for players
if ( pEntity->pev->takedamage != DAMAGE_NO )
{
// blast's don't tavel out of water,
// so ignore players that lie on the other side.
if (pEntity->pev->waterlevel == 0)
continue;
// Trace to the target
UTIL_TraceLine( pev->origin, pEntity->BodyTarget( pev->origin ) , dont_ignore_monsters, ENT(pev), &tr);
if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict())
{
ClearMultiDamage( );
pEntity->TraceAttack( VARS( pev->owner ), 30, Vector(0,0,1), &tr, DMG_SHOCK | DMG_ALWAYSGIB );
ApplyMultiDamage( pev, VARS( pev->owner ) );
UTIL_ScreenFade( pEntity, Vector(180,255,96), 2, 0.5, 128, FFADE_IN );
}
}
}
m_fNextElectrify = gpGlobals->time + 1;
}
}
Vector CZapRift::FindBeam( void )
{
TraceResult tr;
// Direction is within the forward hemisphere.
UTIL_MakeAimVectors( pev->angles );
Vector vecDir = gpGlobals->v_right * RANDOM_FLOAT( -1, 1)
+ gpGlobals->v_up * RANDOM_FLOAT( -1, 1)
+ gpGlobals->v_forward * RANDOM_FLOAT( 0, 1);
vecDir = vecDir.Normalize();
// Maximum distance
float fDist = 1024;
// End point of the streamer
Vector vecEnd = pev->origin + vecDir * fDist;
// Trace to the end of the streamer to see if
// there is architecture in the way.
UTIL_TraceLine(pev->origin, vecEnd, ignore_monsters, ENT(pev), &tr);
fDist = (tr.vecEndPos - pev->origin).Length();
// Now we need to find the distance to the water transition.
// (if there is one)
if (UTIL_PointContents(tr.vecEndPos) != CONTENTS_WATER)
{
Vector vecMin = pev->origin;
Vector vecMax = pev->origin + vecDir * fDist;
Vector vecMid;
float diff = fDist;
while (diff > 1.0)
{
vecMid = vecMin + (vecMax - vecMin).Normalize() * diff / 2;
if (UTIL_PointContents(vecMid) == CONTENTS_WATER)
{
vecMin = vecMid;
}
else
{
vecMax = vecMid;
}
diff = (vecMax - vecMin).Length();
}
fDist = (vecMid - pev->origin).Length();
}
// Return new endpoint.
return pev->origin + vecDir * fDist;
}
LINK_ENTITY_TO_CLASS( zapbounce, CZapBounce );
void CZapBounce::Precache( void )
{
PRECACHE_SOUND("debris/zap4.wav");
PRECACHE_SOUND("weapons/electro4.wav");
PRECACHE_SOUND("hassault/hw_shoot1.wav");
PRECACHE_MODEL( RIFTSPR );
PRECACHE_MODEL( RIFTSPR2 );
}
void CZapBounce::Spawn( void )
{
pev->classname = MAKE_STRING( "multizapper" );
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_NOT;
//UTIL_MakeAimVectors( pev->angles );
m_vecStart = pev->origin;
m_vecDir = pev->angles;
m_fDamage = 90;
m_iBounce = 5;
m_bFirstZap = TRUE;
SetThink ( BounceThink );
pev->nextthink = gpGlobals->time + 0.2;
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0,100 );
}
void CZapBounce::BounceThink( void )
{
TraceResult tr;
CBaseEntity *pEntity;
pev->nextthink = gpGlobals->time + 0.05;
// Zap Forward with some randomness.
if (!m_bFirstZap)
{
m_vecDir = m_vecDir * 5 + Vector(RANDOM_FLOAT( -1, 1),
RANDOM_FLOAT( -1, 1),
RANDOM_FLOAT( -1, 1)
);
}
m_vecDir = m_vecDir.Normalize();
// Maximum distance for this segment.
float fDist = 128;
// End point of the streamer
Vector vecEnd = m_vecStart + m_vecDir * fDist;
// Trace to the end of the streamer
UTIL_TraceLine(m_vecStart, vecEnd, dont_ignore_monsters, pentIgnore, &tr);
fDist = (tr.vecEndPos - m_vecStart).Length();
// draw lightning
for (int i = 0; i < 2; i++)
{
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMPOINTS );
WRITE_COORD(m_vecStart.x);
WRITE_COORD(m_vecStart.y);
WRITE_COORD(m_vecStart.z);
WRITE_COORD( tr.vecEndPos.x );
WRITE_COORD( tr.vecEndPos.y );
WRITE_COORD( tr.vecEndPos.z );
WRITE_SHORT( g_sModelIndexLightning );
WRITE_BYTE( 0 ); // Starting frame
WRITE_BYTE( 0 ); // framerate * 0.1
WRITE_BYTE( 10 ); // life * 0.1
WRITE_BYTE( m_iBounce * 10 ); // width
WRITE_BYTE( 10 * (6 - m_iBounce )); // noise
WRITE_BYTE( 96 ); // color r,g,b
WRITE_BYTE( 180 ); // color r,g,b
WRITE_BYTE( 255 ); // color r,g,b
WRITE_BYTE( m_iBounce * 50); // brightness
WRITE_BYTE( 0 ); // scroll speed
MESSAGE_END();
}
m_vecStart = tr.vecEndPos;
// Did we hit an entity? Then do damage.
pEntity = CBaseEntity::Instance(tr.pHit);
if (pEntity) {
if (pEntity->pev->takedamage)
{
ClearMultiDamage( );
pEntity->TraceAttack( VARS(pev->owner), m_fDamage, m_vecDir, &tr, DMG_SHOCK | DMG_ALWAYSGIB );
ApplyMultiDamage( VARS(pev->owner), VARS(pev->owner) );
UTIL_ScreenFade( pEntity, Vector(96,180,255), 2, 0.5, 128, FFADE_IN );
}
// Did we hit a wall? Then reflect.
else
{
float n;
n = -DotProduct(tr.vecPlaneNormal, m_vecDir);
Vector r;
r = 2.0 * tr.vecPlaneNormal * n + m_vecDir;
m_vecDir = r;
}
pentIgnore = ENT(pEntity->pev);
}
// We can hurt ourselves after the first beam.
m_bFirstZap = FALSE;
// bounce once
m_iBounce--;
if (m_iBounce < 1)
{
// UTIL_ClientPrintAll( HUD_PRINTTALK, "<SERVER> Zap bounce point destroyed.\n");
SetThink(SUB_Remove);
pev->nextthink = gpGlobals->time + .1;
return;
}
// split beam?
if (RANDOM_LONG(0,100) < 80)
{
m_fDamage *= .75;
CZapBounce* pRift = (CZapBounce*)CBaseEntity::Create( "zapbounce",
m_vecStart,
m_vecDir,
ENT(pev->owner) );
pRift->m_fDamage = m_fDamage;
pRift->m_iBounce = m_iBounce;
pRift->m_bFirstZap = FALSE;
pRift->pev->nextthink = gpGlobals->time + RANDOM_LONG(.2,.3);
}
}