447 lines
10 KiB
C++
447 lines
10 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.
|
|
*
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
* without written permission from Valve LLC.
|
|
*
|
|
****/
|
|
#include "extdll.h"
|
|
#include "util.h"
|
|
#include "cmbase.h"
|
|
#include "cmbasemonster.h"
|
|
#include "monsters.h"
|
|
#include "customentity.h"
|
|
#include "effects.h"
|
|
#include "weapons.h"
|
|
#include "decals.h"
|
|
#include "func_break.h"
|
|
#include "shake.h"
|
|
|
|
#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired
|
|
|
|
#define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them.
|
|
|
|
|
|
// --------------------------------------------------
|
|
//
|
|
// Beams
|
|
//
|
|
// --------------------------------------------------
|
|
|
|
void CMBeam::Spawn( void )
|
|
{
|
|
pev->solid = SOLID_NOT; // Remove model & collisions
|
|
Precache( );
|
|
}
|
|
|
|
void CMBeam::Precache( void )
|
|
{
|
|
if ( pev->owner )
|
|
SetStartEntity( ENTINDEX( pev->owner ) );
|
|
if ( pev->aiment )
|
|
SetEndEntity( ENTINDEX( pev->aiment ) );
|
|
}
|
|
|
|
void CMBeam::SetStartEntity( int entityIndex )
|
|
{
|
|
pev->sequence = (entityIndex & 0x0FFF) | ((pev->sequence&0xF000)<<12);
|
|
pev->owner = g_engfuncs.pfnPEntityOfEntIndex( entityIndex );
|
|
}
|
|
|
|
void CMBeam::SetEndEntity( int entityIndex )
|
|
{
|
|
pev->skin = (entityIndex & 0x0FFF) | ((pev->skin&0xF000)<<12);
|
|
pev->aiment = g_engfuncs.pfnPEntityOfEntIndex( entityIndex );
|
|
}
|
|
|
|
|
|
// These don't take attachments into account
|
|
const Vector &CMBeam::GetStartPos( void )
|
|
{
|
|
if ( GetType() == BEAM_ENTS )
|
|
{
|
|
edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetStartEntity() );
|
|
return pent->v.origin;
|
|
}
|
|
return pev->origin;
|
|
}
|
|
|
|
|
|
const Vector &CMBeam::GetEndPos( void )
|
|
{
|
|
int type = GetType();
|
|
if ( type == BEAM_POINTS || type == BEAM_HOSE )
|
|
{
|
|
return pev->angles;
|
|
}
|
|
|
|
edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetEndEntity() );
|
|
if ( pent )
|
|
return pent->v.origin;
|
|
return pev->angles;
|
|
}
|
|
|
|
|
|
CMBeam *CMBeam::BeamCreate( const char *pSpriteName, int width )
|
|
{
|
|
// Create a new entity with CMBeam private data
|
|
CMBeam *pBeam = CreateClassPtr( (CMBeam *)NULL );
|
|
|
|
if (pBeam == NULL)
|
|
return NULL;
|
|
|
|
pBeam->pev->classname = MAKE_STRING("beam");
|
|
|
|
pBeam->BeamInit( pSpriteName, width );
|
|
|
|
return pBeam;
|
|
}
|
|
|
|
|
|
void CMBeam::BeamInit( const char *pSpriteName, int width )
|
|
{
|
|
pev->flags |= FL_CUSTOMENTITY;
|
|
SetColor( 255, 255, 255 );
|
|
SetBrightness( 255 );
|
|
SetNoise( 0 );
|
|
SetFrame( 0 );
|
|
SetScrollRate( 0 );
|
|
pev->model = MAKE_STRING( pSpriteName );
|
|
SetTexture( PRECACHE_MODEL( (char *)pSpriteName ) );
|
|
SetWidth( width );
|
|
pev->skin = 0;
|
|
pev->sequence = 0;
|
|
pev->rendermode = 0;
|
|
}
|
|
|
|
|
|
void CMBeam::PointsInit( const Vector &start, const Vector &end )
|
|
{
|
|
SetType( BEAM_POINTS );
|
|
SetStartPos( start );
|
|
SetEndPos( end );
|
|
SetStartAttachment( 0 );
|
|
SetEndAttachment( 0 );
|
|
RelinkBeam();
|
|
}
|
|
|
|
|
|
void CMBeam::HoseInit( const Vector &start, const Vector &direction )
|
|
{
|
|
SetType( BEAM_HOSE );
|
|
SetStartPos( start );
|
|
SetEndPos( direction );
|
|
SetStartAttachment( 0 );
|
|
SetEndAttachment( 0 );
|
|
RelinkBeam();
|
|
}
|
|
|
|
|
|
void CMBeam::PointEntInit( const Vector &start, int endIndex )
|
|
{
|
|
SetType( BEAM_ENTPOINT );
|
|
SetStartPos( start );
|
|
SetEndEntity( endIndex );
|
|
SetStartAttachment( 0 );
|
|
SetEndAttachment( 0 );
|
|
RelinkBeam();
|
|
}
|
|
|
|
void CMBeam::EntsInit( int startIndex, int endIndex )
|
|
{
|
|
SetType( BEAM_ENTS );
|
|
SetStartEntity( startIndex );
|
|
SetEndEntity( endIndex );
|
|
SetStartAttachment( 0 );
|
|
SetEndAttachment( 0 );
|
|
RelinkBeam();
|
|
}
|
|
|
|
|
|
void CMBeam::RelinkBeam( void )
|
|
{
|
|
const Vector &startPos = GetStartPos(), &endPos = GetEndPos();
|
|
|
|
pev->mins.x = min( startPos.x, endPos.x );
|
|
pev->mins.y = min( startPos.y, endPos.y );
|
|
pev->mins.z = min( startPos.z, endPos.z );
|
|
pev->maxs.x = max( startPos.x, endPos.x );
|
|
pev->maxs.y = max( startPos.y, endPos.y );
|
|
pev->maxs.z = max( startPos.z, endPos.z );
|
|
pev->mins = pev->mins - pev->origin;
|
|
pev->maxs = pev->maxs - pev->origin;
|
|
|
|
UTIL_SetSize( pev, pev->mins, pev->maxs );
|
|
UTIL_SetOrigin( pev, pev->origin );
|
|
}
|
|
|
|
#if 0
|
|
void CMBeam::SetObjectCollisionBox( void )
|
|
{
|
|
const Vector &startPos = GetStartPos(), &endPos = GetEndPos();
|
|
|
|
pev->absmin.x = min( startPos.x, endPos.x );
|
|
pev->absmin.y = min( startPos.y, endPos.y );
|
|
pev->absmin.z = min( startPos.z, endPos.z );
|
|
pev->absmax.x = max( startPos.x, endPos.x );
|
|
pev->absmax.y = max( startPos.y, endPos.y );
|
|
pev->absmax.z = max( startPos.z, endPos.z );
|
|
}
|
|
#endif
|
|
|
|
|
|
void CMBeam::TriggerTouch( edict_t *pOther )
|
|
{
|
|
if ( pOther->v.flags & (FL_CLIENT | FL_MONSTER) )
|
|
{
|
|
if ( pev->owner )
|
|
{
|
|
if (pev->owner->v.euser4 != NULL)
|
|
{
|
|
CMBaseEntity *pOwner = GetClassPtr((CMBaseEntity *)VARS(pev->owner));
|
|
pOwner->Use( pOther, this->edict(), USE_TOGGLE, 0 );
|
|
}
|
|
}
|
|
ALERT( at_console, "Firing targets!!!\n" );
|
|
}
|
|
}
|
|
|
|
|
|
edict_t *CMBeam::RandomTargetname( const char *szName )
|
|
{
|
|
int total = 0;
|
|
|
|
edict_t *pEntity = NULL;
|
|
edict_t *pNewEntity = NULL;
|
|
while ((pNewEntity = UTIL_FindEntityByTargetname( pNewEntity, szName )) != NULL)
|
|
{
|
|
total++;
|
|
if (RANDOM_LONG(0,total-1) < 1)
|
|
pEntity = pNewEntity;
|
|
}
|
|
return pEntity;
|
|
}
|
|
|
|
|
|
void CMBeam::DoSparks( const Vector &start, const Vector &end )
|
|
{
|
|
if ( pev->spawnflags & (SF_BEAM_SPARKSTART|SF_BEAM_SPARKEND) )
|
|
{
|
|
if ( pev->spawnflags & SF_BEAM_SPARKSTART )
|
|
{
|
|
UTIL_Sparks( start );
|
|
}
|
|
if ( pev->spawnflags & SF_BEAM_SPARKEND )
|
|
{
|
|
UTIL_Sparks( end );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMBeam::BeamDamage( TraceResult *ptr )
|
|
{
|
|
RelinkBeam();
|
|
if ( ptr->flFraction != 1.0 && ptr->pHit != NULL )
|
|
{
|
|
if (UTIL_IsPlayer(ptr->pHit))
|
|
{
|
|
ClearMultiDamage();
|
|
UTIL_TraceAttack(ptr->pHit, pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM );
|
|
ApplyMultiDamage( pev, pev );
|
|
|
|
if ( pev->spawnflags & SF_BEAM_DECALS )
|
|
{
|
|
if ( UTIL_IsBSPModel(ptr->pHit) )
|
|
UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) );
|
|
}
|
|
|
|
}
|
|
else if (ptr->pHit->v.euser4 != NULL)
|
|
{
|
|
ClearMultiDamage();
|
|
CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(ptr->pHit));
|
|
pMonster->TraceAttack( pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM );
|
|
ApplyMultiDamage( pev, pev );
|
|
|
|
if ( pev->spawnflags & SF_BEAM_DECALS )
|
|
{
|
|
if ( pMonster->IsBSPModel() )
|
|
UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) );
|
|
}
|
|
}
|
|
}
|
|
pev->dmgtime = gpGlobals->time;
|
|
}
|
|
|
|
|
|
void CMSprite::Spawn( void )
|
|
{
|
|
pev->solid = SOLID_NOT;
|
|
pev->movetype = MOVETYPE_NONE;
|
|
pev->effects = 0;
|
|
pev->frame = 0;
|
|
|
|
Precache();
|
|
SET_MODEL( ENT(pev), STRING(pev->model) );
|
|
|
|
m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1;
|
|
if ( pev->targetname && !(pev->spawnflags & SF_SPRITE_STARTON) )
|
|
TurnOff();
|
|
else
|
|
TurnOn();
|
|
|
|
// Worldcraft only sets y rotation, copy to Z
|
|
if ( pev->angles.y != 0 && pev->angles.z == 0 )
|
|
{
|
|
pev->angles.z = pev->angles.y;
|
|
pev->angles.y = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void CMSprite::Precache( void )
|
|
{
|
|
PRECACHE_MODEL( (char *)STRING(pev->model) );
|
|
|
|
// Reset attachment after save/restore
|
|
if ( pev->aiment )
|
|
SetAttachment( pev->aiment, pev->body );
|
|
else
|
|
{
|
|
// Clear attachment
|
|
pev->skin = 0;
|
|
pev->body = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void CMSprite::SpriteInit( const char *pSpriteName, const Vector &origin )
|
|
{
|
|
pev->model = MAKE_STRING(pSpriteName);
|
|
pev->origin = origin;
|
|
Spawn();
|
|
}
|
|
|
|
CMSprite *CMSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate )
|
|
{
|
|
CMSprite *pSprite = CreateClassPtr( (CMSprite *)NULL );
|
|
pSprite->SpriteInit( pSpriteName, origin );
|
|
pSprite->pev->classname = MAKE_STRING("env_sprite");
|
|
pSprite->pev->solid = SOLID_NOT;
|
|
pSprite->pev->movetype = MOVETYPE_NOCLIP;
|
|
if ( animate )
|
|
pSprite->TurnOn();
|
|
|
|
return pSprite;
|
|
}
|
|
|
|
|
|
void CMSprite::AnimateThink( void )
|
|
{
|
|
Animate( pev->framerate * (gpGlobals->time - m_lastTime) );
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
m_lastTime = gpGlobals->time;
|
|
}
|
|
|
|
void CMSprite::AnimateUntilDead( void )
|
|
{
|
|
if ( gpGlobals->time > pev->dmgtime )
|
|
UTIL_Remove(this->edict());
|
|
else
|
|
{
|
|
AnimateThink();
|
|
pev->nextthink = gpGlobals->time;
|
|
}
|
|
}
|
|
|
|
void CMSprite::Expand( float scaleSpeed, float fadeSpeed )
|
|
{
|
|
pev->speed = scaleSpeed;
|
|
pev->health = fadeSpeed;
|
|
SetThink( &CMSprite::ExpandThink );
|
|
|
|
pev->nextthink = gpGlobals->time;
|
|
m_lastTime = gpGlobals->time;
|
|
}
|
|
|
|
|
|
void CMSprite::ExpandThink( void )
|
|
{
|
|
float frametime = gpGlobals->time - m_lastTime;
|
|
pev->scale += pev->speed * frametime;
|
|
pev->renderamt -= pev->health * frametime;
|
|
if ( pev->renderamt <= 0 )
|
|
{
|
|
pev->renderamt = 0;
|
|
UTIL_Remove( this->edict() );
|
|
}
|
|
else
|
|
{
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
m_lastTime = gpGlobals->time;
|
|
}
|
|
}
|
|
|
|
|
|
void CMSprite::Animate( float frames )
|
|
{
|
|
pev->frame += frames;
|
|
if ( pev->frame > m_maxFrame )
|
|
{
|
|
if ( pev->spawnflags & SF_SPRITE_ONCE )
|
|
{
|
|
TurnOff();
|
|
}
|
|
else
|
|
{
|
|
if ( m_maxFrame > 0 )
|
|
pev->frame = fmod( pev->frame, m_maxFrame );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CMSprite::TurnOff( void )
|
|
{
|
|
pev->effects = EF_NODRAW;
|
|
pev->nextthink = 0;
|
|
}
|
|
|
|
|
|
void CMSprite::TurnOn( void )
|
|
{
|
|
pev->effects = 0;
|
|
if ( (pev->framerate && m_maxFrame > 1.0) || (pev->spawnflags & SF_SPRITE_ONCE) )
|
|
{
|
|
SetThink( &CMSprite::AnimateThink );
|
|
pev->nextthink = gpGlobals->time;
|
|
m_lastTime = gpGlobals->time;
|
|
}
|
|
pev->frame = 0;
|
|
}
|
|
|
|
|
|
void CMSprite::Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
int on = pev->effects != EF_NODRAW;
|
|
if ( ShouldToggle( useType, on ) )
|
|
{
|
|
if ( on )
|
|
{
|
|
TurnOff();
|
|
}
|
|
else
|
|
{
|
|
TurnOn();
|
|
}
|
|
}
|
|
}
|