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.

920 lines
24 KiB

7 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"
#include "movewith.h"
#include "player.h"
extern CGraph WorldGraph;
8 years ago
extern BOOL FEntIsVisible( entvars_t *pev, entvars_t *pevTarget );
7 years ago
extern DLL_GLOBAL int g_iSkillLevel;
// Landmark class
8 years ago
void CPointEntity::Spawn( void )
7 years ago
{
pev->solid = SOLID_NOT;
8 years ago
//UTIL_SetSize( pev, g_vecZero, g_vecZero );
7 years ago
}
class CNullEntity : public CBaseEntity
{
public:
void Spawn( void );
};
// Null Entity, remove on startup
8 years ago
void CNullEntity::Spawn( void )
7 years ago
{
8 years ago
REMOVE_ENTITY( ENT( pev ) );
7 years ago
}
LINK_ENTITY_TO_CLASS(info_null,CNullEntity);
LINK_ENTITY_TO_CLASS(info_texlights,CNullEntity); // don't complain about Merl's new info entities
LINK_ENTITY_TO_CLASS(info_compile_parameters,CNullEntity);
class CBaseDMStart : public CPointEntity
{
public:
8 years ago
void KeyValue( KeyValueData *pkvd );
7 years ago
STATE GetState( CBaseEntity *pEntity );
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 )
7 years ago
void CBaseDMStart::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "master" ) )
7 years ago
{
8 years ago
pev->netname = ALLOC_STRING( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
STATE CBaseDMStart::GetState( CBaseEntity *pEntity )
{
if (UTIL_IsMasterTriggered( pev->netname, pEntity ))
return STATE_ON;
else
return STATE_OFF;
}
// This updates global tables that need to know about entities being removed
void CBaseEntity::UpdateOnRemove( void )
{
8 years ago
int i;
7 years ago
CBaseEntity* pTemp;
if (!g_pWorld)
{
ALERT(at_console, "UpdateOnRemove has no AssistList!\n");
7 years ago
return;
}
//LRC - remove this from the AssistList.
for (pTemp = g_pWorld; pTemp->m_pAssistLink != NULL; pTemp = pTemp->m_pAssistLink)
{
if (this == pTemp->m_pAssistLink)
{
// ALERT(at_console,"REMOVE: %s removed from the Assist List.\n", STRING(pev->classname));
pTemp->m_pAssistLink = this->m_pAssistLink;
this->m_pAssistLink = NULL;
break;
}
}
//LRC
if (m_pMoveWith)
{
// if I'm moving with another entity, take me out of the list. (otherwise things crash!)
pTemp = m_pMoveWith->m_pChildMoveWith;
if (pTemp == this)
{
m_pMoveWith->m_pChildMoveWith = this->m_pSiblingMoveWith;
}
else
{
while (pTemp->m_pSiblingMoveWith)
{
if (pTemp->m_pSiblingMoveWith == this)
{
pTemp->m_pSiblingMoveWith = this->m_pSiblingMoveWith;
break;
}
pTemp = pTemp->m_pSiblingMoveWith;
}
}
// ALERT(at_console,"REMOVE: %s removed from the %s ChildMoveWith list.\n", STRING(pev->classname), STRING(m_pMoveWith->pev->targetname));
}
//LRC - do the same thing if another entity is moving with _me_.
if (m_pChildMoveWith)
{
CBaseEntity* pCur = m_pChildMoveWith;
CBaseEntity* pNext;
while (pCur != NULL)
{
pNext = pCur->m_pSiblingMoveWith;
// bring children to a stop
UTIL_SetMoveWithVelocity(pCur, g_vecZero, 100);
UTIL_SetMoveWithAvelocity(pCur, g_vecZero, 100);
pCur->m_pMoveWith = NULL;
pCur->m_pSiblingMoveWith = NULL;
pCur = pNext;
}
}
8 years ago
if( FBitSet( pev->flags, FL_GRAPHED ) )
7 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++ )
7 years ago
{
8 years ago
if( WorldGraph.m_pLinkPool[i].m_pLinkEnt == pev )
7 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;
7 years ago
}
}
}
8 years ago
if( pev->globalname )
7 years ago
gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD );
}
// Convenient way to delay removing oneself
8 years ago
void CBaseEntity::SUB_Remove( void )
7 years ago
{
UpdateOnRemove();
8 years ago
if( pev->health > 0 )
7 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" );
7 years ago
}
8 years ago
REMOVE_ENTITY( ENT( pev ) );
7 years ago
}
// Convenient way to explicitly do nothing (passed to functions that require a method)
8 years ago
void CBaseEntity::SUB_DoNothing( void )
7 years ago
{
// if (pev->ltime)
// ALERT(at_console, "Doing Nothing %f\n", pev->ltime);
// else
// ALERT(at_console, "Doing Nothing %f\n", gpGlobals->time);
}
// Global Savedata for Delay
TYPEDESCRIPTION CBaseDelay::m_SaveData[] =
7 years ago
{
DEFINE_FIELD( CBaseDelay, m_flDelay, FIELD_FLOAT ),
DEFINE_FIELD( CBaseDelay, m_iszKillTarget, FIELD_STRING ),
DEFINE_FIELD( CBaseDelay, m_hActivator, FIELD_EHANDLE ), //LRC
};
IMPLEMENT_SAVERESTORE( CBaseDelay, CBaseEntity )
7 years ago
8 years ago
void CBaseDelay::KeyValue( KeyValueData *pkvd )
7 years ago
{
8 years ago
if( FStrEq( pkvd->szKeyName, "delay" ) )
7 years ago
{
m_flDelay = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "killtarget" ) )
7 years ago
{
8 years ago
m_iszKillTarget = ALLOC_STRING( pkvd->szValue );
7 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 )
7 years ago
{
//
// fire targets
//
8 years ago
if( !FStringNull( pev->target ) )
7 years ago
{
8 years ago
FireTargets( STRING( pev->target ), pActivator, this, useType, value );
7 years ago
}
}
void FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
const char *inputTargetName = targetName;
CBaseEntity *inputActivator = pActivator;
CBaseEntity *pTarget = NULL;
int i,j, found = false;
char szBuf[80];
8 years ago
if( !targetName )
7 years ago
return;
if (useType == USE_NOT)
return;
//LRC - allow changing of usetype
if (targetName[0] == '+')
{
targetName++;
useType = USE_ON;
}
else if (targetName[0] == '-')
{
targetName++;
useType = USE_OFF;
}
ALERT( at_aiconsole, "Firing: (%s)\n", targetName );
pTarget = UTIL_FindEntityByTargetname(pTarget, targetName, pActivator);
if( !pTarget )
{
// it's not an entity name; check for a locus specifier, e.g: "fadein(mywall)"
for (i = 0; targetName[i]; i++)
{
if (targetName[i] == '(')
{
i++;
for (j = i; targetName[j]; j++)
{
if (targetName[j] == ')')
{
strncpy(szBuf, targetName+i, j-i);
szBuf[j-i] = 0;
pActivator = UTIL_FindEntityByTargetname(NULL, szBuf, inputActivator);
if (!pActivator)
9 years ago
{
7 years ago
//ALERT(at_console, "Missing activator \"%s\"\n", szBuf);
return; // it's a locus specifier, but the locus is invalid.
}
//ALERT(at_console, "Found activator \"%s\"\n", STRING(pActivator->pev->targetname));
found = true;
break;
}
}
if (!found) ALERT(at_error, "Missing ')' in target value \"%s\"", inputTargetName);
9 years ago
break;
7 years ago
}
}
if (!found) return; // no, it's not a locus specifier.
strncpy(szBuf, targetName, i-1);
szBuf[i-1] = 0;
targetName = szBuf;
pTarget = UTIL_FindEntityByTargetname(NULL, targetName, inputActivator);
if (!pTarget) return; // it's a locus specifier all right, but the target's invalid.
}
do // start firing targets
{
if ( !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents
{
if (useType == USE_KILL)
{
ALERT( at_aiconsole, "Use_kill on %s\n", STRING( pTarget->pev->classname ) );
UTIL_Remove( pTarget );
}
else
{
8 years ago
ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING( pTarget->pev->classname ), targetName );
7 years ago
pTarget->Use( pActivator, pCaller, useType, value );
}
}
pTarget = UTIL_FindEntityByTargetname(pTarget, targetName, inputActivator);
} while (pTarget);
//LRC- Firing has finished, aliases can now reflect their new values.
UTIL_FlushAliases();
}
LINK_ENTITY_TO_CLASS( DelayedUse, CBaseDelay )
7 years ago
8 years ago
void CBaseDelay::SUB_UseTargets( CBaseEntity *pActivator, USE_TYPE useType, float value )
7 years ago
{
//
// exit immediatly if we don't have a target or kill target
//
8 years ago
if( FStringNull( pev->target ) && !m_iszKillTarget )
7 years ago
return;
//
// check for a delay
//
8 years ago
if( m_flDelay != 0 )
7 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" );
7 years ago
pTemp->SetNextThink( m_flDelay );
9 years ago
pTemp->SetThink( &CBaseDelay::DelayThink );
7 years ago
// Save the useType
pTemp->pev->button = (int)useType;
pTemp->m_iszKillTarget = m_iszKillTarget;
pTemp->m_flDelay = 0.0f; // prevent "recursion"
7 years ago
pTemp->pev->target = pev->target;
//LRC - Valve had a hacked thing here to avoid breaking
// save/restore. In Spirit that's not a problem.
// I've moved m_hActivator into this class, for the "elegant" fix.
pTemp->m_hActivator = pActivator;
return;
}
//
// kill the killtargets
//
8 years ago
if( m_iszKillTarget )
7 years ago
{
edict_t *pentKillTarget = NULL;
8 years ago
ALERT( at_aiconsole, "KillTarget: %s\n", STRING( m_iszKillTarget ) );
7 years ago
//LRC- now just USE_KILLs its killtarget, for consistency.
FireTargets( STRING(m_iszKillTarget), pActivator, this, USE_KILL, 0);
}
8 years ago
7 years ago
//
// fire targets
//
8 years ago
if( !FStringNull( pev->target ) )
7 years ago
{
8 years ago
FireTargets( STRING( pev->target ), pActivator, this, useType, value );
7 years ago
}
}
/*
8 years ago
void CBaseDelay::SUB_UseTargetsEntMethod( void )
7 years ago
{
8 years ago
SUB_UseTargets( pev );
7 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 )
{
pev->movedir = GetMovedir(pev->angles);
pev->angles = g_vecZero;
}
Vector GetMovedir( Vector vecAngles )
{
if (vecAngles == Vector(0, -1, 0))
{
return Vector(0, 0, 1);
}
else if (vecAngles == Vector(0, -2, 0))
{
return Vector(0, 0, -1);
}
else
{
UTIL_MakeVectors(vecAngles);
return gpGlobals->v_forward;
}
9 years ago
}
7 years ago
void CBaseDelay::DelayThink( void )
{
CBaseEntity *pActivator = NULL;
// The use type is cached (and stashed) in pev->button
//LRC - now using m_hActivator.
SUB_UseTargets( m_hActivator, (USE_TYPE)pev->button, 0 );
8 years ago
REMOVE_ENTITY( ENT( pev ) );
7 years ago
}
// Global Savedata for Toggle
TYPEDESCRIPTION CBaseToggle::m_SaveData[] =
7 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_flLinearMoveSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CBaseToggle, m_flAngularMoveSpeed, FIELD_FLOAT ), //LRC
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 )
7 years ago
void CBaseToggle::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq(pkvd->szKeyName, "lip" ) )
7 years ago
{
8 years ago
m_flLip = atof( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "wait" ) )
7 years ago
{
8 years ago
m_flWait = atof( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "master" ) )
7 years ago
{
8 years ago
m_sMaster = ALLOC_STRING( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "distance" ) )
7 years ago
{
8 years ago
m_flMoveDistance = atof( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
else
CBaseDelay::KeyValue( pkvd );
}
/*
//void CBaseToggle :: LinearMove( Vector vecInput, float flSpeed)
//{
// LinearMove(vecInput, flSpeed);
//}
=============
LinearMove
calculate pev->velocity and pev->nextthink to reach vecDest from
pev->origin traveling at flSpeed
===============
*/
void CBaseToggle :: LinearMove( Vector vecInput, float flSpeed )//, BOOL bNow )
{
8 years ago
ASSERTSZ( flSpeed != 0, "LinearMove: no speed is defined!" );
//ASSERTSZ( m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined" );
7 years ago
m_flLinearMoveSpeed = flSpeed;
m_vecFinalDest = vecInput;
// if ((m_pMoveWith || m_pChildMoveWith))// && !bNow)
// {
// ALERT(at_console,"Setting LinearMoveNow to happen after %f\n",gpGlobals->time);
SetThink(&CBaseToggle :: LinearMoveNow );
UTIL_DesiredThink( this );
//pev->nextthink = pev->ltime + 0.01;
// }
// else
// {
// LinearMoveNow(); // starring Martin Sheen and Marlon Brando
// }
}
void CBaseToggle :: LinearMoveNow( void )
{
// ALERT(at_console, "LMNow %s\n", STRING(pev->targetname));
Vector vecDest;
// if (m_pMoveWith || m_pChildMoveWith )
// ALERT(at_console,"THINK: LinearMoveNow happens at %f, speed %f\n",gpGlobals->time, m_flLinearMoveSpeed);
if (m_pMoveWith)
{
vecDest = m_vecFinalDest + m_pMoveWith->pev->origin;
}
else
vecDest = m_vecFinalDest;
// ALERT(at_console,"LinearMoveNow: Destination is (%f %f %f), finalDest was (%f %f %f)\n",
// vecDest.x,vecDest.y,vecDest.z,
// m_vecFinalDest.x,m_vecFinalDest.y,m_vecFinalDest.z
// );
// Already there?
8 years ago
if( vecDest == pev->origin )
7 years ago
{
LinearMoveDone();
return;
}
8 years ago
7 years ago
// set destdelta to the vector needed to move
Vector vecDestDelta = vecDest - pev->origin;
7 years ago
// divide vector length by speed to get time to reach dest
float flTravelTime = vecDestDelta.Length() / m_flLinearMoveSpeed;
if( flTravelTime < 0.05f )
{
UTIL_SetOrigin( this, m_vecFinalDest );
LinearMoveDone();
return;
}
7 years ago
// set nextthink to trigger a call to LinearMoveDone when dest is reached
SetNextThink( flTravelTime, TRUE );
9 years ago
SetThink( &CBaseToggle::LinearMoveDone );
7 years ago
// scale the destdelta vector by the time spent traveling to get velocity
// pev->velocity = vecDestDelta / flTravelTime;
UTIL_SetVelocity( this, vecDestDelta / flTravelTime );
// ALERT(at_console, "LMNow \"%s\": Vel %f %f %f, think %f\n", STRING(pev->targetname), pev->velocity.x, pev->velocity.y, pev->velocity.z, pev->nextthink);
}
/*
============
After moving, set origin to exact final destination, call "move done" function
============
*/
/*void CBaseToggle :: LinearMoveDone( void )
{
Vector vecDiff;
if (m_pMoveWith)
vecDiff = (m_vecFinalDest + m_pMoveWith->pev->origin) - pev->origin;
else
vecDiff = m_vecFinalDest - pev->origin;
if( vecDiff.Length() > 0.05f ) //pev->velocity.Length())
7 years ago
{
// HACK: not there yet, try waiting one more frame.
ALERT(at_console,"Rejecting difference %f\n",vecDiff.Length());
SetThink(&CBaseToggle ::LinearMoveFinalDone);
pev->nextthink = gpGlobals->time + 0.01;
}
else
{
LinearMoveFinalDone();
}
}*/
void CBaseToggle::LinearMoveDone( void )
7 years ago
{
SetThink(&CBaseToggle ::LinearMoveDoneNow);
// ALERT(at_console, "LMD: desiredThink %s\n", STRING(pev->targetname));
UTIL_DesiredThink( this );
}
void CBaseToggle :: LinearMoveDoneNow( void )
{
Vector vecDest;
// ALERT(at_console, "LMDone %s\n", STRING(pev->targetname));
UTIL_SetVelocity(this, g_vecZero);//, TRUE);
// pev->velocity = g_vecZero;
if (m_pMoveWith)
{
vecDest = m_vecFinalDest + m_pMoveWith->pev->origin;
// ALERT(at_console, "LMDone %s: p.origin = %f %f %f, origin = %f %f %f. Set it to %f %f %f\n", STRING(pev->targetname), m_pMoveWith->pev->origin.x, m_pMoveWith->pev->origin.y, m_pMoveWith->pev->origin.z, pev->origin.x, pev->origin.y, pev->origin.z, vecDest.x, vecDest.y, vecDest.z);
}
else
{
vecDest = m_vecFinalDest;
// ALERT(at_console, "LMDone %s: origin = %f %f %f. Set it to %f %f %f\n", STRING(pev->targetname), pev->origin.x, pev->origin.y, pev->origin.z, vecDest.x, vecDest.y, vecDest.z);
}
UTIL_AssignOrigin(this, vecDest);
DontThink(); //LRC
//pev->nextthink = -1;
8 years ago
if( m_pfnCallWhenMoveDone )
( this->*m_pfnCallWhenMoveDone )();
7 years ago
}
8 years ago
BOOL CBaseToggle::IsLockedByMaster( void )
7 years ago
{
if (UTIL_IsMasterTriggered(m_sMaster, m_hActivator))
return FALSE;
else
return TRUE;
}
//LRC- mapping toggle-states to global states
STATE CBaseToggle :: GetState ( void )
{
switch (m_toggle_state)
{
case TS_AT_TOP: return STATE_ON;
case TS_AT_BOTTOM: return STATE_OFF;
case TS_GOING_UP: return STATE_TURN_ON;
case TS_GOING_DOWN: return STATE_TURN_OFF;
default: return STATE_OFF; // This should never happen.
}
};
/*
=============
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 )
7 years ago
{
8 years ago
ASSERTSZ( flSpeed != 0, "AngularMove: no speed is defined!" );
//ASSERTSZ( m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined" );
7 years ago
m_vecFinalAngle = vecDestAngle;
m_flAngularMoveSpeed = flSpeed;
// if ((m_pMoveWith || m_pChildMoveWith))// && !bNow)
// {
// ALERT(at_console,"Setting AngularMoveNow to happen after %f\n",gpGlobals->time);
SetThink(&CBaseToggle :: AngularMoveNow );
UTIL_DesiredThink( this );
// ExternalThink( 0.01 );
// pev->nextthink = pev->ltime + 0.01;
// }
// else
// {
// AngularMoveNow(); // starring Martin Sheen and Marlon Brando
// }
}
void CBaseToggle :: AngularMoveNow()
{
// ALERT(at_console, "AngularMoveNow %f\n", pev->ltime);
Vector vecDestAngle;
if (m_pMoveWith)
vecDestAngle = m_vecFinalAngle + m_pMoveWith->pev->angles;
else
vecDestAngle = m_vecFinalAngle;
// Already there?
8 years ago
if( vecDestAngle == pev->angles )
7 years ago
{
AngularMoveDone();
return;
}
7 years ago
// set destdelta to the vector needed to move
Vector vecDestDelta = vecDestAngle - pev->angles;
7 years ago
// divide by speed to get time to reach dest
float flTravelTime = vecDestDelta.Length() / m_flAngularMoveSpeed;
// set nextthink to trigger a call to AngularMoveDone when dest is reached
SetNextThink( flTravelTime, TRUE );
9 years ago
SetThink( &CBaseToggle::AngularMoveDone );
7 years ago
// scale the destdelta vector by the time spent traveling to get velocity
UTIL_SetAvelocity(this, vecDestDelta / flTravelTime );
}
void CBaseToggle :: AngularMoveDone( void )
{
SetThink(&CBaseToggle ::AngularMoveDoneNow);
// ALERT(at_console, "LMD: desiredThink %s\n", STRING(pev->targetname));
UTIL_DesiredThink( this );
}
/*
============
After rotating, set angle to exact final angle, call "move done" function
============
*/
void CBaseToggle :: AngularMoveDoneNow( void )
{
// ALERT(at_console, "AngularMoveDone %f\n", pev->ltime);
UTIL_SetAvelocity(this, g_vecZero);
if (m_pMoveWith)
{
UTIL_SetAngles(this, m_vecFinalAngle + m_pMoveWith->pev->angles);
}
else
{
7 years ago
UTIL_SetAngles(this, m_vecFinalAngle);
}
DontThink();
8 years ago
if( m_pfnCallWhenMoveDone )
( this->*m_pfnCallWhenMoveDone )();
7 years ago
}
// this isn't currently used. Otherwise I'd fix it to use movedir the way it should...
8 years ago
float CBaseToggle::AxisValue( int flags, const Vector &angles )
7 years ago
{
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_Z ) )
7 years ago
return angles.z;
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_X ) )
7 years ago
return angles.x;
return angles.y;
}
8 years ago
void CBaseToggle::AxisDir( entvars_t *pev )
7 years ago
{
if ( pev->movedir != g_vecZero) //LRC
return;
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
7 years ago
else
8 years ago
pev->movedir = Vector( 0, 1, 0 ); // around y-axis
7 years ago
}
8 years ago
float CBaseToggle::AxisDelta( int flags, const Vector &angle1, const Vector &angle2 )
7 years ago
{
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_Z ) )
7 years ago
return angle1.z - angle2.z;
8 years ago
if( FBitSet( flags, SF_DOOR_ROTATE_X ) )
7 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)
{
7 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 )
7 years ago
return FALSE; // sight line crossed contents
8 years ago
if( tr.flFraction == 1 )
7 years ago
return TRUE;
return FALSE;
}
//=========================================================
// LRC - info_movewith, the first entity I've made which
// truly doesn't fit ANY preexisting category.
//=========================================================
#define SF_IMW_INACTIVE 1
#define SF_IMW_BLOCKABLE 2
class CInfoMoveWith : public CBaseEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
STATE GetState() { return (pev->spawnflags & SF_IMW_INACTIVE)?STATE_OFF:STATE_ON; }
};
LINK_ENTITY_TO_CLASS(info_movewith, CInfoMoveWith);
void CInfoMoveWith :: Spawn( void )
{
if (pev->spawnflags & SF_IMW_INACTIVE)
m_MoveWith = pev->netname;
else
m_MoveWith = pev->target;
if (pev->spawnflags & SF_IMW_BLOCKABLE)
{
pev->solid = SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
}
// and allow InitMoveWith to set things up as usual.
}
void CInfoMoveWith :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
CBaseEntity *pSibling;
if (!ShouldToggle(useType)) return;
if (m_pMoveWith)
{
// remove this from the old parent's list of children
pSibling = m_pMoveWith->m_pChildMoveWith;
if (pSibling == this)
m_pMoveWith->m_pChildMoveWith = this->m_pSiblingMoveWith;
else
{
while (pSibling->m_pSiblingMoveWith && pSibling->m_pSiblingMoveWith != this)
{ pSibling = pSibling->m_pSiblingMoveWith; }
if (pSibling->m_pSiblingMoveWith == this)
{
pSibling->m_pSiblingMoveWith = this->m_pSiblingMoveWith;
}
else
{
// failed to find myself in the list, complain
ALERT(at_error, "info_movewith can't find itself\n");
return;
}
}
m_pMoveWith = NULL;
m_pSiblingMoveWith = NULL;
}
if (pev->spawnflags & SF_IMW_INACTIVE)
{
pev->spawnflags &= ~SF_IMW_INACTIVE;
m_MoveWith = pev->target;
}
else
{
pev->spawnflags |= SF_IMW_INACTIVE;
m_MoveWith = pev->netname;
}
// set things up for the new m_MoveWith value
if (!m_MoveWith)
{
UTIL_SetVelocity(this, g_vecZero); // come to a stop
return;
}
m_pMoveWith = UTIL_FindEntityByTargetname(NULL, STRING(m_MoveWith));
if (!m_pMoveWith)
{
ALERT(at_console,"Missing movewith entity %s\n", STRING(m_MoveWith));
7 years ago
return;
}
pSibling = m_pMoveWith->m_pChildMoveWith;
while (pSibling) // check that this entity isn't already in the list of children
{
if (pSibling == this) return;
pSibling = pSibling->m_pSiblingMoveWith;
}
// add this entity to the list of children
m_pSiblingMoveWith = m_pMoveWith->m_pChildMoveWith; // may be null: that's fine by me.
m_pMoveWith->m_pChildMoveWith = this;
m_vecMoveWithOffset = pev->origin - m_pMoveWith->pev->origin;
UTIL_SetVelocity(this, g_vecZero); // match speed with the new entity
}