963 lines
27 KiB
C++
963 lines
27 KiB
C++
//=======================================================================
|
|
// Copyright (C) Shambler Team 2005
|
|
// baseentity.cpp - base class
|
|
//=======================================================================
|
|
|
|
#include "extdll.h"
|
|
#include "utils.h"
|
|
#include "cbase.h"
|
|
#include "saverestore.h"
|
|
#include "client.h"
|
|
#include "nodes.h"
|
|
#include "decals.h"
|
|
#include "gamerules.h"
|
|
#include "game.h"
|
|
#include "damage.h"
|
|
#include "defaults.h"
|
|
#include "bullets.h"
|
|
|
|
extern Vector VecBModelOrigin( entvars_t* pevBModel );
|
|
extern DLL_GLOBAL Vector g_vecAttackDir;
|
|
extern void SetObjectCollisionBox( entvars_t *pev );
|
|
extern BOOL NewLevel;
|
|
extern CGraph WorldGraph;
|
|
|
|
//=======================================================================
|
|
// decent mechanisms
|
|
//=======================================================================
|
|
void CBaseEntity::DontThink( void )
|
|
{
|
|
m_fNextThink = 0;
|
|
if (m_pParent == NULL && m_pChild == NULL)
|
|
{
|
|
pev->nextthink = 0;
|
|
m_fPevNextThink = 0;
|
|
}
|
|
}
|
|
|
|
void CBaseEntity :: SetEternalThink( void )
|
|
{
|
|
if (pev->movetype == MOVETYPE_PUSH)
|
|
{
|
|
pev->nextthink = pev->ltime + 1E6;
|
|
m_fPevNextThink = pev->nextthink;
|
|
}
|
|
CBaseEntity *pChild;
|
|
for (pChild = m_pChild; pChild != NULL; pChild = pChild->m_pNextChild)
|
|
pChild->SetEternalThink( );
|
|
}
|
|
|
|
void CBaseEntity :: SetNextThink( float delay, BOOL correctSpeed )
|
|
{
|
|
if (m_pParent || m_pChild )
|
|
{
|
|
if (pev->movetype == MOVETYPE_PUSH)
|
|
m_fNextThink = pev->ltime + delay;
|
|
else m_fNextThink = gpGlobals->time + delay;
|
|
|
|
SetEternalThink( );
|
|
UTIL_MarkChild( this, correctSpeed, FALSE );
|
|
}
|
|
else
|
|
{
|
|
// set nextthink as normal.
|
|
if (pev->movetype == MOVETYPE_PUSH)pev->nextthink = pev->ltime + delay;
|
|
else pev->nextthink = gpGlobals->time + delay;
|
|
m_fPevNextThink = m_fNextThink = pev->nextthink;
|
|
}
|
|
}
|
|
|
|
void CBaseEntity :: AbsoluteNextThink( float time, BOOL correctSpeed )
|
|
{
|
|
if (m_pParent || m_pChild)
|
|
{
|
|
m_fNextThink = time;
|
|
SetEternalThink( );
|
|
UTIL_MarkChild( this, correctSpeed, FALSE );
|
|
}
|
|
else
|
|
{
|
|
// set nextthink as normal.
|
|
pev->nextthink = time;
|
|
m_fPevNextThink = m_fNextThink = pev->nextthink;
|
|
}
|
|
}
|
|
|
|
void CBaseEntity :: ThinkCorrection( void )
|
|
{
|
|
if (pev->nextthink != m_fPevNextThink)
|
|
{
|
|
m_fNextThink += pev->nextthink - m_fPevNextThink;
|
|
m_fPevNextThink = pev->nextthink;
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
// set parent (void ) dinamically link parents
|
|
//=======================================================================
|
|
void CBaseEntity :: SetParent( int m_iNewParent, int m_iAttachment )
|
|
{
|
|
if(!m_iNewParent) //unlink entity from chain
|
|
{
|
|
ResetParent();
|
|
return;//disable
|
|
}
|
|
|
|
CBaseEntity* pParent;
|
|
|
|
if(!m_iAttachment) //try to extract aiment from name
|
|
{
|
|
char *name = (char*)STRING(m_iNewParent);
|
|
for (char *c = name; *c; c++)
|
|
{
|
|
if (*c == '.')
|
|
{
|
|
m_iAttachment = atoi(c+1);
|
|
name[strlen(name)-2] = 0;
|
|
pParent = UTIL_FindEntityByTargetname( NULL, name);
|
|
SetParent( pParent, m_iAttachment);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
pParent = UTIL_FindEntityByTargetname( NULL, STRING(m_iNewParent));
|
|
SetParent( pParent, m_iAttachment );
|
|
}
|
|
|
|
//=======================================================================
|
|
// set parent main function
|
|
//=======================================================================
|
|
void CBaseEntity :: SetParent( CBaseEntity* pParent, int m_iAttachment )
|
|
{
|
|
m_pParent = pParent;
|
|
|
|
if(!m_pParent)
|
|
{
|
|
Msg("=========/Xash Parent System Info:/=========\n");
|
|
if(pev->targetname) Msg("Warning! Not found parent for %s with name %s\n", STRING(pev->classname), STRING(pev->targetname) );
|
|
else Msg("Warning! Not found parent for %s\n", STRING(pev->classname) );
|
|
SHIFT;
|
|
ResetParent();//lose parent or not found parent
|
|
return;
|
|
}
|
|
|
|
//check for himself parent
|
|
if(m_pParent == this)
|
|
{
|
|
ALERT(at_console, "=========/Xash Parent System Info:/=========\n");
|
|
if(pev->targetname) Msg( "ERROR! %s with name %s has illegal parent\n", STRING(pev->classname), STRING(pev->targetname) );
|
|
else Msg( "ERROR! %s has illegal parent\n", STRING(pev->classname) );
|
|
SHIFT;
|
|
ResetParent();//clear parent
|
|
return;
|
|
}
|
|
|
|
CBaseEntity *pCurChild = m_pParent->m_pChild;
|
|
while (pCurChild) //check that this entity isn't already in the list of children
|
|
{
|
|
if (pCurChild == this) break;
|
|
pCurChild = pCurChild->m_pNextChild;
|
|
}
|
|
if(!pCurChild)
|
|
{
|
|
m_pNextChild = m_pParent->m_pChild; // may be null: that's fine by me.
|
|
m_pParent->m_pChild = this;
|
|
|
|
if(m_iAttachment)
|
|
{
|
|
if(pev->flags & FL_POINTENTITY || pev->flags & FL_MONSTER)
|
|
{
|
|
pev->skin = ENTINDEX(m_pParent->edict());
|
|
pev->body = m_iAttachment;
|
|
pev->aiment = m_pParent->edict();
|
|
pev->movetype = MOVETYPE_FOLLOW;
|
|
}
|
|
else //error
|
|
{
|
|
ALERT(at_console, "=========/Xash Parent System Info:/=========\n");
|
|
if(pev->targetname) Msg("ERROR! %s with name %s not following with aiment %d!(yet)\n", STRING(pev->classname), STRING(pev->targetname), m_iAttachment );
|
|
else Msg("ERROR! %s not following with aiment %d!(yet)\n", STRING(pev->classname), m_iAttachment );
|
|
SHIFT;
|
|
}
|
|
return;
|
|
}
|
|
else//appllayed to origin
|
|
{
|
|
if (pev->movetype == MOVETYPE_NONE)
|
|
{
|
|
if (pev->solid == SOLID_BSP)
|
|
pev->movetype = MOVETYPE_PUSH;
|
|
else pev->movetype = MOVETYPE_NOCLIP;
|
|
SetBits (pFlags, PF_MOVENONE);//member movetype
|
|
}
|
|
if(m_pParent->pev->movetype == MOVETYPE_WALK)//parent is walking monster?
|
|
{
|
|
SetBits (pFlags, PF_POSTORG);//copy pos from parent every frame
|
|
pev->solid = SOLID_NOT;//set non solid
|
|
}
|
|
pParentOrigin = m_pParent->pev->origin;
|
|
pParentAngles = m_pParent->pev->angles;
|
|
}
|
|
|
|
if (m_pParent->m_vecSpawnOffset != g_vecZero)
|
|
{
|
|
UTIL_AssignOrigin(this, pev->origin + m_pParent->m_vecSpawnOffset);
|
|
m_vecSpawnOffset = m_vecSpawnOffset + m_pParent->m_vecSpawnOffset;
|
|
}
|
|
OffsetOrigin = pev->origin - pParentOrigin;
|
|
OffsetAngles = pev->angles - pParentAngles;
|
|
|
|
if((m_pParent->pFlags & PF_ANGULAR && OffsetOrigin != g_vecZero) || m_pParent->pev->flags & FL_POINTENTITY)
|
|
{
|
|
SetBits (pFlags, PF_POSTORG);//magic stuff
|
|
//GetPInfo( this );
|
|
}
|
|
|
|
if(g_serveractive)//maybe parent is moving ?
|
|
{
|
|
pev->velocity += m_pParent->pev->velocity;
|
|
pev->avelocity += m_pParent->pev->avelocity;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
// reset parent (void ) dinamically unlink parents
|
|
//=======================================================================
|
|
void CBaseEntity :: ResetParent( void )
|
|
{
|
|
if(pFlags & PF_MOVENONE)//this entity was static e.g. func_wall
|
|
{
|
|
ClearBits (pFlags, PF_MOVENONE);
|
|
pev->movetype = MOVETYPE_NONE;
|
|
}
|
|
|
|
if ( !g_pWorld )return; //???
|
|
|
|
CBaseEntity* pTemp;
|
|
|
|
for (pTemp = g_pWorld; pTemp->m_pLinkList != NULL; pTemp = pTemp->m_pLinkList)
|
|
{
|
|
if (this == pTemp->m_pLinkList)
|
|
{
|
|
pTemp->m_pLinkList = this->m_pLinkList;//save pointer
|
|
this->m_pLinkList = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_pParent)
|
|
{
|
|
pTemp = m_pParent->m_pChild;
|
|
|
|
if (pTemp == this)m_pParent->m_pChild = this->m_pNextChild;
|
|
else
|
|
{
|
|
while (pTemp->m_pNextChild)
|
|
{
|
|
if (pTemp->m_pNextChild == this)
|
|
{
|
|
pTemp->m_pNextChild = this->m_pNextChild;
|
|
break;
|
|
}
|
|
pTemp = pTemp->m_pNextChild;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_pNextChild)
|
|
{
|
|
CBaseEntity* pCur = m_pChild;
|
|
CBaseEntity* pNext;
|
|
while (pCur != NULL)
|
|
{
|
|
pNext = pCur->m_pNextChild;
|
|
//bring children to a stop
|
|
UTIL_SetChildVelocity (pCur, g_vecZero, MAX_CHILDS);
|
|
UTIL_SetChildAvelocity(pCur, g_vecZero, MAX_CHILDS);
|
|
pCur->m_pParent = NULL;
|
|
pCur->m_pNextChild = NULL;
|
|
pCur = pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
// setup physics (execute once at spawn)
|
|
//=======================================================================
|
|
void CBaseEntity :: SetupPhysics( void )
|
|
{
|
|
//rebuild all parents
|
|
if(pFlags & PF_LINKCHILD) LinkChild( this );
|
|
if(m_physinit) return;
|
|
SetParent(); //set all parents
|
|
m_physinit = true;
|
|
PostSpawn();//post spawn
|
|
}
|
|
|
|
void CBaseEntity :: RestorePhysics( void )
|
|
{
|
|
if(m_iParent) SetParent();
|
|
}
|
|
|
|
void CBaseEntity :: ClearPointers( void )
|
|
{
|
|
m_pChild = NULL;
|
|
m_pNextChild = NULL;
|
|
m_pLinkList = NULL;
|
|
}
|
|
|
|
//=========================================================
|
|
// FVisible - returns true if a line can be traced from
|
|
// the caller's eyes to the target vector
|
|
//=========================================================
|
|
BOOL CBaseEntity :: FVisible ( const Vector &vecOrigin )
|
|
{
|
|
TraceResult tr;
|
|
Vector vecLookerOrigin;
|
|
|
|
vecLookerOrigin = EyePosition();//look through the caller's 'eyes'
|
|
|
|
UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr);
|
|
|
|
if (tr.flFraction != 1.0)
|
|
return FALSE;
|
|
return TRUE;// line of sight is valid.
|
|
}
|
|
|
|
//=========================================================
|
|
// FVisible - returns true if a line can be traced from
|
|
// the caller's eyes to the target
|
|
//=========================================================
|
|
BOOL CBaseEntity :: FVisible ( CBaseEntity *pEntity )
|
|
{
|
|
TraceResult tr;
|
|
Vector vecLookerOrigin;
|
|
Vector vecTargetOrigin;
|
|
|
|
if( FBitSet( pEntity->pev->flags, FL_NOTARGET ))
|
|
return FALSE;
|
|
|
|
// don't look through water
|
|
if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0))
|
|
return FALSE;
|
|
|
|
vecLookerOrigin = pev->origin + pev->view_ofs;//look through the caller's 'eyes'
|
|
vecTargetOrigin = pEntity->EyePosition();
|
|
|
|
UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr);
|
|
|
|
if (tr.flFraction != 1.0 && tr.pHit != ENT(pEntity->pev))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
//=======================================================================
|
|
// fire bullets
|
|
//=======================================================================
|
|
void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker )
|
|
{
|
|
static int tracerCount;
|
|
int tracer;
|
|
TraceResult tr;
|
|
Vector vecRight = gpGlobals->v_right;
|
|
Vector vecUp = gpGlobals->v_up;
|
|
|
|
if ( pevAttacker == NULL )
|
|
pevAttacker = pev; // the default attacker is ourselves
|
|
|
|
ClearMultiDamage();
|
|
gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB;
|
|
|
|
for (ULONG iShot = 1; iShot <= cShots; iShot++)
|
|
{
|
|
// get circular gaussian spread
|
|
float x, y, z;
|
|
do {
|
|
x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5);
|
|
y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5);
|
|
z = x*x+y*y;
|
|
} while (z > 1);
|
|
|
|
Vector vecDir = vecDirShooting +
|
|
x * vecSpread.x * vecRight +
|
|
y * vecSpread.y * vecUp;
|
|
Vector vecEnd;
|
|
|
|
vecEnd = vecSrc + vecDir * flDistance;
|
|
UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr);
|
|
|
|
tracer = 0;
|
|
if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0)
|
|
{
|
|
Vector vecTracerSrc;
|
|
|
|
if ( IsPlayer() )
|
|
{// adjust tracer position for player
|
|
vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16;
|
|
}
|
|
else
|
|
{
|
|
vecTracerSrc = vecSrc;
|
|
}
|
|
|
|
if ( iTracerFreq != 1 ) // guns that always trace also always decal
|
|
tracer = 1;
|
|
switch( iBulletType )
|
|
{
|
|
case BULLET_MP5:
|
|
case BULLET_9MM:
|
|
case BULLET_12MM:
|
|
case BULLET_357:
|
|
case BULLET_556:
|
|
case BULLET_762:
|
|
case BULLET_BUCKSHOT:
|
|
default:
|
|
MESSAGE_BEGIN( MSG_PAS, gmsg.TempEntity, vecTracerSrc );
|
|
WRITE_BYTE( TE_TRACER );
|
|
WRITE_COORD( vecTracerSrc.x );
|
|
WRITE_COORD( vecTracerSrc.y );
|
|
WRITE_COORD( vecTracerSrc.z );
|
|
WRITE_COORD( tr.vecEndPos.x );
|
|
WRITE_COORD( tr.vecEndPos.y );
|
|
WRITE_COORD( tr.vecEndPos.z );
|
|
MESSAGE_END();
|
|
break;
|
|
}
|
|
}
|
|
// do damage, paint decals
|
|
if (tr.flFraction != 1.0)
|
|
{
|
|
CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
|
|
|
|
if ( iDamage )
|
|
{
|
|
pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) );
|
|
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
}
|
|
else switch(iBulletType)
|
|
{
|
|
default:
|
|
case BULLET_9MM:
|
|
pEntity->TraceAttack(pevAttacker, _9MM_DMG, vecDir, &tr, DMG_BULLET);
|
|
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
|
|
break;
|
|
|
|
case BULLET_MP5:
|
|
pEntity->TraceAttack(pevAttacker, _MP5_DMG, vecDir, &tr, DMG_BULLET);
|
|
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
|
|
break;
|
|
|
|
case BULLET_12MM:
|
|
pEntity->TraceAttack(pevAttacker, _12MM_DMG, vecDir, &tr, DMG_BULLET);
|
|
if ( !tracer )
|
|
{
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
}
|
|
break;
|
|
|
|
case BULLET_556:
|
|
pEntity->TraceAttack(pevAttacker, _556_DMG, vecDir, &tr, DMG_BULLET);
|
|
if ( !tracer )
|
|
{
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
}
|
|
break;
|
|
|
|
case BULLET_762:
|
|
pEntity->TraceAttack(pevAttacker, _762_DMG, vecDir, &tr, DMG_BULLET);
|
|
if ( !tracer )
|
|
{
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
}
|
|
break;
|
|
|
|
case BULLET_BUCKSHOT:
|
|
pEntity->TraceAttack(pevAttacker, BUCKSHOT_DMG, vecDir, &tr, DMG_BULLET);
|
|
if ( !tracer )
|
|
{
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
}
|
|
break;
|
|
|
|
case BULLET_357:
|
|
pEntity->TraceAttack(pevAttacker, _357_DMG, vecDir, &tr, DMG_BULLET);
|
|
if ( !tracer )
|
|
{
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
DecalGunshot( &tr, iBulletType );
|
|
}
|
|
break;
|
|
|
|
case BULLET_NONE: // FIX
|
|
pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
|
|
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
|
|
// only decal glass
|
|
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
|
|
{
|
|
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
// make bullet trails
|
|
UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 );
|
|
}
|
|
ApplyMultiDamage(pev, pevAttacker);
|
|
}
|
|
|
|
//=======================================================================
|
|
// traceing operations
|
|
//=======================================================================
|
|
void CBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
|
{
|
|
if (BloodColor() == DONT_BLEED)
|
|
return;
|
|
|
|
if (flDamage == 0)
|
|
return;
|
|
|
|
if (!(bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR)))
|
|
return;
|
|
|
|
// make blood decal on the wall!
|
|
TraceResult Bloodtr;
|
|
Vector vecTraceDir;
|
|
float flNoise;
|
|
int cCount;
|
|
int i;
|
|
|
|
if (flDamage < 10)
|
|
{
|
|
flNoise = 0.1;
|
|
cCount = 1;
|
|
}
|
|
else if (flDamage < 25)
|
|
{
|
|
flNoise = 0.2;
|
|
cCount = 2;
|
|
}
|
|
else
|
|
{
|
|
flNoise = 0.3;
|
|
cCount = 4;
|
|
}
|
|
|
|
for ( i = 0 ; i < cCount ; i++ )
|
|
{
|
|
vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
|
|
|
|
vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise );
|
|
vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise );
|
|
vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise );
|
|
|
|
UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr);
|
|
|
|
if ( Bloodtr.flFraction != 1.0 )
|
|
{
|
|
UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBaseEntity::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
|
{
|
|
Vector vecOrigin = ptr->vecEndPos - vecDir * 4;
|
|
|
|
if ( pev->takedamage )
|
|
{
|
|
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
|
|
|
int blood = BloodColor();
|
|
|
|
if ( blood != DONT_BLEED )
|
|
{
|
|
SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood.
|
|
TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
|
|
}
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
// take damage\health
|
|
//=======================================================================
|
|
int CBaseEntity :: TakeHealth( float flHealth, int bitsDamageType )
|
|
{
|
|
if(!pev->takedamage) return 0;
|
|
if ( pev->health >= pev->max_health ) return 0;
|
|
|
|
pev->health += flHealth;
|
|
pev->health = min(pev->health, pev->max_health);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CBaseEntity :: TakeArmor( float flArmor, int suit )
|
|
{
|
|
if(!pev->takedamage) return 0;
|
|
if (pev->armorvalue >= MAX_NORMAL_BATTERY) return 0;
|
|
|
|
pev->armorvalue += flArmor;
|
|
pev->armorvalue = min(pev->armorvalue, MAX_NORMAL_BATTERY);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
|
{
|
|
Vector vecTemp;
|
|
|
|
if (!pev->takedamage) return 0;
|
|
|
|
// if Attacker == Inflictor, the attack was a melee or other instant-hit attack.
|
|
// (that is, no actual entity projectile was involved in the attack so use the shooter's origin).
|
|
if ( pevAttacker == pevInflictor )
|
|
{
|
|
vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) );
|
|
}
|
|
else // an actual missile was involved.
|
|
{
|
|
vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) );
|
|
}
|
|
|
|
// this global is still used for glass and other non-monster killables, along with decals.
|
|
g_vecAttackDir = vecTemp.Normalize();
|
|
|
|
// save damage based on the target's armor level
|
|
|
|
// figure momentum add (don't let hurt brushes or other triggers move player)
|
|
if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) )
|
|
{
|
|
Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5;
|
|
vecDir = vecDir.Normalize();
|
|
|
|
float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5;
|
|
|
|
if (flForce > 1000.0) flForce = 1000.0;
|
|
pev->velocity = pev->velocity + vecDir * flForce;
|
|
}
|
|
|
|
// do the damage
|
|
pev->health -= flDamage;
|
|
if (pev->health <= 0)
|
|
{
|
|
Killed( pevAttacker, GIB_NORMAL );
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int CBaseEntity :: DamageDecal( int bitsDamageType )
|
|
{
|
|
if ( pev->rendermode == kRenderTransAlpha )
|
|
return -1;
|
|
|
|
if ( pev->rendermode != kRenderNormal )
|
|
return DECAL_BPROOF1;
|
|
|
|
return DECAL_GUNSHOT1 + RANDOM_LONG(0, 4);
|
|
}
|
|
|
|
//=======================================================================
|
|
// killed/create operations
|
|
//=======================================================================
|
|
CBaseEntity * CBaseEntity::Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner )
|
|
{
|
|
edict_t *pent;
|
|
int istr = ALLOC_STRING(szName);
|
|
CBaseEntity *pEntity;
|
|
|
|
// check for virtual entities
|
|
if( FUNCTION_FROM_NAME( szName ) != 0 )
|
|
{
|
|
pent = CREATE_NAMED_ENTITY( istr );
|
|
if ( FNullEnt( pent )) return NULL;
|
|
}
|
|
else if(!strncmp( szName, "weapon_", 7))
|
|
{
|
|
//may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY(MAKE_STRING("weapon_generic"));
|
|
if ( FNullEnt( pent )) return NULL; // this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else if(!strncmp( szName, "item_", 5))
|
|
{
|
|
//may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
|
|
if ( FNullEnt( pent )) return NULL; // this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else if(!strncmp( szName, "ammo_", 5))
|
|
{
|
|
//may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
|
|
if ( FNullEnt( pent )) return NULL; // this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else //unknown error
|
|
{
|
|
Msg("can't create %s\n", szName );
|
|
return NULL;
|
|
}
|
|
|
|
pEntity = Instance( pent );
|
|
pEntity->pev->owner = pentOwner;
|
|
pEntity->pev->origin = vecOrigin;
|
|
pEntity->pev->angles = vecAngles;
|
|
DispatchSpawn( pEntity->edict() );
|
|
return pEntity;
|
|
}
|
|
|
|
CBaseEntity * CBaseEntity::CreateGib( char *szName, char *szModel )
|
|
{
|
|
edict_t *pent;
|
|
CBaseEntity *pEntity;
|
|
string_t model = MAKE_STRING( szModel );
|
|
int istr = ALLOC_STRING(szName);
|
|
|
|
//check for virtual entities
|
|
if( FUNCTION_FROM_NAME( szName ))
|
|
{
|
|
pent = CREATE_NAMED_ENTITY( istr );
|
|
if ( FNullEnt( pent )) return NULL;
|
|
}
|
|
else if(!strncmp( szName, "weapon_", 7))
|
|
{
|
|
// may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY( MAKE_STRING( "weapon_generic" ));
|
|
if ( FNullEnt( pent )) return NULL; //this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else if(!strncmp( szName, "item_", 5))
|
|
{
|
|
//may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
|
|
if ( FNullEnt( pent )) return NULL;//this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else if(!strncmp( szName, "ammo_", 5))
|
|
{
|
|
//may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
|
|
if ( FNullEnt( pent )) return NULL;//this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else //unknown error
|
|
{
|
|
Msg("can't create %s\n", szName );
|
|
return NULL;
|
|
}
|
|
|
|
pEntity = Instance( pent );
|
|
DispatchSpawn( pEntity->edict() );
|
|
|
|
if(!FStringNull( model ))
|
|
{
|
|
UTIL_SetModel( pEntity->edict(), szModel );
|
|
Msg("szModel %s\n", szModel );
|
|
}
|
|
return pEntity;
|
|
}
|
|
|
|
void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib )
|
|
{
|
|
pev->takedamage = DAMAGE_NO;
|
|
pev->deadflag = DEAD_DEAD;
|
|
UTIL_Remove( this );
|
|
}
|
|
|
|
void CBaseEntity::UpdateOnRemove( void )
|
|
{
|
|
ResetParent();
|
|
|
|
if ( FBitSet( pev->flags, FL_GRAPHED ) )
|
|
{
|
|
for (int i = 0 ; i < WorldGraph.m_cLinks ; i++ )
|
|
{
|
|
if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev )
|
|
WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL;
|
|
}
|
|
}
|
|
if ( pev->globalname ) gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD );
|
|
}
|
|
|
|
//=======================================================================
|
|
// three methods of remove entity
|
|
//=======================================================================
|
|
void CBaseEntity :: Remove( void )
|
|
{
|
|
UpdateOnRemove();
|
|
if (pev->health > 0)pev->health = 0;
|
|
REMOVE_ENTITY(ENT(pev));
|
|
}
|
|
|
|
void CBaseEntity :: PVSRemove( void )
|
|
{
|
|
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict()))) SetThink( Remove );
|
|
SetNextThink( 0.1 );
|
|
}
|
|
|
|
void CBaseEntity :: Fadeout( void )
|
|
{
|
|
if (pev->rendermode == kRenderNormal)
|
|
{
|
|
pev->renderamt = 255;
|
|
pev->rendermode = kRenderTransTexture;
|
|
}
|
|
|
|
pev->solid = SOLID_NOT;
|
|
pev->avelocity = g_vecZero;
|
|
|
|
if ( pev->renderamt > 7 )
|
|
{
|
|
pev->renderamt -= 7;
|
|
SetNextThink( 0.1 );
|
|
}
|
|
else
|
|
{
|
|
pev->renderamt = 0;
|
|
SetNextThink( 0.2 );
|
|
SetThink( Remove );
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
// global save\restore data
|
|
//=======================================================================
|
|
TYPEDESCRIPTION CBaseEntity::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ),
|
|
|
|
//parent system saves
|
|
DEFINE_FIELD( CBaseEntity, m_iParent, FIELD_STRING ),
|
|
DEFINE_FIELD( CBaseEntity, m_pParent, FIELD_CLASSPTR ),
|
|
DEFINE_FIELD( CBaseEntity, m_pChild, FIELD_CLASSPTR ),
|
|
DEFINE_FIELD( CBaseEntity, m_pNextChild, FIELD_CLASSPTR ),
|
|
DEFINE_FIELD( CBaseEntity, OffsetOrigin, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseEntity, OffsetAngles, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseEntity, pFlags, FIELD_INTEGER ),
|
|
DEFINE_FIELD( CBaseEntity, m_physinit, FIELD_BOOLEAN ),
|
|
|
|
//local child coordinates
|
|
DEFINE_FIELD( CBaseEntity, PostOrigin, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseEntity, PostAngles, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseEntity, PostVelocity, FIELD_VECTOR ),
|
|
DEFINE_FIELD( CBaseEntity, PostAvelocity, FIELD_VECTOR ),
|
|
|
|
//think time
|
|
DEFINE_FIELD( CBaseEntity, m_fNextThink, FIELD_TIME ),
|
|
DEFINE_FIELD( CBaseEntity, m_fPevNextThink, FIELD_TIME ),
|
|
|
|
DEFINE_FIELD( CBaseEntity, m_iStyle, FIELD_INTEGER ),
|
|
DEFINE_FIELD( CBaseEntity, m_iClassType, FIELD_INTEGER ),
|
|
DEFINE_FIELD( CBaseEntity, m_iAcessLevel, FIELD_INTEGER ),
|
|
|
|
DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ),
|
|
DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ),
|
|
DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ),
|
|
DEFINE_FIELD( CBaseEntity, m_pfnBlocked, FIELD_FUNCTION ),
|
|
};
|
|
|
|
int CBaseEntity::Save( CSave &save )
|
|
{
|
|
ThinkCorrection();
|
|
|
|
if ( save.WriteEntVars( "ENTVARS", pev ) )
|
|
{
|
|
if (pev->targetname)
|
|
return save.WriteFields( STRING(pev->targetname), "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
|
|
else return save.WriteFields( STRING(pev->classname ), "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CBaseEntity :: Restore( CRestore &restore )
|
|
{
|
|
int status;
|
|
|
|
status = restore.ReadEntVars( "ENTVARS", pev );
|
|
if( status ) status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE( m_SaveData ));
|
|
|
|
// restore edict class here
|
|
SetObjectClass( m_iClassType );
|
|
|
|
if( pev->modelindex != 0 && !FStringNull( pev->model ))
|
|
{
|
|
Vector mins = pev->mins, maxs = pev->maxs; // Set model is about to destroy these
|
|
|
|
UTIL_PrecacheModel( pev->model );
|
|
UTIL_SetModel( ENT( pev ), pev->model );
|
|
UTIL_SetSize( pev, mins, maxs );
|
|
}
|
|
return status;
|
|
}
|
|
|
|
//=======================================================================
|
|
// collisoin boxes
|
|
//=======================================================================
|
|
void CBaseEntity::SetObjectCollisionBox( void )
|
|
{
|
|
::SetObjectCollisionBox( pev );
|
|
}
|
|
|
|
int CBaseEntity :: Intersects( CBaseEntity *pOther )
|
|
{
|
|
if ( pOther->pev->absmin.x > pev->absmax.x ||
|
|
pOther->pev->absmin.y > pev->absmax.y ||
|
|
pOther->pev->absmin.z > pev->absmax.z ||
|
|
pOther->pev->absmax.x < pev->absmin.x ||
|
|
pOther->pev->absmax.y < pev->absmin.y ||
|
|
pOther->pev->absmax.z < pev->absmin.z )
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
//=======================================================================
|
|
// Dormant operations
|
|
//=======================================================================
|
|
void CBaseEntity :: MakeDormant( void )
|
|
{
|
|
SetBits( pev->flags, FL_DORMANT );
|
|
|
|
pev->solid = SOLID_NOT;
|
|
pev->movetype = MOVETYPE_NONE;
|
|
SetBits( pev->effects, EF_NODRAW );
|
|
DontThink();
|
|
UTIL_SetOrigin( this, pev->origin );
|
|
}
|
|
|
|
int CBaseEntity :: IsDormant( void )
|
|
{
|
|
return FBitSet( pev->flags, FL_DORMANT );
|
|
}
|
|
|
|
BOOL CBaseEntity :: IsInWorld( void )
|
|
{
|
|
if (pev->origin.x >= MAP_HALFSIZE) return FALSE;
|
|
if (pev->origin.y >= MAP_HALFSIZE) return FALSE;
|
|
if (pev->origin.z >= MAP_HALFSIZE) return FALSE;
|
|
if (pev->origin.x <= -MAP_HALFSIZE) return FALSE;
|
|
if (pev->origin.y <= -MAP_HALFSIZE) return FALSE;
|
|
if (pev->origin.z <= -MAP_HALFSIZE) return FALSE;
|
|
if (pev->velocity.x >= MAX_VELOCITY) return FALSE;
|
|
if (pev->velocity.y >= MAX_VELOCITY) return FALSE;
|
|
if (pev->velocity.z >= MAX_VELOCITY) return FALSE;
|
|
if (pev->velocity.x <= -MAX_VELOCITY) return FALSE;
|
|
if (pev->velocity.y <= -MAX_VELOCITY) return FALSE;
|
|
if (pev->velocity.z <= -MAX_VELOCITY) return FALSE;
|
|
|
|
return TRUE;
|
|
} |