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.

553 lines
14 KiB

9 years ago
/***
*
* Copyright (c) 1996-2002, 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.
*
****/
/*
===== subs.cpp ========================================================
frequently used global functions
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "saverestore.h"
#include "nodes.h"
#include "doors.h"
extern CGraph WorldGraph;
8 years ago
extern BOOL FEntIsVisible( entvars_t *pev, entvars_t *pevTarget );
9 years ago
extern DLL_GLOBAL int g_iSkillLevel;
// Landmark class
8 years ago
void CPointEntity::Spawn( void )
9 years ago
{
pev->solid = SOLID_NOT;
8 years ago
//UTIL_SetSize( pev, g_vecZero, g_vecZero );
9 years ago
}
class CNullEntity : public CBaseEntity
{
public:
void Spawn( void );
};
// Null Entity, remove on startup
8 years ago
void CNullEntity::Spawn( void )
9 years ago
{
8 years ago
REMOVE_ENTITY( ENT( pev ) );
9 years ago
}
8 years ago
LINK_ENTITY_TO_CLASS( info_null, CNullEntity )
9 years ago
class CBaseDMStart : public CPointEntity
{
public:
8 years ago
void KeyValue( KeyValueData *pkvd );
BOOL IsTriggered( CBaseEntity *pEntity );
9 years ago
private:
};
// These are the new entry points to entities.
LINK_ENTITY_TO_CLASS( info_player_deathmatch, CBaseDMStart )
8 years ago
LINK_ENTITY_TO_CLASS( info_player_start, CPointEntity )
LINK_ENTITY_TO_CLASS( info_landmark, CPointEntity )
9 years ago
void CBaseDMStart::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "master" ) )
9 years ago
{
8 years ago
pev->netname = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
BOOL CBaseDMStart::IsTriggered( CBaseEntity *pEntity )
{
BOOL master = UTIL_IsMasterTriggered( pev->netname, pEntity );
return master;
}
// This updates global tables that need to know about entities being removed
void CBaseEntity::UpdateOnRemove( void )
{
8 years ago
int i;
9 years ago
8 years ago
if( FBitSet( pev->flags, FL_GRAPHED ) )
9 years ago
{
8 years ago
// this entity was a LinkEnt in the world node graph, so we must remove it from
// the graph since we are removing it from the world.
for( i = 0; i < WorldGraph.m_cLinks; i++ )
9 years ago
{
8 years ago
if( WorldGraph.m_pLinkPool[i].m_pLinkEnt == pev )
9 years ago
{
// if this link has a link ent which is the same ent that is removing itself, remove it!
8 years ago
WorldGraph.m_pLinkPool[i].m_pLinkEnt = NULL;
9 years ago
}
}
}
8 years ago
if( pev->globalname )
9 years ago
gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD );
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
//Killtarget didn't do this before, so the counter broke. - Solokiller
if( CBaseEntity* pOwner = pev->owner ? Instance( pev->owner ) : 0 )
{
7 years ago
pOwner->DeathNotice( pev );
}
9 years ago
}
// Convenient way to delay removing oneself
8 years ago
void CBaseEntity::SUB_Remove( void )
9 years ago
{
UpdateOnRemove();
8 years ago
if( pev->health > 0 )
9 years ago
{
// this situation can screw up monsters who can't tell their entity pointers are invalid.
pev->health = 0;
8 years ago
ALERT( at_aiconsole, "SUB_Remove called on entity with health > 0\n" );
9 years ago
}
8 years ago
REMOVE_ENTITY( ENT( pev ) );
9 years ago
}
// Convenient way to explicitly do nothing (passed to functions that require a method)
8 years ago
void CBaseEntity::SUB_DoNothing( void )
9 years ago
{
}
// Global Savedata for Delay
TYPEDESCRIPTION CBaseDelay::m_SaveData[] =
9 years ago
{
DEFINE_FIELD( CBaseDelay, m_flDelay, FIELD_FLOAT ),
DEFINE_FIELD( CBaseDelay, m_iszKillTarget, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CBaseDelay, CBaseEntity )
9 years ago
8 years ago
void CBaseDelay::KeyValue( KeyValueData *pkvd )
9 years ago
{
8 years ago
if( FStrEq( pkvd->szKeyName, "delay" ) )
9 years ago
{
m_flDelay = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "killtarget" ) )
9 years ago
{
8 years ago
m_iszKillTarget = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
else
{
CBaseEntity::KeyValue( pkvd );
}
}
/*
==============================
SUB_UseTargets
If self.delay is set, a DelayedUse entity will be created that will actually
do the SUB_UseTargets after that many seconds have passed.
Removes all entities with a targetname that match self.killtarget,
and removes them, so some events can remove other triggers.
Search for (string)targetname in all entities that
match (string)self.target and call their .use function (if they have one)
==============================
*/
8 years ago
void CBaseEntity::SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value )
9 years ago
{
//
// fire targets
//
8 years ago
if( !FStringNull( pev->target ) )
9 years ago
{
8 years ago
FireTargets( STRING( pev->target ), pActivator, this, useType, value );
9 years ago
}
}
void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
edict_t *pentTarget = NULL;
8 years ago
if( !targetName )
9 years ago
return;
ALERT( at_aiconsole, "Firing: (%s)\n", targetName );
8 years ago
for( ; ; )
9 years ago
{
8 years ago
pentTarget = FIND_ENTITY_BY_TARGETNAME( pentTarget, targetName );
if( FNullEnt( pentTarget ) )
9 years ago
break;
CBaseEntity *pTarget = CBaseEntity::Instance( pentTarget );
8 years ago
if( pTarget && !( pTarget->pev->flags & FL_KILLME ) ) // Don't use dying ents
9 years ago
{
8 years ago
ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING( pTarget->pev->classname ), targetName );
9 years ago
pTarget->Use( pActivator, pCaller, useType, value );
}
}
}
LINK_ENTITY_TO_CLASS( DelayedUse, CBaseDelay )
9 years ago
8 years ago
void CBaseDelay::SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value )
9 years ago
{
//
// exit immediatly if we don't have a target or kill target
//
8 years ago
if( FStringNull( pev->target ) && !m_iszKillTarget )
9 years ago
return;
//
// check for a delay
//
8 years ago
if( m_flDelay != 0 )
9 years ago
{
// create a temp object to fire at a later time
8 years ago
CBaseDelay *pTemp = GetClassPtr( (CBaseDelay *)NULL );
pTemp->pev->classname = MAKE_STRING( "DelayedUse" );
9 years ago
pTemp->pev->nextthink = gpGlobals->time + m_flDelay;
pTemp->SetThink( &CBaseDelay::DelayThink );
9 years ago
// Save the useType
pTemp->pev->button = (int)useType;
pTemp->m_iszKillTarget = m_iszKillTarget;
pTemp->m_flDelay = 0; // prevent "recursion"
pTemp->pev->target = pev->target;
// HACKHACK
// This wasn't in the release build of Half-Life. We should have moved m_hActivator into this class
// but changing member variable hierarchy would break save/restore without some ugly code.
// This code is not as ugly as that code
8 years ago
if( pActivator && pActivator->IsPlayer() ) // If a player activates, then save it
9 years ago
{
pTemp->pev->owner = pActivator->edict();
}
else
{
pTemp->pev->owner = NULL;
}
return;
}
//
// kill the killtargets
//
8 years ago
if( m_iszKillTarget )
9 years ago
{
edict_t *pentKillTarget = NULL;
8 years ago
ALERT( at_aiconsole, "KillTarget: %s\n", STRING( m_iszKillTarget ) );
pentKillTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( m_iszKillTarget ) );
while( !FNullEnt(pentKillTarget) )
9 years ago
{
8 years ago
UTIL_Remove( CBaseEntity::Instance( pentKillTarget ) );
9 years ago
ALERT( at_aiconsole, "killing %s\n", STRING( pentKillTarget->v.classname ) );
8 years ago
pentKillTarget = FIND_ENTITY_BY_TARGETNAME( pentKillTarget, STRING( m_iszKillTarget ) );
9 years ago
}
}
8 years ago
9 years ago
//
// fire targets
//
8 years ago
if( !FStringNull( pev->target ) )
9 years ago
{
8 years ago
FireTargets( STRING( pev->target ), pActivator, this, useType, value );
9 years ago
}
}
/*
8 years ago
void CBaseDelay::SUB_UseTargetsEntMethod( void )
9 years ago
{
8 years ago
SUB_UseTargets( pev );
9 years ago
}
*/
/*
QuakeEd only writes a single float for angles (bad idea), so up and down are
just constant angles.
*/
void SetMovedir( entvars_t *pev )
{
8 years ago
if( pev->angles == Vector( 0, -1, 0 ) )
9 years ago
{
8 years ago
pev->movedir = Vector( 0, 0, 1 );
9 years ago
}
8 years ago
else if (pev->angles == Vector( 0, -2, 0 ) )
9 years ago
{
8 years ago
pev->movedir = Vector( 0, 0, -1 );
9 years ago
}
else
{
8 years ago
UTIL_MakeVectors( pev->angles );
9 years ago
pev->movedir = gpGlobals->v_forward;
}
8 years ago
9 years ago
pev->angles = g_vecZero;
}
void CBaseDelay::DelayThink( void )
{
CBaseEntity *pActivator = NULL;
8 years ago
if( pev->owner != NULL ) // A player activated this on delay
9 years ago
{
pActivator = CBaseEntity::Instance( pev->owner );
}
9 years ago
// The use type is cached (and stashed) in pev->button
SUB_UseTargets( pActivator, (USE_TYPE)pev->button, 0 );
8 years ago
REMOVE_ENTITY( ENT( pev ) );
9 years ago
}
// Global Savedata for Toggle
TYPEDESCRIPTION CBaseToggle::m_SaveData[] =
9 years ago
{
DEFINE_FIELD( CBaseToggle, m_toggle_state, FIELD_INTEGER ),
DEFINE_FIELD( CBaseToggle, m_flActivateFinished, FIELD_TIME ),
DEFINE_FIELD( CBaseToggle, m_flMoveDistance, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_flWait, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_flLip, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_flTWidth, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_flTLength, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_vecPosition1, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CBaseToggle, m_vecPosition2, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CBaseToggle, m_vecAngle1, FIELD_VECTOR ), // UNDONE: Position could go through transition, but also angle?
DEFINE_FIELD( CBaseToggle, m_vecAngle2, FIELD_VECTOR ), // UNDONE: Position could go through transition, but also angle?
DEFINE_FIELD( CBaseToggle, m_cTriggersLeft, FIELD_INTEGER ),
DEFINE_FIELD( CBaseToggle, m_flHeight, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_hActivator, FIELD_EHANDLE ),
DEFINE_FIELD( CBaseToggle, m_pfnCallWhenMoveDone, FIELD_FUNCTION ),
DEFINE_FIELD( CBaseToggle, m_vecFinalDest, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CBaseToggle, m_vecFinalAngle, FIELD_VECTOR ),
DEFINE_FIELD( CBaseToggle, m_sMaster, FIELD_STRING),
DEFINE_FIELD( CBaseToggle, m_bitsDamageInflict, FIELD_INTEGER ), // damage type inflicted
};
IMPLEMENT_SAVERESTORE( CBaseToggle, CBaseAnimating )
9 years ago
void CBaseToggle::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq(pkvd->szKeyName, "lip" ) )
9 years ago
{
8 years ago
m_flLip = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "wait" ) )
9 years ago
{
8 years ago
m_flWait = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "master" ) )
9 years ago
{
8 years ago
m_sMaster = ALLOC_STRING( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "distance" ) )
9 years ago
{
8 years ago
m_flMoveDistance = atof( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
else
CBaseDelay::KeyValue( pkvd );
}
/*
=============
LinearMove
calculate pev->velocity and pev->nextthink to reach vecDest from
pev->origin traveling at flSpeed
===============
*/
8 years ago
void CBaseToggle::LinearMove( Vector vecDest, float flSpeed )
9 years ago
{
8 years ago
ASSERTSZ( flSpeed != 0, "LinearMove: no speed is defined!" );
//ASSERTSZ( m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined" );
9 years ago
m_vecFinalDest = vecDest;
// Already there?
8 years ago
if( vecDest == pev->origin )
9 years ago
{
LinearMoveDone();
return;
}
8 years ago
9 years ago
// set destdelta to the vector needed to move
Vector vecDestDelta = vecDest - pev->origin;
9 years ago
// divide vector length by speed to get time to reach dest
float flTravelTime = vecDestDelta.Length() / flSpeed;
// set nextthink to trigger a call to LinearMoveDone when dest is reached
pev->nextthink = pev->ltime + flTravelTime;
SetThink( &CBaseToggle::LinearMoveDone );
// scale the destdelta vector by the time spent traveling to get velocity
pev->velocity = vecDestDelta / flTravelTime;
}
/*
============
After moving, set origin to exact final destination, call "move done" function
============
*/
8 years ago
void CBaseToggle::LinearMoveDone( void )
9 years ago
{
Vector delta = m_vecFinalDest - pev->origin;
float error = delta.Length();
if( error > 0.03125 )
{
LinearMove( m_vecFinalDest, 100 );
return;
}
8 years ago
UTIL_SetOrigin( pev, m_vecFinalDest );
9 years ago
pev->velocity = g_vecZero;
pev->nextthink = -1;
8 years ago
if( m_pfnCallWhenMoveDone )
( this->*m_pfnCallWhenMoveDone )();
9 years ago
}
8 years ago
BOOL CBaseToggle::IsLockedByMaster( void )
9 years ago
{
8 years ago
if( m_sMaster && !UTIL_IsMasterTriggered( m_sMaster, m_hActivator ) )
9 years ago
return TRUE;
else
return FALSE;
}
/*
=============
AngularMove
calculate pev->velocity and pev->nextthink to reach vecDest from
pev->origin traveling at flSpeed
Just like LinearMove, but rotational.
===============
*/
8 years ago
void CBaseToggle::AngularMove( Vector vecDestAngle, float flSpeed )
9 years ago
{
8 years ago
ASSERTSZ( flSpeed != 0, "AngularMove: no speed is defined!" );
//ASSERTSZ( m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined" );
9 years ago
m_vecFinalAngle = vecDestAngle;
// Already there?
8 years ago
if( vecDestAngle == pev->angles )
9 years ago
{
AngularMoveDone();
return;
}
9 years ago
// set destdelta to the vector needed to move
Vector vecDestDelta = vecDestAngle - pev->angles;
9 years ago
// divide by speed to get time to reach dest
float flTravelTime = vecDestDelta.Length() / flSpeed;
// set nextthink to trigger a call to AngularMoveDone when dest is reached
pev->nextthink = pev->ltime + flTravelTime;
SetThink( &CBaseToggle::AngularMoveDone );
// scale the destdelta vector by the time spent traveling to get velocity
pev->avelocity = vecDestDelta / flTravelTime;
}
/*
============
After rotating, set angle to exact final angle, call "move done" function
============
*/
8 years ago
void CBaseToggle::AngularMoveDone( void )
9 years ago
{
pev->angles = m_vecFinalAngle;
pev->avelocity = g_vecZero;
pev->nextthink = -1;
8 years ago
if( m_pfnCallWhenMoveDone )
( this->*m_pfnCallWhenMoveDone )();
9 years ago
}
8 years ago
float CBaseToggle::AxisValue( int flags, const Vector &angles )
9 years ago
{
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_Z ) )
9 years ago
return angles.z;
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_X ) )
9 years ago
return angles.x;
return angles.y;
}
8 years ago
void CBaseToggle::AxisDir( entvars_t *pev )
9 years ago
{
8 years ago
if( FBitSet( pev->spawnflags, SF_DOOR_ROTATE_Z ) )
pev->movedir = Vector( 0, 0, 1 ); // around z-axis
else if( FBitSet( pev->spawnflags, SF_DOOR_ROTATE_X ) )
pev->movedir = Vector( 1, 0, 0 ); // around x-axis
9 years ago
else
8 years ago
pev->movedir = Vector( 0, 1, 0 ); // around y-axis
9 years ago
}
8 years ago
float CBaseToggle::AxisDelta( int flags, const Vector &angle1, const Vector &angle2 )
9 years ago
{
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_Z ) )
9 years ago
return angle1.z - angle2.z;
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_X ) )
9 years ago
return angle1.x - angle2.x;
return angle1.y - angle2.y;
}
/*
=============
FEntIsVisible
returns TRUE if the passed entity is visible to caller, even if not infront ()
=============
*/
8 years ago
BOOL FEntIsVisible( entvars_t *pev, entvars_t *pevTarget)
{
9 years ago
Vector vecSpot1 = pev->origin + pev->view_ofs;
Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs;
TraceResult tr;
8 years ago
UTIL_TraceLine( vecSpot1, vecSpot2, ignore_monsters, ENT( pev ), &tr );
8 years ago
if( tr.fInOpen && tr.fInWater )
9 years ago
return FALSE; // sight line crossed contents
8 years ago
if( tr.flFraction == 1 )
9 years ago
return TRUE;
return FALSE;
}