/***
*
* 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 .
*
* * * */
/*
= = = = = triggers . cpp = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
spawn and use functions for editor - placed triggers
*/
# include "extdll.h"
# include "util.h"
# include "cbase.h"
# include "player.h"
# include "saverestore.h"
# include "trains.h" // trigger_camera has train functionality
# include "gamerules.h"
# include "game.h"
# define SF_TRIGGER_PUSH_START_OFF 2 //spawnflag that makes trigger_push spawn turned OFF
# define SF_TRIGGER_HURT_TARGETONCE 1 // Only fire hurt target once
# define SF_TRIGGER_HURT_START_OFF 2 //spawnflag that makes trigger_push spawn turned OFF
# define SF_TRIGGER_HURT_NO_CLIENTS 8 //spawnflag that makes trigger_push spawn turned OFF
# define SF_TRIGGER_HURT_CLIENTONLYFIRE 16 // trigger hurt will only fire its target if it is hurting a client
# define SF_TRIGGER_HURT_CLIENTONLYTOUCH 32 // only clients may touch this trigger.
extern DLL_GLOBAL BOOL g_fGameOver ;
extern void SetMovedir ( entvars_t * pev ) ;
extern Vector VecBModelOrigin ( entvars_t * pevBModel ) ;
class CFrictionModifier : public CBaseEntity
{
public :
void Spawn ( void ) ;
void KeyValue ( KeyValueData * pkvd ) ;
void EXPORT ChangeFriction ( CBaseEntity * pOther ) ;
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
virtual int ObjectCaps ( void ) { return CBaseEntity : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
static TYPEDESCRIPTION m_SaveData [ ] ;
float m_frictionFraction ; // Sorry, couldn't resist this name :)
} ;
LINK_ENTITY_TO_CLASS ( func_friction , CFrictionModifier )
// Global Savedata for changelevel friction modifier
TYPEDESCRIPTION CFrictionModifier : : m_SaveData [ ] =
{
DEFINE_FIELD ( CFrictionModifier , m_frictionFraction , FIELD_FLOAT ) ,
} ;
IMPLEMENT_SAVERESTORE ( CFrictionModifier , CBaseEntity )
// Modify an entity's friction
void CFrictionModifier : : Spawn ( void )
{
pev - > solid = SOLID_TRIGGER ;
SET_MODEL ( ENT ( pev ) , STRING ( pev - > model ) ) ; // set size and link into world
pev - > movetype = MOVETYPE_NONE ;
SetTouch ( & CFrictionModifier : : ChangeFriction ) ;
}
// Sets toucher's friction to m_frictionFraction (1.0 = normal friction)
void CFrictionModifier : : ChangeFriction ( CBaseEntity * pOther )
{
if ( ! mp_coop . value )
{
if ( pOther - > pev - > movetype ! = MOVETYPE_BOUNCEMISSILE & & pOther - > pev - > movetype ! = MOVETYPE_BOUNCE )
pOther - > pev - > friction = m_frictionFraction ;
}
}
// Sets toucher's friction to m_frictionFraction (1.0 = normal friction)
void CFrictionModifier : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " modifier " ) )
{
m_frictionFraction = atof ( pkvd - > szValue ) / 100.0 ;
pkvd - > fHandled = TRUE ;
}
else
CBaseEntity : : KeyValue ( pkvd ) ;
}
// This trigger will fire when the level spawns (or respawns if not fire once)
// It will check a global state before firing. It supports delay and killtargets
# define SF_AUTO_FIREONCE 0x0001
class CAutoTrigger : public CBaseDelay
{
public :
void KeyValue ( KeyValueData * pkvd ) ;
void Spawn ( void ) ;
void Precache ( void ) ;
void Think ( void ) ;
int ObjectCaps ( void ) { return CBaseDelay : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
private :
int m_globalstate ;
USE_TYPE triggerType ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_auto , CAutoTrigger )
TYPEDESCRIPTION CAutoTrigger : : m_SaveData [ ] =
{
DEFINE_FIELD ( CAutoTrigger , m_globalstate , FIELD_STRING ) ,
DEFINE_FIELD ( CAutoTrigger , triggerType , FIELD_INTEGER ) ,
} ;
IMPLEMENT_SAVERESTORE ( CAutoTrigger , CBaseDelay )
void CAutoTrigger : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " globalstate " ) )
{
m_globalstate = ALLOC_STRING ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " triggerstate " ) )
{
int type = atoi ( pkvd - > szValue ) ;
switch ( type )
{
case 0 :
triggerType = USE_OFF ;
break ;
case 2 :
triggerType = USE_TOGGLE ;
break ;
default :
triggerType = USE_ON ;
break ;
}
pkvd - > fHandled = TRUE ;
}
else
CBaseDelay : : KeyValue ( pkvd ) ;
}
void CAutoTrigger : : Spawn ( void )
{
Precache ( ) ;
}
void CAutoTrigger : : Precache ( void )
{
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
void CAutoTrigger : : Think ( void )
{
if ( ! m_globalstate | | gGlobalState . EntityGetState ( m_globalstate ) = = GLOBAL_ON )
{
SUB_UseTargets ( this , triggerType , 0 ) ;
if ( pev - > spawnflags & SF_AUTO_FIREONCE )
UTIL_Remove ( this ) ;
}
}
# define SF_RELAY_FIREONCE 0x0001
class CTriggerRelay : public CBaseDelay
{
public :
void KeyValue ( KeyValueData * pkvd ) ;
void Spawn ( void ) ;
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
int ObjectCaps ( void ) { return CBaseDelay : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
private :
USE_TYPE triggerType ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_relay , CTriggerRelay )
TYPEDESCRIPTION CTriggerRelay : : m_SaveData [ ] =
{
DEFINE_FIELD ( CTriggerRelay , triggerType , FIELD_INTEGER ) ,
} ;
IMPLEMENT_SAVERESTORE ( CTriggerRelay , CBaseDelay )
void CTriggerRelay : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " triggerstate " ) )
{
int type = atoi ( pkvd - > szValue ) ;
switch ( type )
{
case 0 :
triggerType = USE_OFF ;
break ;
case 2 :
triggerType = USE_TOGGLE ;
break ;
default :
triggerType = USE_ON ;
break ;
}
pkvd - > fHandled = TRUE ;
}
else
CBaseDelay : : KeyValue ( pkvd ) ;
}
void CTriggerRelay : : Spawn ( void )
{
}
void CTriggerRelay : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
SUB_UseTargets ( this , triggerType , 0 ) ;
if ( pev - > spawnflags & SF_RELAY_FIREONCE )
UTIL_Remove ( this ) ;
}
//**********************************************************
// The Multimanager Entity - when fired, will fire up to 16 targets
// at specified times.
// FLAG: THREAD (create clones when triggered)
// FLAG: CLONE (this is a clone for a threaded execution)
# define SF_MULTIMAN_CLONE 0x80000000
# define SF_MULTIMAN_THREAD 0x00000001
class CMultiManager : public CBaseToggle
{
public :
void KeyValue ( KeyValueData * pkvd ) ;
void Spawn ( void ) ;
void EXPORT ManagerThink ( void ) ;
void EXPORT ManagerUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
# if _DEBUG
void EXPORT ManagerReport ( void ) ;
# endif
BOOL HasTarget ( string_t targetname ) ;
int ObjectCaps ( void ) { return CBaseToggle : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
int m_cTargets ; // the total number of targets in this manager's fire list.
int m_index ; // Current target
float m_startTime ; // Time we started firing
int m_iTargetName [ MAX_MULTI_TARGETS ] ; // list if indexes into global string array
float m_flTargetDelay [ MAX_MULTI_TARGETS ] ; // delay (in seconds) from time of manager fire to target fire
private :
inline BOOL IsClone ( void ) { return ( pev - > spawnflags & SF_MULTIMAN_CLONE ) ? TRUE : FALSE ; }
inline BOOL ShouldClone ( void )
{
if ( IsClone ( ) )
return FALSE ;
return ( pev - > spawnflags & SF_MULTIMAN_THREAD ) ? TRUE : FALSE ;
}
CMultiManager * Clone ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( multi_manager , CMultiManager )
// Global Savedata for multi_manager
TYPEDESCRIPTION CMultiManager : : m_SaveData [ ] =
{
DEFINE_FIELD ( CMultiManager , m_cTargets , FIELD_INTEGER ) ,
DEFINE_FIELD ( CMultiManager , m_index , FIELD_INTEGER ) ,
DEFINE_FIELD ( CMultiManager , m_startTime , FIELD_TIME ) ,
DEFINE_ARRAY ( CMultiManager , m_iTargetName , FIELD_STRING , MAX_MULTI_TARGETS ) ,
DEFINE_ARRAY ( CMultiManager , m_flTargetDelay , FIELD_FLOAT , MAX_MULTI_TARGETS ) ,
} ;
IMPLEMENT_SAVERESTORE ( CMultiManager , CBaseToggle )
void CMultiManager : : KeyValue ( KeyValueData * pkvd )
{
// UNDONE: Maybe this should do something like this:
//CBaseToggle::KeyValue( pkvd );
// if( !pkvd->fHandled )
// ... etc.
if ( FStrEq ( pkvd - > szKeyName , " wait " ) )
{
m_flWait = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else // add this field to the target list
{
// this assumes that additional fields are targetnames and their values are delay values.
if ( m_cTargets < MAX_MULTI_TARGETS )
{
char tmp [ 128 ] ;
UTIL_StripToken ( pkvd - > szKeyName , tmp ) ;
m_iTargetName [ m_cTargets ] = ALLOC_STRING ( tmp ) ;
m_flTargetDelay [ m_cTargets ] = atof ( pkvd - > szValue ) ;
m_cTargets + + ;
pkvd - > fHandled = TRUE ;
}
}
}
void CMultiManager : : Spawn ( void )
{
pev - > solid = SOLID_NOT ;
SetUse ( & CMultiManager : : ManagerUse ) ;
SetThink ( & CMultiManager : : ManagerThink ) ;
// Sort targets
// Quick and dirty bubble sort
int swapped = 1 ;
while ( swapped )
{
swapped = 0 ;
for ( int i = 1 ; i < m_cTargets ; i + + )
{
if ( m_flTargetDelay [ i ] < m_flTargetDelay [ i - 1 ] )
{
// Swap out of order elements
int name = m_iTargetName [ i ] ;
float delay = m_flTargetDelay [ i ] ;
m_iTargetName [ i ] = m_iTargetName [ i - 1 ] ;
m_flTargetDelay [ i ] = m_flTargetDelay [ i - 1 ] ;
m_iTargetName [ i - 1 ] = name ;
m_flTargetDelay [ i - 1 ] = delay ;
swapped = 1 ;
}
}
}
}
BOOL CMultiManager : : HasTarget ( string_t targetname )
{
for ( int i = 0 ; i < m_cTargets ; i + + )
if ( FStrEq ( STRING ( targetname ) , STRING ( m_iTargetName [ i ] ) ) )
return TRUE ;
return FALSE ;
}
// Designers were using this to fire targets that may or may not exist --
// so I changed it to use the standard target fire code, made it a little simpler.
void CMultiManager : : ManagerThink ( void )
{
float time ;
time = gpGlobals - > time - m_startTime ;
while ( m_index < m_cTargets & & m_flTargetDelay [ m_index ] < = time )
{
FireTargets ( STRING ( m_iTargetName [ m_index ] ) , m_hActivator , this , USE_TOGGLE , 0 ) ;
m_index + + ;
}
if ( m_index > = m_cTargets ) // have we fired all targets?
{
SetThink ( NULL ) ;
if ( IsClone ( ) )
{
UTIL_Remove ( this ) ;
return ;
}
SetUse ( & CMultiManager : : ManagerUse ) ; // allow manager re-use
}
else
pev - > nextthink = m_startTime + m_flTargetDelay [ m_index ] ;
}
CMultiManager * CMultiManager : : Clone ( void )
{
CMultiManager * pMulti = GetClassPtr ( ( CMultiManager * ) NULL ) ;
edict_t * pEdict = pMulti - > pev - > pContainingEntity ;
memcpy ( pMulti - > pev , pev , sizeof ( * pev ) ) ;
pMulti - > pev - > pContainingEntity = pEdict ;
pMulti - > pev - > spawnflags | = SF_MULTIMAN_CLONE ;
pMulti - > m_cTargets = m_cTargets ;
memcpy ( pMulti - > m_iTargetName , m_iTargetName , sizeof ( m_iTargetName ) ) ;
memcpy ( pMulti - > m_flTargetDelay , m_flTargetDelay , sizeof ( m_flTargetDelay ) ) ;
return pMulti ;
}
// The USE function builds the time table and starts the entity thinking.
void CMultiManager : : ManagerUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
// In multiplayer games, clone the MM and execute in the clone (like a thread)
// to allow multiple players to trigger the same multimanager
if ( ShouldClone ( ) )
{
CMultiManager * pClone = Clone ( ) ;
pClone - > ManagerUse ( pActivator , pCaller , useType , value ) ;
return ;
}
m_hActivator = pActivator ;
m_index = 0 ;
m_startTime = gpGlobals - > time ;
SetUse ( NULL ) ; // disable use until all targets have fired
SetThink ( & CMultiManager : : ManagerThink ) ;
pev - > nextthink = gpGlobals - > time ;
}
# if _DEBUG
void CMultiManager : : ManagerReport ( void )
{
int cIndex ;
for ( cIndex = 0 ; cIndex < m_cTargets ; cIndex + + )
{
ALERT ( at_console , " %s %f \n " , STRING ( m_iTargetName [ cIndex ] ) , m_flTargetDelay [ cIndex ] ) ;
}
}
# endif
//***********************************************************
//
// Render parameters trigger
//
// This entity will copy its render parameters (renderfx, rendermode, rendercolor, renderamt)
// to its targets when triggered.
//
// Flags to indicate masking off various render parameters that are normally copied to the targets
# define SF_RENDER_MASKFX ( 1 << 0 )
# define SF_RENDER_MASKAMT ( 1 << 1 )
# define SF_RENDER_MASKMODE ( 1 << 2 )
# define SF_RENDER_MASKCOLOR ( 1 << 3 )
class CRenderFxManager : public CBaseEntity
{
public :
void Spawn ( void ) ;
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
} ;
LINK_ENTITY_TO_CLASS ( env_render , CRenderFxManager )
void CRenderFxManager : : Spawn ( void )
{
pev - > solid = SOLID_NOT ;
}
void CRenderFxManager : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
if ( ! FStringNull ( pev - > target ) )
{
edict_t * pentTarget = NULL ;
while ( 1 )
{
pentTarget = FIND_ENTITY_BY_TARGETNAME ( pentTarget , STRING ( pev - > target ) ) ;
if ( FNullEnt ( pentTarget ) )
break ;
entvars_t * pevTarget = VARS ( pentTarget ) ;
if ( ! FBitSet ( pev - > spawnflags , SF_RENDER_MASKFX ) )
pevTarget - > renderfx = pev - > renderfx ;
if ( ! FBitSet ( pev - > spawnflags , SF_RENDER_MASKAMT ) )
pevTarget - > renderamt = pev - > renderamt ;
if ( ! FBitSet ( pev - > spawnflags , SF_RENDER_MASKMODE ) )
pevTarget - > rendermode = pev - > rendermode ;
if ( ! FBitSet ( pev - > spawnflags , SF_RENDER_MASKCOLOR ) )
pevTarget - > rendercolor = pev - > rendercolor ;
}
}
}
class CBaseTrigger : public CBaseToggle
{
public :
void EXPORT TeleportTouch ( CBaseEntity * pOther ) ;
void KeyValue ( KeyValueData * pkvd ) ;
void EXPORT MultiTouch ( CBaseEntity * pOther ) ;
void EXPORT HurtTouch ( CBaseEntity * pOther ) ;
void EXPORT CDAudioTouch ( CBaseEntity * pOther ) ;
void ActivateMultiTrigger ( CBaseEntity * pActivator ) ;
void EXPORT MultiWaitOver ( void ) ;
void EXPORT CounterUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void EXPORT ToggleUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void InitTrigger ( void ) ;
virtual int ObjectCaps ( void ) { return CBaseToggle : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
} ;
LINK_ENTITY_TO_CLASS ( trigger , CBaseTrigger )
/*
= = = = = = = = = = = = = = = =
InitTrigger
= = = = = = = = = = = = = = = =
*/
void CBaseTrigger : : InitTrigger ( )
{
// trigger angles are used for one-way touches. An angle of 0 is assumed
// to mean no restrictions, so use a yaw of 360 instead.
if ( pev - > angles ! = g_vecZero )
SetMovedir ( pev ) ;
pev - > solid = SOLID_TRIGGER ;
pev - > movetype = MOVETYPE_NONE ;
SET_MODEL ( ENT ( pev ) , STRING ( pev - > model ) ) ; // set size and link into world
if ( CVAR_GET_FLOAT ( " showtriggers " ) = = 0 )
SetBits ( pev - > effects , EF_NODRAW ) ;
}
//
// Cache user-entity-field values until spawn is called.
//
void CBaseTrigger : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " damage " ) )
{
pev - > dmg = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " count " ) )
{
m_cTriggersLeft = ( int ) atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " damagetype " ) )
{
m_bitsDamageInflict = atoi ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseToggle : : KeyValue ( pkvd ) ;
}
class CTriggerHurt : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void EXPORT RadiationThink ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_hurt , CTriggerHurt )
//
// trigger_monsterjump
//
class CTriggerMonsterJump : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void Touch ( CBaseEntity * pOther ) ;
void Think ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_monsterjump , CTriggerMonsterJump )
void CTriggerMonsterJump : : Spawn ( void )
{
SetMovedir ( pev ) ;
InitTrigger ( ) ;
pev - > nextthink = 0 ;
pev - > speed = 200 ;
m_flHeight = 150 ;
if ( ! FStringNull ( pev - > targetname ) )
{
// if targetted, spawn turned off
pev - > solid = SOLID_NOT ;
UTIL_SetOrigin ( pev , pev - > origin ) ; // Unlink from trigger list
SetUse ( & CBaseTrigger : : ToggleUse ) ;
}
}
void CTriggerMonsterJump : : Think ( void )
{
pev - > solid = SOLID_NOT ; // kill the trigger for now !!!UNDONE
UTIL_SetOrigin ( pev , pev - > origin ) ; // Unlink from trigger list
SetThink ( NULL ) ;
}
void CTriggerMonsterJump : : Touch ( CBaseEntity * pOther )
{
entvars_t * pevOther = pOther - > pev ;
if ( ! FBitSet ( pevOther - > flags , FL_MONSTER ) )
{
// touched by a non-monster.
return ;
}
pevOther - > origin . z + = 1 ;
if ( FBitSet ( pevOther - > flags , FL_ONGROUND ) )
{
// clear the onground so physics don't bitch
pevOther - > flags & = ~ FL_ONGROUND ;
}
// toss the monster!
pevOther - > velocity = pev - > movedir * pev - > speed ;
pevOther - > velocity . z + = m_flHeight ;
pev - > nextthink = gpGlobals - > time ;
}
//=====================================
//
// trigger_cdaudio - starts/stops cd audio tracks
//
class CTriggerCDAudio : public CBaseTrigger
{
public :
void Spawn ( void ) ;
virtual void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void PlayTrack ( void ) ;
void Touch ( CBaseEntity * pOther ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_cdaudio , CTriggerCDAudio )
//
// Changes tracks or stops CD when player touches
//
// !!!HACK - overloaded HEALTH to avoid adding new field
void CTriggerCDAudio : : Touch ( CBaseEntity * pOther )
{
if ( ! pOther - > IsPlayer ( ) )
{
// only clients may trigger these events
return ;
}
PlayTrack ( ) ;
}
void CTriggerCDAudio : : Spawn ( void )
{
InitTrigger ( ) ;
}
void CTriggerCDAudio : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
PlayTrack ( ) ;
}
void PlayCDTrack ( int iTrack )
{
edict_t * pClient ;
// manually find the single player.
pClient = g_engfuncs . pfnPEntityOfEntIndex ( 1 ) ;
// Can't play if the client is not connected!
if ( ! pClient )
return ;
if ( iTrack < - 1 | | iTrack > 30 )
{
ALERT ( at_console , " TriggerCDAudio - Track %d out of range \n " ) ;
return ;
}
if ( iTrack = = - 1 )
{
CLIENT_COMMAND ( pClient , " cd pause \n " ) ;
}
else
{
char string [ 64 ] ;
sprintf ( string , " cd play %3d \n " , iTrack ) ;
CLIENT_COMMAND ( pClient , string ) ;
}
}
// only plays for ONE client, so only use in single play!
void CTriggerCDAudio : : PlayTrack ( void )
{
PlayCDTrack ( ( int ) pev - > health ) ;
SetTouch ( NULL ) ;
UTIL_Remove ( this ) ;
}
// This plays a CD track when fired or when the player enters it's radius
class CTargetCDAudio : public CPointEntity
{
public :
void Spawn ( void ) ;
void KeyValue ( KeyValueData * pkvd ) ;
virtual void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void Think ( void ) ;
void Play ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( target_cdaudio , CTargetCDAudio )
void CTargetCDAudio : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " radius " ) )
{
pev - > scale = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CPointEntity : : KeyValue ( pkvd ) ;
}
void CTargetCDAudio : : Spawn ( void )
{
pev - > solid = SOLID_NOT ;
pev - > movetype = MOVETYPE_NONE ;
if ( pev - > scale > 0 )
pev - > nextthink = gpGlobals - > time + 1.0 ;
}
void CTargetCDAudio : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
Play ( ) ;
}
// only plays for ONE client, so only use in single play!
void CTargetCDAudio : : Think ( void )
{
edict_t * pClient ;
// manually find the single player.
pClient = g_engfuncs . pfnPEntityOfEntIndex ( 1 ) ;
// Can't play if the client is not connected!
if ( ! pClient )
return ;
pev - > nextthink = gpGlobals - > time + 0.5 ;
if ( ( pClient - > v . origin - pev - > origin ) . Length ( ) < = pev - > scale )
Play ( ) ;
}
void CTargetCDAudio : : Play ( void )
{
PlayCDTrack ( ( int ) pev - > health ) ;
UTIL_Remove ( this ) ;
}
//=====================================
//
// trigger_hurt - hurts anything that touches it. if the trigger has a targetname, firing it will toggle state
//
//int gfToggleState = 0; // used to determine when all radiation trigger hurts have called 'RadiationThink'
void CTriggerHurt : : Spawn ( void )
{
InitTrigger ( ) ;
SetTouch ( & CBaseTrigger : : HurtTouch ) ;
if ( ! FStringNull ( pev - > targetname ) )
{
SetUse ( & CBaseTrigger : : ToggleUse ) ;
if ( mp_coop . value )
{
if ( pev - > size . x > 200 & & pev - > size . y > 200 & & pev - > size . z > 200 )
{
//it seems that it covers whole room or map
if ( pev - > dmg > 10 )
// trying to kill everything? Not in coop!
pev - > dmg = 3 ;
}
}
}
else
{
SetUse ( NULL ) ;
}
if ( m_bitsDamageInflict & DMG_RADIATION )
{
SetThink ( & CTriggerHurt : : RadiationThink ) ;
pev - > nextthink = gpGlobals - > time + RANDOM_FLOAT ( 0.0 , 0.5 ) ;
}
if ( FBitSet ( pev - > spawnflags , SF_TRIGGER_HURT_START_OFF ) ) // if flagged to Start Turned Off, make trigger nonsolid.
pev - > solid = SOLID_NOT ;
UTIL_SetOrigin ( pev , pev - > origin ) ; // Link into the list
}
// trigger hurt that causes radiation will do a radius
// check and set the player's geiger counter level
// according to distance from center of trigger
void CTriggerHurt : : RadiationThink ( void )
{
edict_t * pentPlayer ;
CBasePlayer * pPlayer = NULL ;
float flRange ;
entvars_t * pevTarget ;
Vector vecSpot1 ;
Vector vecSpot2 ;
Vector vecRange ;
Vector origin ;
Vector view_ofs ;
// check to see if a player is in pvs
// if not, continue
// set origin to center of trigger so that this check works
origin = pev - > origin ;
view_ofs = pev - > view_ofs ;
pev - > origin = ( pev - > absmin + pev - > absmax ) * 0.5 ;
pev - > view_ofs = pev - > view_ofs * 0.0 ;
pentPlayer = FIND_CLIENT_IN_PVS ( edict ( ) ) ;
pev - > origin = origin ;
pev - > view_ofs = view_ofs ;
// reset origin
if ( ! FNullEnt ( pentPlayer ) )
{
pPlayer = GetClassPtr ( ( CBasePlayer * ) VARS ( pentPlayer ) ) ;
pevTarget = VARS ( pentPlayer ) ;
// get range to player;
vecSpot1 = ( pev - > absmin + pev - > absmax ) * 0.5 ;
vecSpot2 = ( pevTarget - > absmin + pevTarget - > absmax ) * 0.5 ;
vecRange = vecSpot1 - vecSpot2 ;
flRange = vecRange . Length ( ) ;
// if player's current geiger counter range is larger
// than range to this trigger hurt, reset player's
// geiger counter range
if ( pPlayer - > m_flgeigerRange > = flRange )
pPlayer - > m_flgeigerRange = flRange ;
}
pev - > nextthink = gpGlobals - > time + 0.25 ;
}
//
// ToggleUse - If this is the USE function for a trigger, its state will toggle every time it's fired
//
void CBaseTrigger : : ToggleUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
if ( pev - > solid = = SOLID_NOT )
{
// if the trigger is off, turn it on
pev - > solid = SOLID_TRIGGER ;
// Force retouch
gpGlobals - > force_retouch + + ;
}
else
{
// turn the trigger off
pev - > solid = SOLID_NOT ;
}
UTIL_SetOrigin ( pev , pev - > origin ) ;
}
// When touched, a hurt trigger does DMG points of damage each half-second
void CBaseTrigger : : HurtTouch ( CBaseEntity * pOther )
{
float fldmg ;
if ( ! pOther - > pev - > takedamage )
return ;
if ( ( pev - > spawnflags & SF_TRIGGER_HURT_CLIENTONLYTOUCH ) & & ! pOther - > IsPlayer ( ) )
{
// this trigger is only allowed to touch clients, and this ain't a client.
return ;
}
if ( ( pev - > spawnflags & SF_TRIGGER_HURT_NO_CLIENTS ) & & pOther - > IsPlayer ( ) )
return ;
// HACKHACK -- In multiplayer, players touch this based on packet receipt.
// So the players who send packets later aren't always hurt. Keep track of
// how much time has passed and whether or not you've touched that player
if ( ! mp_coop . value & & g_pGameRules - > IsMultiplayer ( ) )
{
if ( pev - > dmgtime > gpGlobals - > time )
{
if ( gpGlobals - > time ! = pev - > pain_finished )
{
// too early to hurt again, and not same frame with a different entity
if ( pOther - > IsPlayer ( ) )
{
int playerMask = 1 < < ( pOther - > entindex ( ) - 1 ) ;
// If I've already touched this player (this time), then bail out
if ( pev - > impulse & playerMask )
return ;
// Mark this player as touched
// BUGBUG - There can be only 32 players!
pev - > impulse | = playerMask ;
}
else
{
return ;
}
}
}
else
{
// New clock, "un-touch" all players
pev - > impulse = 0 ;
if ( pOther - > IsPlayer ( ) )
{
int playerMask = 1 < < ( pOther - > entindex ( ) - 1 ) ;
// Mark this player as touched
// BUGBUG - There can be only 32 players!
pev - > impulse | = playerMask ;
}
}
}
else // Original code -- single player
{
if ( pev - > dmgtime > gpGlobals - > time & & gpGlobals - > time ! = pev - > pain_finished )
{
// too early to hurt again, and not same frame with a different entity
return ;
}
}
// If this is time_based damage (poison, radiation), override the pev->dmg with a
// default for the given damage type. Monsters only take time-based damage
// while touching the trigger. Player continues taking damage for a while after
// leaving the trigger
fldmg = pev - > dmg * 0.5 ; // 0.5 seconds worth of damage, pev->dmg is damage/second
// JAY: Cut this because it wasn't fully realized. Damage is simpler now.
#if 0
switch ( m_bitsDamageInflict )
{
default :
break ;
case DMG_POISON :
fldmg = POISON_DAMAGE / 4 ;
break ;
case DMG_NERVEGAS :
fldmg = NERVEGAS_DAMAGE / 4 ;
break ;
case DMG_RADIATION :
fldmg = RADIATION_DAMAGE / 4 ;
break ;
case DMG_PARALYZE : // UNDONE: cut this? should slow movement to 50%
fldmg = PARALYZE_DAMAGE / 4 ;
break ;
case DMG_ACID :
fldmg = ACID_DAMAGE / 4 ;
break ;
case DMG_SLOWBURN :
fldmg = SLOWBURN_DAMAGE / 4 ;
break ;
case DMG_SLOWFREEZE :
fldmg = SLOWFREEZE_DAMAGE / 4 ;
break ;
}
# endif
if ( fldmg < 0 )
pOther - > TakeHealth ( - fldmg , m_bitsDamageInflict ) ;
else
pOther - > TakeDamage ( pev , pev , fldmg , m_bitsDamageInflict ) ;
// Store pain time so we can get all of the other entities on this frame
pev - > pain_finished = gpGlobals - > time ;
// Apply damage every half second
pev - > dmgtime = gpGlobals - > time + 0.5 ; // half second delay until this trigger can hurt toucher again
if ( pev - > target )
{
// trigger has a target it wants to fire.
if ( pev - > spawnflags & SF_TRIGGER_HURT_CLIENTONLYFIRE )
{
// if the toucher isn't a client, don't fire the target!
if ( ! pOther - > IsPlayer ( ) )
{
return ;
}
}
SUB_UseTargets ( pOther , USE_TOGGLE , 0 ) ;
if ( pev - > spawnflags & SF_TRIGGER_HURT_TARGETONCE )
pev - > target = 0 ;
}
}
/*QUAKED trigger_multiple (.5 .5 .5) ? notouch
Variable sized repeatable trigger . Must be targeted at one or more entities .
If " health " is set , the trigger must be killed to activate each time .
If " delay " is set , the trigger waits some time after activating before firing .
" wait " : Seconds between triggerings . ( .2 default )
If notouch is set , the trigger is only fired by other entities , not by touching .
NOTOUCH has been obsoleted by trigger_relay !
sounds
1 ) secret
2 ) beep beep
3 ) large switch
4 )
NEW
if a trigger has a NETNAME , that NETNAME will become the TARGET of the triggered object .
*/
class CTriggerMultiple : public CBaseTrigger
{
public :
void Spawn ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_multiple , CTriggerMultiple )
void CTriggerMultiple : : Spawn ( void )
{
if ( m_flWait = = 0 )
m_flWait = 0.2 ;
InitTrigger ( ) ;
ASSERTSZ ( pev - > health = = 0 , " trigger_multiple with health " ) ;
/*UTIL_SetOrigin( pev, pev->origin );
SET_MODEL ( ENT ( pev ) , STRING ( pev - > model ) ) ;
if ( pev - > health > 0 )
{
if ( FBitSet ( pev - > spawnflags , SPAWNFLAG_NOTOUCH ) )
ALERT ( at_error , " trigger_multiple spawn: health and notouch don't make sense " ) ;
pev - > max_health = pev - > health ;
UNDONE : where to get pfnDie from ?
pev - > pfnDie = multi_killed ;
pev - > takedamage = DAMAGE_YES ;
pev - > solid = SOLID_BBOX ;
UTIL_SetOrigin ( pev , pev - > origin ) ; // make sure it links into the world
}
else */
{
SetTouch ( & CBaseTrigger : : MultiTouch ) ;
}
}
/*QUAKED trigger_once (.5 .5 .5) ? notouch
Variable sized trigger . Triggers once , then removes itself . You must set the key " target " to the name of another object in the level that has a matching
" targetname " . If " health " is set , the trigger must be killed to activate .
If notouch is set , the trigger is only fired by other entities , not by touching .
if " killtarget " is set , any objects that have a matching " target " will be removed when the trigger is fired .
if " angle " is set , the trigger will only fire when someone is facing the direction of the angle . Use " 360 " for an angle of 0.
sounds
1 ) secret
2 ) beep beep
3 ) large switch
4 )
*/
class CTriggerOnce : public CTriggerMultiple
{
public :
void Spawn ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_once , CTriggerOnce )
void CTriggerOnce : : Spawn ( void )
{
m_flWait = - 1 ;
CTriggerMultiple : : Spawn ( ) ;
}
void CBaseTrigger : : MultiTouch ( CBaseEntity * pOther )
{
entvars_t * pevToucher ;
pevToucher = pOther - > pev ;
// Only touch clients, monsters, or pushables (depending on flags)
if ( ( ( pevToucher - > flags & FL_CLIENT ) & & ! ( pev - > spawnflags & SF_TRIGGER_NOCLIENTS ) ) | |
( ( pevToucher - > flags & FL_MONSTER ) & & ( pev - > spawnflags & SF_TRIGGER_ALLOWMONSTERS ) ) | |
( ( pev - > spawnflags & SF_TRIGGER_PUSHABLES ) & & FClassnameIs ( pevToucher , " func_pushable " ) ) )
{
#if 0
// if the trigger has an angles field, check player's facing direction
if ( pev - > movedir ! = g_vecZero )
{
UTIL_MakeVectors ( pevToucher - > angles ) ;
if ( DotProduct ( gpGlobals - > v_forward , pev - > movedir ) < 0 )
return ; // not facing the right way
}
# endif
ActivateMultiTrigger ( pOther ) ;
}
}
//
// the trigger was just touched/killed/used
// self.enemy should be set to the activator so it can be held through a delay
// so wait for the delay time before firing
//
void CBaseTrigger : : ActivateMultiTrigger ( CBaseEntity * pActivator )
{
if ( pev - > nextthink > gpGlobals - > time )
return ; // still waiting for reset time
if ( ! UTIL_IsMasterTriggered ( m_sMaster , pActivator ) )
return ;
if ( FClassnameIs ( pev , " trigger_secret " ) )
{
if ( pev - > enemy = = NULL | | ! FClassnameIs ( pev - > enemy , " player " ) )
return ;
gpGlobals - > found_secrets + + ;
}
if ( ! FStringNull ( pev - > noise ) )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , ( char * ) STRING ( pev - > noise ) , 1 , ATTN_NORM ) ;
// don't trigger again until reset
// pev->takedamage = DAMAGE_NO;
m_hActivator = pActivator ;
SUB_UseTargets ( m_hActivator , USE_TOGGLE , 0 ) ;
if ( pev - > message & & pActivator - > IsPlayer ( ) )
{
UTIL_ShowMessage ( STRING ( pev - > message ) , pActivator ) ;
//CLIENT_PRINTF( ENT( pActivator->pev ), print_center, STRING( pev->message ) );
}
if ( m_flWait > 0 )
{
SetThink ( & CBaseTrigger : : MultiWaitOver ) ;
pev - > nextthink = gpGlobals - > time + m_flWait ;
}
else
{
// we can't just remove (self) here, because this is a touch function
// called while C code is looping through area links...
SetTouch ( NULL ) ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
SetThink ( & CBaseEntity : : SUB_Remove ) ;
}
}
// the wait time has passed, so set back up for another activation
void CBaseTrigger : : MultiWaitOver ( void )
{
/*if( pev->max_health )
{
pev - > health = pev - > max_health ;
pev - > takedamage = DAMAGE_YES ;
pev - > solid = SOLID_BBOX ;
} */
SetThink ( NULL ) ;
}
// ========================= COUNTING TRIGGER =====================================
//
// GLOBALS ASSUMED SET: g_eoActivator
//
void CBaseTrigger : : CounterUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
m_cTriggersLeft - - ;
m_hActivator = pActivator ;
if ( m_cTriggersLeft < 0 )
return ;
BOOL fTellActivator =
( m_hActivator ! = 0 ) & &
FClassnameIs ( m_hActivator - > pev , " player " ) & &
! FBitSet ( pev - > spawnflags , SPAWNFLAG_NOMESSAGE ) ;
if ( m_cTriggersLeft ! = 0 )
{
if ( fTellActivator )
{
// UNDONE: I don't think we want these Quakesque messages
switch ( m_cTriggersLeft )
{
case 1 :
ALERT ( at_console , " Only 1 more to go... " ) ;
break ;
case 2 :
ALERT ( at_console , " Only 2 more to go... " ) ;
break ;
case 3 :
ALERT ( at_console , " Only 3 more to go... " ) ;
break ;
default :
ALERT ( at_console , " There are more to go... " ) ;
break ;
}
}
return ;
}
// !!!UNDONE: I don't think we want these Quakesque messages
if ( fTellActivator )
ALERT ( at_console , " Sequence completed! " ) ;
ActivateMultiTrigger ( m_hActivator ) ;
}
/*QUAKED trigger_counter (.5 .5 .5) ? nomessage
Acts as an intermediary for an action that takes multiple inputs .
If nomessage is not set , it will print " 1 more.. " etc when triggered and
" sequence complete " when finished . After the counter has been triggered " cTriggersLeft "
times ( default 2 ) , it will fire all of it ' s targets and remove itself .
*/
class CTriggerCounter : public CBaseTrigger
{
public :
void Spawn ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_counter , CTriggerCounter )
void CTriggerCounter : : Spawn ( void )
{
// By making the flWait be -1, this counter-trigger will disappear after it's activated
// (but of course it needs cTriggersLeft "uses" before that happens).
m_flWait = - 1 ;
if ( m_cTriggersLeft = = 0 )
m_cTriggersLeft = 2 ;
SetUse ( & CBaseTrigger : : CounterUse ) ;
}
// ====================== TRIGGER_CHANGELEVEL ================================
class CTriggerVolume : public CPointEntity // Derive from point entity so this doesn't move across levels
{
public :
void Spawn ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_transition , CTriggerVolume )
// Define space that travels across a level transition
void CTriggerVolume : : Spawn ( void )
{
pev - > solid = SOLID_NOT ;
pev - > movetype = MOVETYPE_NONE ;
SET_MODEL ( ENT ( pev ) , STRING ( pev - > model ) ) ; // set size and link into world
pev - > model = NULL ;
pev - > modelindex = 0 ;
}
// Fires a target after level transition and then dies
class CFireAndDie : public CBaseDelay
{
public :
void Spawn ( void ) ;
void Precache ( void ) ;
void Think ( void ) ;
int ObjectCaps ( void ) { return CBaseDelay : : ObjectCaps ( ) | FCAP_FORCE_TRANSITION ; } // Always go across transitions
} ;
LINK_ENTITY_TO_CLASS ( fireanddie , CFireAndDie )
void CFireAndDie : : Spawn ( void )
{
ALERT ( at_console , " fireanddie: spawn! \n " ) ;
pev - > classname = MAKE_STRING ( " fireanddie " ) ;
// Don't call Precache() - it should be called on restore
}
void CFireAndDie : : Precache ( void )
{
ALERT ( at_console , " fireanddie: precache! \n " ) ;
// This gets called on restore
pev - > nextthink = gpGlobals - > time + m_flDelay ;
}
void CFireAndDie : : Think ( void )
{
ALERT ( at_console , " fireanddie: firing! \n " ) ;
SUB_UseTargets ( this , USE_TOGGLE , 0 ) ;
UTIL_Remove ( this ) ;
}
# define SF_CHANGELEVEL_USEONLY 0x0002
class CChangeLevel : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void KeyValue ( KeyValueData * pkvd ) ;
void EXPORT UseChangeLevel ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void EXPORT TriggerChangeLevel ( void ) ;
void EXPORT ExecuteChangeLevel ( void ) ;
void EXPORT TouchChangeLevel ( CBaseEntity * pOther ) ;
void EXPORT UpdateColor ( ) ;
void ChangeLevelNow ( CBaseEntity * pActivator ) ;
static edict_t * FindLandmark ( const char * pLandmarkName ) ;
static int ChangeList ( LEVELLIST * pLevelList , int maxList ) ;
static int AddTransitionToList ( LEVELLIST * pLevelList , int listCount , const char * pMapName , const char * pLandmarkName , edict_t * pentLandmark ) ;
static int InTransitionVolume ( CBaseEntity * pEntity , char * pVolumeName ) ;
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
char m_szMapName [ cchMapNameMost ] ; // trigger_changelevel only: next map
char m_szLandmarkName [ cchMapNameMost ] ; // trigger_changelevel only: landmark on next map
int m_changeTarget ;
float m_changeTargetDelay ;
unsigned int m_uTouchCount ;
bool m_bUsed ;
Vector m_vecSpawnOrigin ;
Vector m_vecSpawnAngles ;
bool m_fSpawnSaved ;
bool m_fIsBack ;
bool m_fSkipSpawnCheck ; // skip 30 seconds check when called by multimanager or trigger_once
float m_flRepeatTimer ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_changelevel , CChangeLevel )
// Global Savedata for changelevel trigger
TYPEDESCRIPTION CChangeLevel : : m_SaveData [ ] =
{
DEFINE_ARRAY ( CChangeLevel , m_szMapName , FIELD_CHARACTER , cchMapNameMost ) ,
DEFINE_ARRAY ( CChangeLevel , m_szLandmarkName , FIELD_CHARACTER , cchMapNameMost ) ,
DEFINE_FIELD ( CChangeLevel , m_changeTarget , FIELD_STRING ) ,
DEFINE_FIELD ( CChangeLevel , m_changeTargetDelay , FIELD_FLOAT ) ,
} ;
IMPLEMENT_SAVERESTORE ( CChangeLevel , CBaseTrigger )
//
// Cache user-entity-field values until spawn is called.
//
void CChangeLevel : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " map " ) )
{
if ( strlen ( pkvd - > szValue ) > = cchMapNameMost )
ALERT ( at_error , " Map name '%s' too long (32 chars) \n " , pkvd - > szValue ) ;
strcpy ( m_szMapName , pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " landmark " ) )
{
if ( strlen ( pkvd - > szValue ) > = cchMapNameMost )
ALERT ( at_error , " Landmark name '%s' too long (32 chars) \n " , pkvd - > szValue ) ;
strcpy ( m_szLandmarkName , pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " changetarget " ) )
{
m_changeTarget = ALLOC_STRING ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " changedelay " ) )
{
m_changeTargetDelay = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseTrigger : : KeyValue ( pkvd ) ;
}
bool CoopGetSpawnPoint ( Vector * origin , Vector * angles ) ;
/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION
When the player touches this , he gets sent to the map listed in the " map " variable . Unless the NO_INTERMISSION flag is set , the view will go to the info_intermission spot and display stats .
*/
void CChangeLevel : : Spawn ( void )
{
m_uTouchCount = 0 ;
m_bUsed = false ;
if ( FStrEq ( m_szMapName , " " ) )
ALERT ( at_console , " a trigger_changelevel doesn't have a map " ) ;
if ( FStrEq ( m_szLandmarkName , " " ) )
ALERT ( at_console , " trigger_changelevel to %s doesn't have a landmark \n " , m_szMapName ) ;
if ( ! FStringNull ( pev - > targetname ) )
{
SetUse ( & CChangeLevel : : UseChangeLevel ) ;
}
InitTrigger ( ) ;
if ( ! ( pev - > spawnflags & SF_CHANGELEVEL_USEONLY ) )
{
SetTouch ( & CChangeLevel : : TouchChangeLevel ) ;
if ( mp_coop_changelevel . value )
{
if ( gpGlobals - > startspot & & STRING ( gpGlobals - > startspot ) & & ! strcmp ( STRING ( gpGlobals - > startspot ) , m_szLandmarkName ) )
m_fIsBack = true ;
// set color (got from XDM)
if ( m_fIsBack )
pev - > rendercolor . z = 255 ;
else
pev - > rendercolor . y = 255 ;
pev - > renderamt = 127 ;
pev - > effects & = ~ EF_NODRAW ;
pev - > rendermode = kRenderTransColor ;
SetThink ( & CChangeLevel : : UpdateColor ) ;
pev - > nextthink = gpGlobals - > time + 2 ;
}
}
//ALERT( at_console, "TRANSITION: %s (%s)\n", m_szMapName, m_szLandmarkName );
}
void CChangeLevel : : ExecuteChangeLevel ( void )
{
MESSAGE_BEGIN ( MSG_ALL , SVC_CDTRACK ) ;
WRITE_BYTE ( 3 ) ;
WRITE_BYTE ( 3 ) ;
MESSAGE_END ( ) ;
MESSAGE_BEGIN ( MSG_ALL , SVC_INTERMISSION ) ;
MESSAGE_END ( ) ;
}
FILE_GLOBAL char st_szNextMap [ cchMapNameMost ] ;
FILE_GLOBAL char st_szNextSpot [ cchMapNameMost ] ;
edict_t * CChangeLevel : : FindLandmark ( const char * pLandmarkName )
{
edict_t * pentLandmark ;
pentLandmark = FIND_ENTITY_BY_STRING ( NULL , " targetname " , pLandmarkName ) ;
while ( ! FNullEnt ( pentLandmark ) )
{
// Found the landmark
if ( FClassnameIs ( pentLandmark , " info_landmark " ) )
return pentLandmark ;
else
pentLandmark = FIND_ENTITY_BY_STRING ( pentLandmark , " targetname " , pLandmarkName ) ;
}
ALERT ( at_error , " Can't find landmark %s \n " , pLandmarkName ) ;
return NULL ;
}
//=========================================================
// CChangeLevel :: Use - allows level transitions to be
// triggered by buttons, etc.
//
//=========================================================
void CChangeLevel : : UseChangeLevel ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
// if not activated by touch, do not count players
m_bUsed = true ;
if ( pCaller & & FClassnameIs ( pCaller - > edict ( ) , " multimanager " ) | | FClassnameIs ( pCaller - > edict ( ) , " trigger_once " ) )
m_fSkipSpawnCheck = true ;
else
m_fSkipSpawnCheck = false ;
ChangeLevelNow ( pActivator ) ;
}
void UTIL_CoopActivateChangeLevel ( CBaseEntity * pTrigger )
{
CChangeLevel * trigger = ( CChangeLevel * ) pTrigger ;
if ( ! trigger )
return ;
trigger - > m_bUsed = true ;
trigger - > ChangeLevelNow ( NULL ) ;
}
void GlobalMenu : : VoteMenu ( CBasePlayer * pPlayer )
{
if ( g_iMenu & & gpGlobals - > time - m_flTime < 30 )
return ; // wait 30s befor new confirm vote
CBaseEntity * pTrigger = NULL ;
int i = 0 ;
g_iMenu = 2 ;
m_flTime = gpGlobals - > time ;
maps [ i + + ] = " Keep this map " ;
maps [ i + + ] = " BAN " ;
while ( ( pTrigger = UTIL_FindEntityByClassname ( pTrigger , " trigger_changelevel " ) ) & & ( i < 5 ) )
{
CChangeLevel * ent = ( CChangeLevel * ) pTrigger ;
votes [ i ] = 0 ;
triggers [ i ] = ent ;
maps [ i + + ] = ent - > m_szMapName ;
}
votes [ i ] = 0 ;
triggers [ i ] = NULL ;
m_iConfirm = i ;
m_iVoteCount = 0 ;
m_pPlayer = pPlayer ;
MESSAGE_BEGIN ( MSG_ALL , 8 , NULL ) ; // svc_print
WRITE_BYTE ( 3 ) ; // PRINT_CHAT
WRITE_STRING ( UTIL_VarArgs ( " %s^7 opened vote menu \n " , ( pPlayer - > pev - > netname & & STRING ( pPlayer - > pev - > netname ) [ 0 ] ! = 0 ) ? STRING ( pPlayer - > pev - > netname ) : " unconnected " ) ) ;
MESSAGE_END ( ) ;
ShowGlobalMenu ( UTIL_VarArgs ( " %s requested to force change map " , UTIL_CoopPlayerName ( pPlayer ) ) , i , maps ) ;
}
void UTIL_CoopValidateOffset ( void )
{
if ( ! g_SavedCoords . validoffset )
{
edict_t * landmark = CChangeLevel : : FindLandmark ( g_SavedCoords . landmark ) ;
if ( landmark )
g_SavedCoords . offset = landmark - > v . origin - g_SavedCoords . offset ;
else
g_SavedCoords . offset = g_vecZero - g_SavedCoords . offset ;
g_SavedCoords . validoffset = true ;
}
}
void CChangeLevel : : UpdateColor ( void )
{
Vector origin = VecBModelOrigin ( pev ) ;
Vector point , angles ;
CBaseEntity * pPlayer ;
pev - > nextthink = gpGlobals - > time + 30 ;
if ( ! m_fIsBack & & ( pPlayer = UTIL_FindEntityByClassname ( NULL , " info_player_start " ) ) )
if ( ( origin - pPlayer - > pev - > origin ) . Length ( ) < 500 )
m_fIsBack = true ;
if ( ! m_fIsBack & & UTIL_CoopGetSpawnPoint ( & point , & angles ) )
if ( ( origin - point ) . Length ( ) < 500 )
m_fIsBack = true ;
pev - > rendercolor = g_vecZero ;
if ( m_fIsBack )
pev - > rendercolor . z = 255 ;
else
pev - > rendercolor . y = 255 ;
if ( gpGlobals - > time - g_GlobalMenu . m_flTime > 30 )
{
g_iMenu = 0 ;
g_GlobalMenu . m_iConfirm = 0 ;
}
}
// If some player is on train with global state, save id
CBaseEntity * FindTriggerTransition ( char * pVolumeName )
{
edict_t * pentVolume = FIND_ENTITY_BY_TARGETNAME ( NULL , pVolumeName ) ;
while ( ! FNullEnt ( pentVolume ) )
{
CBaseEntity * pVolume = CBaseEntity : : Instance ( pentVolume ) ;
if ( pVolume & & FClassnameIs ( pVolume - > pev , " trigger_transition " ) )
{
return pVolume ;
}
pentVolume = FIND_ENTITY_BY_TARGETNAME ( pentVolume , pVolumeName ) ;
}
return NULL ;
}
void CChangeLevel : : ChangeLevelNow ( CBaseEntity * pActivator )
{
edict_t * pentLandmark ;
LEVELLIST levels [ 16 ] ;
hudtextparms_t params = { } ;
params . fadeinTime = 0.5 ;
params . fadeoutTime = .5 ;
params . holdTime = 10 ;
params . r2 = params . g2 = params . b2 = params . a2 = params . r1 = params . g1 = params . b1 = params . a1 = 255 ;
bool valid = false ;
ASSERT ( ! FStrEq ( m_szMapName , " " ) ) ;
if ( s_SavedCoords . valid )
return ; //already pending
if ( ! strcmp ( m_szMapName , mp_coop_disabledmap . string ) )
{
UTIL_HudMessageAll ( params , UTIL_VarArgs ( " MAP %S IS DISABLED " , m_szMapName ) ) ;
return ;
}
// forget touch by some fool
if ( gpGlobals - > time - pev - > dmgtime > 30 )
{
m_uTouchCount = 0 ;
pev - > nextthink = gpGlobals - > time + 30 ;
}
if ( mp_coop_changelevel . value )
{
SavedCoords l_SavedCoords = { } ;
// if not activated by touch, do not count players
if ( ! m_bUsed )
{
m_uTouchCount | = 1 < < ( ENTINDEX ( pActivator - > edict ( ) ) - 1 ) ;
// Keep first toucher coords to save correct spawn angles
if ( ! m_fSpawnSaved & & pActivator - > IsPlayer ( ) )
{
m_vecSpawnOrigin = pActivator - > pev - > origin ;
m_vecSpawnAngles = pActivator - > pev - > angles ;
m_fSpawnSaved = true ;
UTIL_CoopSaveTrain ( pActivator , & l_SavedCoords ) ;
}
unsigned int count1 = 0 ;
unsigned int count2 = 0 ;
unsigned int i = 0 ;
// loop through all clients, count number of players
for ( i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * plr = UTIL_PlayerByIndex ( i ) ;
CBaseEntity * pTrain = UTIL_CoopGetPlayerTrain ( plr ) ;
if ( ! pTrain & & UTIL_CoopIsBadPlayer ( plr ) )
continue ;
// count only players spawned more 30 seconds ago
if ( plr & & plr - > IsPlayer ( ) & & ( pTrain | | ( gpGlobals - > time - ( ( CBasePlayer * ) plr ) - > m_flSpawnTime ) > 30 ) )
{
count2 + + ;
// player touched trigger, save it's coordinates
if ( m_uTouchCount & ( 1 < < ( i - 1 ) ) )
{
count1 + + ;
if ( InTransitionVolume ( plr , m_szLandmarkName ) )
{
l_SavedCoords . fDuck | = ! ! ( plr - > pev - > flags & FL_DUCKING ) ;
UTIL_CoopSaveTrain ( plr , & l_SavedCoords ) ;
char * ip = g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( plr - > edict ( ) ) , " ip " ) ;
strcpy ( l_SavedCoords . ip [ l_SavedCoords . iCount ] , ip ) ;
l_SavedCoords . origin [ l_SavedCoords . iCount ] = plr - > pev - > origin ;
l_SavedCoords . angles [ l_SavedCoords . iCount ] = plr - > pev - > angles ;
l_SavedCoords . iCount + + ;
}
}
}
}
i = 0 ;
if ( ! l_SavedCoords . trainsaved )
{
if ( ! count2 )
{
if ( pActivator & & pActivator - > IsPlayer ( ) & & m_flRepeatTimer - gpGlobals - > time < - 1 ) {
CBasePlayer * pPlayer = ( CBasePlayer * ) pActivator ;
MESSAGE_BEGIN ( MSG_ALL , 8 , NULL ) ; // svc_print
WRITE_BYTE ( 3 ) ; // PRINT_CHAT
WRITE_STRING ( UTIL_VarArgs ( " %s^7 trying activate changelevel too soon \n " , ( pPlayer - > pev - > netname & & STRING ( pPlayer - > pev - > netname ) [ 0 ] ! = 0 ) ? STRING ( pPlayer - > pev - > netname ) : " unconnected " ) ) ;
MESSAGE_END ( ) ;
UTIL_CleanSpawnPoint ( pPlayer - > pev - > origin , 50 ) ;
m_flRepeatTimer = gpGlobals - > time ;
pPlayer - > m_iLocalConfirm = - 2 ;
}
UTIL_HudMessageAll ( params , " Cannot change level: Not enough players! \n Wait 30 sec before you may changelevel! " ) ;
return ;
}
if ( count1 > 1 & & count1 < count2 / 3 )
i = count1 - count1 < count2 / 3 ;
if ( count1 > 1 & & count1 < count2 / 3 )
i = 1 ;
if ( i )
UTIL_HudMessageAll ( params , UTIL_VarArgs ( " %s touched end of map \n next is %s %s, %d to go \n " ,
( pActivator - > pev - > netname & & STRING ( pActivator - > pev - > netname ) [ 0 ] ! = 0 ) ? STRING ( pActivator - > pev - > netname ) : " unconnected " ,
st_szNextMap , st_szNextSpot , i ) ) ;
if ( count2 )
{
pev - > rendercolor . x = count1 * 255 / count2 ;
if ( m_fIsBack )
pev - > rendercolor . z = 255 - pev - > rendercolor . x ;
else
pev - > rendercolor . y = 255 - pev - > rendercolor . x ;
}
ALERT ( at_console , " ^3CHANGELEVEL:^7 %d %d \n " , count2 , count1 ) ;
if ( count1 > 1 & & count1 < count2 / 3 )
return ;
//if( count1 <= 1 && count2 == 2 )
// return;
if ( m_fIsBack )
{
if ( gpGlobals - > time - g_GlobalMenu . m_flTime > 30 )
{
g_iMenu = 0 ;
g_GlobalMenu . m_iConfirm = 0 ;
}
if ( g_iMenu ! = 1 )
{
if ( ! UTIL_CoopIsBadPlayer ( pActivator ) )
g_GlobalMenu . ConfirmMenu ( ( CBasePlayer * ) pActivator , this , m_szMapName ) ;
return ;
}
if ( g_GlobalMenu . m_iConfirm < count2 )
return ;
//if( mp_coop_strongpolicy.value )
/*{
// do not allow go back if there are checkpoints, but not near changelevel
if ( g_checkpoints [ 0 ] . time & & ( g_checkpoints [ 0 ] . origin - VecBModelOrigin ( pev ) ) . Length ( ) > 150 )
return ;
if ( count2 < 2 )
return ;
} */
}
}
if ( m_fSpawnSaved )
{
l_SavedCoords . triggerangles = m_vecSpawnAngles ;
l_SavedCoords . triggerorigin = m_vecSpawnOrigin ;
valid = true ;
}
ALERT ( at_console , " ^2CHANGELEVEL:^7 %d %d %d \n " , count2 , count1 , ( int ) m_fIsBack ) ;
}
else
{
l_SavedCoords . fDuck = false ;
// Get current player's coordinates
if ( pActivator & & pActivator - > IsPlayer ( ) )
{
l_SavedCoords . triggerangles = pActivator - > pev - > angles ;
l_SavedCoords . triggerorigin = pActivator - > pev - > origin ;
valid = true ;
l_SavedCoords . fDuck | = ! ! ( pActivator - > pev - > flags & FL_DUCKING ) ;
}
}
UTIL_CoopSaveTrain ( pActivator , & l_SavedCoords ) ;
s_SavedCoords = l_SavedCoords ;
}
// Don't work in deathmatch
else if ( g_pGameRules - > IsDeathmatch ( ) )
return ;
m_uTouchCount = 0 ;
m_bUsed = false ;
// Some people are firing these multiple times in a frame, disable
if ( gpGlobals - > time = = pev - > dmgtime )
return ;
pev - > dmgtime = gpGlobals - > time ;
CBaseEntity * pPlayer = NULL ;
if ( pActivator & & pActivator - > IsPlayer ( ) )
pPlayer = pActivator ;
if ( ! pPlayer )
{
int count = 0 ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * plr = UTIL_PlayerByIndex ( i ) ;
if ( plr & & plr - > IsPlayer ( ) & & ( ! FindTriggerTransition ( m_szLandmarkName ) | | ( gpGlobals - > time - ( ( CBasePlayer * ) plr ) - > m_flSpawnTime ) > 30 | | m_fSkipSpawnCheck ) )
{
if ( InTransitionVolume ( plr , m_szLandmarkName ) )
{
count + + ;
}
}
}
if ( ! count )
{
ALERT ( at_console , " There are no players in transition volume %s, aborting \n " , m_szLandmarkName ) ;
return ;
}
ALERT ( at_console , " There are %d players in transition volume %s \n " , count , m_szLandmarkName ) ;
}
else if ( ! m_fSkipSpawnCheck & & ( ( gpGlobals - > time - ( ( CBasePlayer * ) pPlayer ) - > m_flSpawnTime ) < 30 ) & & FindTriggerTransition ( m_szLandmarkName ) & & ! InTransitionVolume ( pPlayer , m_szLandmarkName ) )
{
ALERT ( at_console , " Player isn't in the transition volume %s, aborting \n " , m_szLandmarkName ) ;
return ;
}
//else
/* if !pPlayer
{
ALERT ( at_console , " , aborting \n " ) ;
return ;
} */
MESSAGE_BEGIN ( MSG_ALL , 8 , NULL ) ; // svc_print
WRITE_BYTE ( 3 ) ; // PRINT_CHAT
WRITE_STRING ( UTIL_VarArgs ( " %s^7 activated changelevel \n " , ( pPlayer - > pev - > netname & & STRING ( pPlayer - > pev - > netname ) [ 0 ] ! = 0 ) ? STRING ( pPlayer - > pev - > netname ) : " unconnected " ) ) ;
MESSAGE_END ( ) ;
// shedule remove ke^w on first info_player_start
/*edict_t *playerstart = FIND_ENTITY_BY_CLASSNAME( NULL, "info_player_start" );
if ( ! FNullEnt ( playerstart ) )
playerstart - > v . flags | = FL_KILLME ; */
// This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory
strcpy ( st_szNextMap , m_szMapName ) ;
m_hActivator = pActivator ;
SUB_UseTargets ( pActivator , USE_TOGGLE , 0 ) ;
st_szNextSpot [ 0 ] = 0 ; // Init landmark to NULL
// look for a landmark entity
pentLandmark = FindLandmark ( m_szLandmarkName ) ;
if ( ! FNullEnt ( pentLandmark ) )
{
strcpy ( st_szNextSpot , m_szLandmarkName ) ;
strcpy ( s_SavedCoords . landmark , m_szLandmarkName ) ;
s_SavedCoords . offset = gpGlobals - > vecLandmarkOffset = VARS ( pentLandmark ) - > origin ;
}
// Create an entity to fire the changetarget
if ( m_changeTarget )
{
CFireAndDie * pFireAndDie = GetClassPtr ( ( CFireAndDie * ) NULL ) ;
if ( pFireAndDie )
{
// Set target and delay
pFireAndDie - > pev - > target = m_changeTarget ;
pFireAndDie - > m_flDelay = m_changeTargetDelay + 1 ;
if ( pPlayer )
pFireAndDie - > pev - > origin = pPlayer - > pev - > origin ;
else if ( ! FNullEnt ( pentLandmark ) )
pFireAndDie - > pev - > origin = VARS ( pentLandmark ) - > origin ; // always in landmark's PVS
// Call spawn
DispatchSpawn ( pFireAndDie - > edict ( ) ) ;
}
}
//ALERT( at_console, "Level touches %d levels\n", ChangeList( levels, 16 ) );
ALERT ( at_console , " CHANGE LEVEL: %s %s \n " , st_szNextMap , st_szNextSpot ) ;
g_iMenu = 0 ;
g_GlobalMenu . m_iConfirm = 0 ;
// loop through all clients, reset state
if ( mp_coop_changelevel . value )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * plr = ( CBasePlayer * ) UTIL_PlayerByIndex ( i ) ;
// reset all players state
if ( plr )
{
plr - > m_state = STATE_UNINITIALIZED ;
plr - > RemoveAllItems ( TRUE ) ;
UTIL_BecomeSpectator ( plr ) ;
//plr->SetThink( &CBasePlayer::Spawn );
//plr->pev->nextthink = gpGlobals->time + 1;
// HACK: force perform reconnection
if ( mp_coop_reconnect_hack . value )
CLIENT_COMMAND ( plr - > edict ( ) , " reconnect \n " ) ;
//CLIENT_COMMAND( plr->edict(), "alias cmd \"reconnect;unalias cmd\"\n" );
//MESSAGE_BEGIN( MSG_ONE, 2, NULL, plr->pev ); // svc_disconnect after stufftext
//MESSAGE_END();
//SERVER_COMMAND( UTIL_VarArgs( "kick %d\n", i-1 ) );
//SERVER_EXECUTE();
}
}
g_fPause = true ;
}
s_SavedCoords . fUsed = m_bUsed ;
s_SavedCoords . valid = valid ;
if ( mp_coop_reconnect_hack . value )
SERVER_COMMAND ( UTIL_VarArgs ( " wait;wait;wait;wait;wait;changelevel %s %s \n " , st_szNextMap , st_szNextSpot ) ) ;
else
CHANGE_LEVEL ( st_szNextMap , st_szNextSpot ) ;
}
//
// GLOBALS ASSUMED SET: st_szNextMap
//
void CChangeLevel : : TouchChangeLevel ( CBaseEntity * pOther )
{
if ( ! FClassnameIs ( pOther - > pev , " player " ) )
return ;
m_fSkipSpawnCheck = false ;
ChangeLevelNow ( pOther ) ;
}
// Add a transition to the list, but ignore duplicates
// (a designer may have placed multiple trigger_changelevels with the same landmark)
int CChangeLevel : : AddTransitionToList ( LEVELLIST * pLevelList , int listCount , const char * pMapName , const char * pLandmarkName , edict_t * pentLandmark )
{
int i ;
if ( ! pLevelList | | ! pMapName | | ! pLandmarkName | | ! pentLandmark )
return 0 ;
for ( i = 0 ; i < listCount ; i + + )
{
if ( pLevelList [ i ] . pentLandmark = = pentLandmark & & strcmp ( pLevelList [ i ] . mapName , pMapName ) = = 0 )
return 0 ;
}
strcpy ( pLevelList [ listCount ] . mapName , pMapName ) ;
strcpy ( pLevelList [ listCount ] . landmarkName , pLandmarkName ) ;
pLevelList [ listCount ] . pentLandmark = pentLandmark ;
pLevelList [ listCount ] . vecLandmarkOrigin = VARS ( pentLandmark ) - > origin ;
return 1 ;
}
int BuildChangeList ( LEVELLIST * pLevelList , int maxList )
{
return CChangeLevel : : ChangeList ( pLevelList , maxList ) ;
}
int CChangeLevel : : InTransitionVolume ( CBaseEntity * pEntity , char * pVolumeName )
{
edict_t * pentVolume ;
if ( ! pEntity )
return 0 ;
if ( pEntity - > ObjectCaps ( ) & FCAP_FORCE_TRANSITION )
return 1 ;
// If you're following another entity, follow it through the transition (weapons follow the player)
if ( pEntity - > pev - > movetype = = MOVETYPE_FOLLOW )
{
if ( pEntity - > pev - > aiment ! = NULL )
pEntity = CBaseEntity : : Instance ( pEntity - > pev - > aiment ) ;
}
if ( ! pEntity )
return 0 ;
int inVolume = 1 ; // Unless we find a trigger_transition, everything is in the volume
pentVolume = FIND_ENTITY_BY_TARGETNAME ( NULL , pVolumeName ) ;
while ( ! FNullEnt ( pentVolume ) )
{
CBaseEntity * pVolume = CBaseEntity : : Instance ( pentVolume ) ;
if ( pVolume & & FClassnameIs ( pVolume - > pev , " trigger_transition " ) )
{
if ( pVolume - > Intersects ( pEntity ) ) // It touches one, it's in the volume
return 1 ;
else
inVolume = 0 ; // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go!
}
pentVolume = FIND_ENTITY_BY_TARGETNAME ( pentVolume , pVolumeName ) ;
}
return inVolume ;
}
// We can only ever move 512 entities across a transition
# define MAX_ENTITY 512
// This has grown into a complicated beast
// Can we make this more elegant?
// This builds the list of all transitions on this level and which entities are in their PVS's and can / should
// be moved across.
int CChangeLevel : : ChangeList ( LEVELLIST * pLevelList , int maxList )
{
edict_t * pentChangelevel , * pentLandmark ;
int i , count ;
count = 0 ;
// Find all of the possible level changes on this BSP
pentChangelevel = FIND_ENTITY_BY_STRING ( NULL , " classname " , " trigger_changelevel " ) ;
if ( FNullEnt ( pentChangelevel ) )
return 0 ;
while ( ! FNullEnt ( pentChangelevel ) )
{
CChangeLevel * pTrigger ;
pTrigger = GetClassPtr ( ( CChangeLevel * ) VARS ( pentChangelevel ) ) ;
if ( pTrigger )
{
// Find the corresponding landmark
pentLandmark = FindLandmark ( pTrigger - > m_szLandmarkName ) ;
if ( pentLandmark )
{
// Build a list of unique transitions
if ( AddTransitionToList ( pLevelList , count , pTrigger - > m_szMapName , pTrigger - > m_szLandmarkName , pentLandmark ) )
{
count + + ;
if ( count > = maxList ) // FULL!!
break ;
}
}
}
pentChangelevel = FIND_ENTITY_BY_STRING ( pentChangelevel , " classname " , " trigger_changelevel " ) ;
}
if ( gpGlobals - > pSaveData & & ( ( SAVERESTOREDATA * ) gpGlobals - > pSaveData ) - > pTable )
{
CSave saveHelper ( ( SAVERESTOREDATA * ) gpGlobals - > pSaveData ) ;
for ( i = 0 ; i < count ; i + + )
{
int j , entityCount = 0 ;
CBaseEntity * pEntList [ MAX_ENTITY ] ;
int entityFlags [ MAX_ENTITY ] ;
// Follow the linked list of entities in the PVS of the transition landmark
edict_t * pent = UTIL_EntitiesInPVS ( pLevelList [ i ] . pentLandmark ) ;
// Build a list of valid entities in this linked list (we're going to use pent->v.chain again)
while ( ! FNullEnt ( pent ) )
{
CBaseEntity * pEntity = CBaseEntity : : Instance ( pent ) ;
if ( pEntity )
{
//ALERT( at_console, "Trying %s\n", STRING( pEntity->pev->classname ) );
int caps = pEntity - > ObjectCaps ( ) ;
if ( ! ( caps & FCAP_DONT_SAVE ) )
{
int flags = 0 ;
// If this entity can be moved or is global, mark it
if ( caps & FCAP_ACROSS_TRANSITION )
flags | = FENTTABLE_MOVEABLE ;
if ( pEntity - > pev - > globalname & & ! pEntity - > IsDormant ( ) )
flags | = FENTTABLE_GLOBAL ;
if ( flags )
{
pEntList [ entityCount ] = pEntity ;
entityFlags [ entityCount ] = flags ;
entityCount + + ;
if ( entityCount > = MAX_ENTITY )
{
ALERT ( at_error , " Too many entities across a transition! \n " ) ;
break ;
}
}
//else
// ALERT( at_console, "Failed %s\n", STRING( pEntity->pev->classname ) );
}
//else
// ALERT( at_console, "DON'T SAVE %s\n", STRING( pEntity->pev->classname ) );
}
pent = pent - > v . chain ;
}
for ( j = 0 ; j < entityCount ; j + + )
{
// Check to make sure the entity isn't screened out by a trigger_transition
if ( entityFlags [ j ] & & InTransitionVolume ( pEntList [ j ] , pLevelList [ i ] . landmarkName ) )
{
// Mark entity table with 1<<i
int index = saveHelper . EntityIndex ( pEntList [ j ] ) ;
// Flag it with the level number
saveHelper . EntityFlagsSet ( index , entityFlags [ j ] | ( 1 < < i ) ) ;
}
//else
// ALERT( at_console, "Screened out %s\n", STRING( pEntList[j]->pev->classname ) );
}
}
}
return count ;
}
/*
go to the next level for deathmatch
only called if a time or frag limit has expired
*/
void NextLevel ( void )
{
edict_t * pent ;
CChangeLevel * pChange ;
// find a trigger_changelevel
pent = FIND_ENTITY_BY_CLASSNAME ( NULL , " trigger_changelevel " ) ;
// go back to start if no trigger_changelevel
if ( FNullEnt ( pent ) )
{
gpGlobals - > mapname = ALLOC_STRING ( " start " ) ;
pChange = GetClassPtr ( ( CChangeLevel * ) NULL ) ;
strcpy ( pChange - > m_szMapName , " start " ) ;
}
else
pChange = GetClassPtr ( ( CChangeLevel * ) VARS ( pent ) ) ;
strcpy ( st_szNextMap , pChange - > m_szMapName ) ;
g_fGameOver = TRUE ;
if ( pChange - > pev - > nextthink < gpGlobals - > time )
{
pChange - > SetThink ( & CChangeLevel : : ExecuteChangeLevel ) ;
pChange - > pev - > nextthink = gpGlobals - > time + 0.1 ;
}
}
// ============================== LADDER =======================================
class CLadder : public CBaseTrigger
{
public :
void KeyValue ( KeyValueData * pkvd ) ;
void Spawn ( void ) ;
void Precache ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( func_ladder , CLadder )
void CLadder : : KeyValue ( KeyValueData * pkvd )
{
CBaseTrigger : : KeyValue ( pkvd ) ;
}
//=========================================================
// func_ladder - makes an area vertically negotiable
//=========================================================
void CLadder : : Precache ( void )
{
// Do all of this in here because we need to 'convert' old saved games
pev - > solid = SOLID_NOT ;
pev - > skin = CONTENTS_LADDER ;
if ( CVAR_GET_FLOAT ( " showtriggers " ) = = 0 )
{
pev - > rendermode = kRenderTransTexture ;
pev - > renderamt = 0 ;
}
pev - > effects & = ~ EF_NODRAW ;
}
void CLadder : : Spawn ( void )
{
Precache ( ) ;
SET_MODEL ( ENT ( pev ) , STRING ( pev - > model ) ) ; // set size and link into world
pev - > movetype = MOVETYPE_PUSH ;
}
// ========================== A TRIGGER THAT PUSHES YOU ===============================
class CTriggerPush : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void KeyValue ( KeyValueData * pkvd ) ;
void Touch ( CBaseEntity * pOther ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_push , CTriggerPush )
void CTriggerPush : : KeyValue ( KeyValueData * pkvd )
{
CBaseTrigger : : KeyValue ( pkvd ) ;
}
/*QUAKED trigger_push (.5 .5 .5) ? TRIG_PUSH_ONCE
Pushes the player
*/
void CTriggerPush : : Spawn ( )
{
if ( pev - > angles = = g_vecZero )
pev - > angles . y = 360 ;
InitTrigger ( ) ;
if ( pev - > speed = = 0 )
pev - > speed = 100 ;
// this flag was changed and flying barrels on c2a5 stay broken
if ( FStrEq ( STRING ( gpGlobals - > mapname ) , " c2a5 " ) & & pev - > spawnflags & 4 )
pev - > spawnflags | = SF_TRIG_PUSH_ONCE ;
if ( FBitSet ( pev - > spawnflags , SF_TRIGGER_PUSH_START_OFF ) ) // if flagged to Start Turned Off, make trigger nonsolid.
pev - > solid = SOLID_NOT ;
SetUse ( & CBaseTrigger : : ToggleUse ) ;
UTIL_SetOrigin ( pev , pev - > origin ) ; // Link into the list
}
void CTriggerPush : : Touch ( CBaseEntity * pOther )
{
entvars_t * pevToucher = pOther - > pev ;
// UNDONE: Is there a better way than health to detect things that have physics? (clients/monsters)
switch ( pevToucher - > movetype )
{
case MOVETYPE_NONE :
case MOVETYPE_PUSH :
case MOVETYPE_NOCLIP :
case MOVETYPE_FOLLOW :
return ;
}
if ( pevToucher - > solid ! = SOLID_NOT & & pevToucher - > solid ! = SOLID_BSP )
{
// Instant trigger, just transfer velocity and remove
if ( FBitSet ( pev - > spawnflags , SF_TRIG_PUSH_ONCE ) )
{
pevToucher - > velocity = pevToucher - > velocity + ( pev - > speed * pev - > movedir ) ;
if ( pevToucher - > velocity . z > 0 )
pevToucher - > flags & = ~ FL_ONGROUND ;
UTIL_Remove ( this ) ;
}
else
{
// Push field, transfer to base velocity
Vector vecPush = pev - > speed * pev - > movedir ;
if ( pevToucher - > flags & FL_BASEVELOCITY )
vecPush = vecPush + pevToucher - > basevelocity ;
pevToucher - > basevelocity = vecPush ;
pevToucher - > flags | = FL_BASEVELOCITY ;
//ALERT( at_console, "Vel %f, base %f\n", pevToucher->velocity.z, pevToucher->basevelocity.z );
}
}
}
//======================================
// teleport trigger
//
//
void CBaseTrigger : : TeleportTouch ( CBaseEntity * pOther )
{
entvars_t * pevToucher = pOther - > pev ;
edict_t * pentTarget = NULL ;
if ( ! ( pev - > spawnflags & SF_TRIGGER_TELEPORTALL ) )
{
// Only teleport monsters or clients
if ( ! FBitSet ( pevToucher - > flags , FL_CLIENT | FL_MONSTER ) )
return ;
if ( ! UTIL_IsMasterTriggered ( m_sMaster , pOther ) )
return ;
if ( ! ( pev - > spawnflags & SF_TRIGGER_ALLOWMONSTERS ) )
{
// no monsters allowed!
if ( FBitSet ( pevToucher - > flags , FL_MONSTER ) )
{
return ;
}
}
if ( ( pev - > spawnflags & SF_TRIGGER_NOCLIENTS ) )
{
// no clients allowed
if ( pOther - > IsPlayer ( ) )
{
return ;
}
}
}
else
{
if ( pOther - > IsBSPModel ( ) )
return ;
if ( pOther = = this )
return ;
}
pentTarget = FIND_ENTITY_BY_TARGETNAME ( pentTarget , STRING ( pev - > target ) ) ;
if ( FNullEnt ( pentTarget ) )
return ;
Vector tmp = VARS ( pentTarget ) - > origin ;
UTIL_CleanSpawnPoint ( tmp , 150 ) ;
if ( pOther - > IsPlayer ( ) )
{
tmp . z - = pOther - > pev - > mins . z ; // make origin adjustments in case the teleportee is a player. (origin in center, not at feet)
}
tmp . z + + ;
pevToucher - > flags & = ~ FL_ONGROUND ;
UTIL_SetOrigin ( pevToucher , tmp ) ;
if ( pev - > spawnflags & SF_TRIGGER_PORTAL )
pevToucher - > angles = pevToucher - > angles + pentTarget - > v . angles ;
else
pevToucher - > angles = pentTarget - > v . angles ;
if ( pOther - > IsPlayer ( ) )
{
pevToucher - > v_angle = pentTarget - > v . angles ;
}
pevToucher - > fixangle = TRUE ;
if ( ! ( pev - > spawnflags & SF_TRIGGER_PORTAL ) )
pevToucher - > velocity = pevToucher - > basevelocity = g_vecZero ;
}
class CTriggerTeleport : public CBaseTrigger
{
public :
void Spawn ( void ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_teleport , CTriggerTeleport )
void CTriggerTeleport : : Spawn ( void )
{
InitTrigger ( ) ;
SetTouch ( & CBaseTrigger : : TeleportTouch ) ;
}
LINK_ENTITY_TO_CLASS ( info_teleport_destination , CPointEntity )
class CTriggerSave : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void EXPORT SaveTouch ( CBaseEntity * pOther ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_autosave , CTriggerSave )
void CTriggerSave : : Spawn ( void )
{
if ( g_pGameRules - > IsDeathmatch ( ) )
{
REMOVE_ENTITY ( ENT ( pev ) ) ;
return ;
}
InitTrigger ( ) ;
SetTouch ( & CTriggerSave : : SaveTouch ) ;
}
void CTriggerSave : : SaveTouch ( CBaseEntity * pOther )
{
if ( ! UTIL_IsMasterTriggered ( m_sMaster , pOther ) )
return ;
// Only save on clients
if ( ! pOther - > IsPlayer ( ) )
return ;
SetTouch ( NULL ) ;
UTIL_Remove ( this ) ;
SERVER_COMMAND ( " autosave \n " ) ;
}
# define SF_ENDSECTION_USEONLY 0x0001
class CTriggerEndSection : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void EXPORT EndSectionTouch ( CBaseEntity * pOther ) ;
void KeyValue ( KeyValueData * pkvd ) ;
void EXPORT EndSectionUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_endsection , CTriggerEndSection )
void CTriggerEndSection : : EndSectionUse ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
// Only save on clients
if ( pActivator & & ! pActivator - > IsNetClient ( ) )
return ;
SetUse ( NULL ) ;
if ( pev - > message )
{
g_engfuncs . pfnEndSection ( STRING ( pev - > message ) ) ;
}
UTIL_Remove ( this ) ;
}
void CTriggerEndSection : : Spawn ( void )
{
if ( g_pGameRules - > IsDeathmatch ( ) )
{
REMOVE_ENTITY ( ENT ( pev ) ) ;
return ;
}
InitTrigger ( ) ;
SetUse ( & CTriggerEndSection : : EndSectionUse ) ;
// If it is a "use only" trigger, then don't set the touch function.
if ( ! ( pev - > spawnflags & SF_ENDSECTION_USEONLY ) )
SetTouch ( & CTriggerEndSection : : EndSectionTouch ) ;
}
void CTriggerEndSection : : EndSectionTouch ( CBaseEntity * pOther )
{
// Only save on clients
if ( ! pOther - > IsNetClient ( ) )
return ;
SetTouch ( NULL ) ;
if ( pev - > message )
{
g_engfuncs . pfnEndSection ( STRING ( pev - > message ) ) ;
}
UTIL_Remove ( this ) ;
}
void CTriggerEndSection : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " section " ) )
{
//m_iszSectionName = ALLOC_STRING( pkvd->szValue );
// Store this in message so we don't have to write save/restore for this ent
pev - > message = ALLOC_STRING ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseTrigger : : KeyValue ( pkvd ) ;
}
class CTriggerGravity : public CBaseTrigger
{
public :
void Spawn ( void ) ;
void EXPORT GravityTouch ( CBaseEntity * pOther ) ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_gravity , CTriggerGravity )
void CTriggerGravity : : Spawn ( void )
{
InitTrigger ( ) ;
SetTouch ( & CTriggerGravity : : GravityTouch ) ;
}
void CTriggerGravity : : GravityTouch ( CBaseEntity * pOther )
{
// Only save on clients
if ( ! pOther - > IsPlayer ( ) )
return ;
pOther - > pev - > gravity = pev - > gravity ;
}
// this is a really bad idea.
class CTriggerChangeTarget : public CBaseDelay
{
public :
void KeyValue ( KeyValueData * pkvd ) ;
void Spawn ( void ) ;
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
int ObjectCaps ( void ) { return CBaseDelay : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
private :
int m_iszNewTarget ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_changetarget , CTriggerChangeTarget )
TYPEDESCRIPTION CTriggerChangeTarget : : m_SaveData [ ] =
{
DEFINE_FIELD ( CTriggerChangeTarget , m_iszNewTarget , FIELD_STRING ) ,
} ;
IMPLEMENT_SAVERESTORE ( CTriggerChangeTarget , CBaseDelay )
void CTriggerChangeTarget : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " m_iszNewTarget " ) )
{
m_iszNewTarget = ALLOC_STRING ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseDelay : : KeyValue ( pkvd ) ;
}
void CTriggerChangeTarget : : Spawn ( void )
{
}
void CTriggerChangeTarget : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
CBaseEntity * pTarget = UTIL_FindEntityByString ( NULL , " targetname " , STRING ( pev - > target ) ) ;
if ( pTarget )
{
pTarget - > pev - > target = m_iszNewTarget ;
CBaseMonster * pMonster = pTarget - > MyMonsterPointer ( ) ;
if ( pMonster )
{
pMonster - > m_pGoalEnt = NULL ;
}
}
}
# define SF_CAMERA_PLAYER_POSITION 1
# define SF_CAMERA_PLAYER_TARGET 2
# define SF_CAMERA_PLAYER_TAKECONTROL 4
class CTriggerCamera : public CBaseDelay
{
public :
void Spawn ( void ) ;
void KeyValue ( KeyValueData * pkvd ) ;
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void EXPORT FollowTarget ( void ) ;
void EXPORT CoopThink ( void ) ;
void EXPORT CoopFollowTarget ( void ) ;
void Move ( void ) ;
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
virtual int ObjectCaps ( void ) { return CBaseEntity : : ObjectCaps ( ) & ~ FCAP_ACROSS_TRANSITION ; }
static TYPEDESCRIPTION m_SaveData [ ] ;
EHANDLE m_hPlayer ;
EHANDLE m_hTarget ;
CBaseEntity * m_pentPath ;
int m_sPath ;
float m_flWait ;
float m_flReturnTime ;
float m_flStopTime ;
float m_moveDistance ;
float m_targetSpeed ;
float m_initialSpeed ;
float m_acceleration ;
float m_deceleration ;
int m_state ;
} ;
LINK_ENTITY_TO_CLASS ( trigger_camera , CTriggerCamera )
// Global Savedata for changelevel friction modifier
TYPEDESCRIPTION CTriggerCamera : : m_SaveData [ ] =
{
DEFINE_FIELD ( CTriggerCamera , m_hPlayer , FIELD_EHANDLE ) ,
DEFINE_FIELD ( CTriggerCamera , m_hTarget , FIELD_EHANDLE ) ,
DEFINE_FIELD ( CTriggerCamera , m_pentPath , FIELD_CLASSPTR ) ,
DEFINE_FIELD ( CTriggerCamera , m_sPath , FIELD_STRING ) ,
DEFINE_FIELD ( CTriggerCamera , m_flWait , FIELD_FLOAT ) ,
DEFINE_FIELD ( CTriggerCamera , m_flReturnTime , FIELD_TIME ) ,
DEFINE_FIELD ( CTriggerCamera , m_flStopTime , FIELD_TIME ) ,
DEFINE_FIELD ( CTriggerCamera , m_moveDistance , FIELD_FLOAT ) ,
DEFINE_FIELD ( CTriggerCamera , m_targetSpeed , FIELD_FLOAT ) ,
DEFINE_FIELD ( CTriggerCamera , m_initialSpeed , FIELD_FLOAT ) ,
DEFINE_FIELD ( CTriggerCamera , m_acceleration , FIELD_FLOAT ) ,
DEFINE_FIELD ( CTriggerCamera , m_deceleration , FIELD_FLOAT ) ,
DEFINE_FIELD ( CTriggerCamera , m_state , FIELD_INTEGER ) ,
} ;
IMPLEMENT_SAVERESTORE ( CTriggerCamera , CBaseDelay )
void CTriggerCamera : : Spawn ( void )
{
pev - > movetype = MOVETYPE_NOCLIP ;
pev - > solid = SOLID_NOT ; // Remove model & collisions
pev - > renderamt = 0 ; // The engine won't draw this model if this is set to 0 and blending is on
pev - > rendermode = kRenderTransTexture ;
m_initialSpeed = pev - > speed ;
if ( m_acceleration = = 0 )
m_acceleration = 500 ;
if ( m_deceleration = = 0 )
m_deceleration = 500 ;
}
void CTriggerCamera : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " wait " ) )
{
m_flWait = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " moveto " ) )
{
m_sPath = ALLOC_STRING ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " acceleration " ) )
{
m_acceleration = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " deceleration " ) )
{
m_deceleration = atof ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseDelay : : KeyValue ( pkvd ) ;
}
void CTriggerCamera : : CoopThink ( )
{
m_hPlayer = NULL ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * plr = ( CBasePlayer * ) UTIL_PlayerByIndex ( i ) ;
// for all players
if ( plr & & plr - > IsPlayer ( ) )
{
m_hPlayer = plr ;
SET_VIEW ( plr - > edict ( ) , edict ( ) ) ;
SET_MODEL ( ENT ( pev ) , STRING ( plr - > pev - > model ) ) ;
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TAKECONTROL ) )
{
( ( CBasePlayer * ) plr ) - > EnableControl ( FALSE ) ;
}
}
}
if ( ! m_hPlayer )
{
SetThink ( & CTriggerCamera : : CoopThink ) ;
pev - > nextthink = gpGlobals - > time + 1 ;
return ;
}
m_flReturnTime = gpGlobals - > time + m_flWait ;
pev - > speed = m_initialSpeed ;
m_targetSpeed = m_initialSpeed ;
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TARGET ) )
{
m_hTarget = m_hPlayer ;
}
else
{
m_hTarget = GetNextTarget ( ) ;
}
// Nothing to look at!
if ( m_hTarget = = NULL )
{
return ;
}
if ( m_sPath )
{
m_pentPath = Instance ( FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( m_sPath ) ) ) ;
}
else
{
m_pentPath = NULL ;
}
m_flStopTime = gpGlobals - > time ;
if ( m_pentPath )
{
if ( m_pentPath - > pev - > speed ! = 0 )
m_targetSpeed = m_pentPath - > pev - > speed ;
m_flStopTime + = m_pentPath - > GetDelay ( ) ;
}
// copy over player information
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_POSITION ) )
{
CBaseEntity * plr = m_hPlayer ;
UTIL_SetOrigin ( pev , plr - > pev - > origin + plr - > pev - > view_ofs ) ;
pev - > angles . x = - plr - > pev - > angles . x ;
pev - > angles . y = plr - > pev - > angles . y ;
pev - > angles . z = 0 ;
pev - > velocity = plr - > pev - > velocity ;
}
else
{
pev - > velocity = Vector ( 0 , 0 , 0 ) ;
}
// follow the player down
SetThink ( & CTriggerCamera : : CoopFollowTarget ) ;
pev - > nextthink = gpGlobals - > time ;
m_moveDistance = 0 ;
Move ( ) ;
}
void CTriggerCamera : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
if ( ! ShouldToggle ( useType , m_state ) )
return ;
// Toggle state
m_state = ! m_state ;
if ( m_state = = 0 )
{
m_flReturnTime = gpGlobals - > time ;
return ;
}
if ( mp_coop . value )
{
CoopThink ( ) ;
return ;
}
if ( ! pActivator | | ! pActivator - > IsPlayer ( ) )
{
pActivator = CBaseEntity : : Instance ( g_engfuncs . pfnPEntityOfEntIndex ( 1 ) ) ;
}
if ( ! pActivator )
return ;
m_hPlayer = pActivator ;
m_flReturnTime = gpGlobals - > time + m_flWait ;
pev - > speed = m_initialSpeed ;
m_targetSpeed = m_initialSpeed ;
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TARGET ) )
{
m_hTarget = m_hPlayer ;
}
else
{
m_hTarget = GetNextTarget ( ) ;
}
// Nothing to look at!
if ( m_hTarget = = NULL )
{
return ;
}
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TAKECONTROL ) )
{
( ( CBasePlayer * ) pActivator ) - > EnableControl ( FALSE ) ;
}
if ( m_sPath )
{
m_pentPath = Instance ( FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( m_sPath ) ) ) ;
}
else
{
m_pentPath = NULL ;
}
m_flStopTime = gpGlobals - > time ;
if ( m_pentPath )
{
if ( m_pentPath - > pev - > speed ! = 0 )
m_targetSpeed = m_pentPath - > pev - > speed ;
m_flStopTime + = m_pentPath - > GetDelay ( ) ;
}
// copy over player information
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_POSITION ) )
{
UTIL_SetOrigin ( pev , pActivator - > pev - > origin + pActivator - > pev - > view_ofs ) ;
pev - > angles . x = - pActivator - > pev - > angles . x ;
pev - > angles . y = pActivator - > pev - > angles . y ;
pev - > angles . z = 0 ;
pev - > velocity = pActivator - > pev - > velocity ;
}
else
{
pev - > velocity = Vector ( 0 , 0 , 0 ) ;
}
SET_VIEW ( pActivator - > edict ( ) , edict ( ) ) ;
SET_MODEL ( ENT ( pev ) , STRING ( pActivator - > pev - > model ) ) ;
// follow the player down
SetThink ( & CTriggerCamera : : FollowTarget ) ;
pev - > nextthink = gpGlobals - > time ;
m_moveDistance = 0 ;
Move ( ) ;
}
void CTriggerCamera : : FollowTarget ( )
{
if ( m_hPlayer = = NULL )
return ;
if ( m_hTarget = = NULL | | m_flReturnTime < gpGlobals - > time )
{
if ( m_hPlayer - > IsAlive ( ) )
{
SET_VIEW ( m_hPlayer - > edict ( ) , m_hPlayer - > edict ( ) ) ;
( ( CBasePlayer * ) ( ( CBaseEntity * ) m_hPlayer ) ) - > EnableControl ( TRUE ) ;
}
SUB_UseTargets ( this , USE_TOGGLE , 0 ) ;
pev - > avelocity = Vector ( 0 , 0 , 0 ) ;
m_state = 0 ;
return ;
}
Vector vecGoal = UTIL_VecToAngles ( m_hTarget - > pev - > origin - pev - > origin ) ;
vecGoal . x = - vecGoal . x ;
if ( pev - > angles . y > 360 )
pev - > angles . y - = 360 ;
if ( pev - > angles . y < 0 )
pev - > angles . y + = 360 ;
float dx = vecGoal . x - pev - > angles . x ;
float dy = vecGoal . y - pev - > angles . y ;
if ( dx < - 180 )
dx + = 360 ;
if ( dx > 180 )
dx = dx - 360 ;
if ( dy < - 180 )
dy + = 360 ;
if ( dy > 180 )
dy = dy - 360 ;
pev - > avelocity . x = dx * 40 * gpGlobals - > frametime ;
pev - > avelocity . y = dy * 40 * gpGlobals - > frametime ;
if ( ! ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TAKECONTROL ) ) )
{
pev - > velocity = pev - > velocity * 0.8 ;
if ( pev - > velocity . Length ( ) < 10.0 )
pev - > velocity = g_vecZero ;
}
pev - > nextthink = gpGlobals - > time ;
Move ( ) ;
}
void CTriggerCamera : : CoopFollowTarget ( )
{
// if( m_hPlayer == NULL )
// return;
if ( m_hTarget = = NULL | | m_flReturnTime < gpGlobals - > time )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * plr = ( CBasePlayer * ) UTIL_PlayerByIndex ( i ) ;
// for all players
if ( plr & & plr - > IsPlayer ( ) )
{
SET_VIEW ( plr - > edict ( ) , plr - > edict ( ) ) ;
plr - > EnableControl ( TRUE ) ;
}
}
SUB_UseTargets ( this , USE_TOGGLE , 0 ) ;
pev - > avelocity = Vector ( 0 , 0 , 0 ) ;
m_state = 0 ;
return ;
}
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * plr = ( CBasePlayer * ) UTIL_PlayerByIndex ( i ) ;
// for all players
if ( plr & & plr - > IsPlayer ( ) )
{
SET_VIEW ( plr - > edict ( ) , edict ( ) ) ;
if ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TAKECONTROL ) )
{
plr - > EnableControl ( FALSE ) ;
}
}
}
Vector vecGoal = UTIL_VecToAngles ( m_hTarget - > pev - > origin - pev - > origin ) ;
vecGoal . x = - vecGoal . x ;
if ( pev - > angles . y > 360 )
pev - > angles . y - = 360 ;
if ( pev - > angles . y < 0 )
pev - > angles . y + = 360 ;
float dx = vecGoal . x - pev - > angles . x ;
float dy = vecGoal . y - pev - > angles . y ;
if ( dx < - 180 )
dx + = 360 ;
if ( dx > 180 )
dx = dx - 360 ;
if ( dy < - 180 )
dy + = 360 ;
if ( dy > 180 )
dy = dy - 360 ;
pev - > avelocity . x = dx * 40 * gpGlobals - > frametime ;
pev - > avelocity . y = dy * 40 * gpGlobals - > frametime ;
if ( ! ( FBitSet ( pev - > spawnflags , SF_CAMERA_PLAYER_TAKECONTROL ) ) )
{
pev - > velocity = pev - > velocity * 0.8 ;
if ( pev - > velocity . Length ( ) < 10.0 )
pev - > velocity = g_vecZero ;
}
pev - > nextthink = gpGlobals - > time ;
Move ( ) ;
}
void CTriggerCamera : : Move ( )
{
// Not moving on a path, return
if ( ! m_pentPath )
return ;
// Subtract movement from the previous frame
m_moveDistance - = pev - > speed * gpGlobals - > frametime ;
// Have we moved enough to reach the target?
if ( m_moveDistance < = 0 )
{
// Fire the passtarget if there is one
if ( m_pentPath - > pev - > message )
{
FireTargets ( STRING ( m_pentPath - > pev - > message ) , this , this , USE_TOGGLE , 0 ) ;
if ( FBitSet ( m_pentPath - > pev - > spawnflags , SF_CORNER_FIREONCE ) )
m_pentPath - > pev - > message = 0 ;
}
// Time to go to the next target
m_pentPath = m_pentPath - > GetNextTarget ( ) ;
// Set up next corner
if ( ! m_pentPath )
{
pev - > velocity = g_vecZero ;
}
else
{
if ( m_pentPath - > pev - > speed ! = 0 )
m_targetSpeed = m_pentPath - > pev - > speed ;
Vector delta = m_pentPath - > pev - > origin - pev - > origin ;
m_moveDistance = delta . Length ( ) ;
pev - > movedir = delta . Normalize ( ) ;
m_flStopTime = gpGlobals - > time + m_pentPath - > GetDelay ( ) ;
}
}
if ( m_flStopTime > gpGlobals - > time )
pev - > speed = UTIL_Approach ( 0 , pev - > speed , m_deceleration * gpGlobals - > frametime ) ;
else
pev - > speed = UTIL_Approach ( m_targetSpeed , pev - > speed , m_acceleration * gpGlobals - > frametime ) ;
float fraction = 2 * gpGlobals - > frametime ;
pev - > velocity = ( ( pev - > movedir * pev - > speed ) * fraction ) + ( pev - > velocity * ( 1 - fraction ) ) ;
}