This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/server/ents/basefunc.cpp

2449 lines
73 KiB
C++

//=======================================================================
// Copyright (C) Shambler Team 2004
// basefunc.cpp - brush based entities: tanks,
// cameras, vehicles etc
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "sfx.h"
#include "decals.h"
#include "client.h"
#include "saverestore.h"
#include "gamerules.h"
#include "basebrush.h"
#include "defaults.h"
#include "player.h"
//=======================================================================
// STATIC BRUSHES
//=======================================================================
//=======================================================================
// func_wall - standard not moving wall. affect to physics
//=======================================================================
class CFuncWall : public CBaseBrush
{
public:
void Spawn( void );
void TurnOn( void );
void TurnOff( void );
void Precache( void ){ CBaseBrush::Precache(); }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
};
LINK_ENTITY_TO_CLASS( func_wall, CFuncWall );
LINK_ENTITY_TO_CLASS( func_wall_toggle, CFuncWall );
LINK_ENTITY_TO_CLASS( func_illusionary, CFuncWall );
void CFuncWall :: Spawn( void )
{
CBaseBrush::Spawn();
if(FClassnameIs(pev, "func_illusionary" ))
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
UTIL_SetModel( ENT( pev ), pev->model );
// smart field system ®
if( !FStringNull( pev->angles ) && FStringNull( pev->origin ))
{
pev->angles = g_vecZero;
ALERT( at_console, "\n======/Xash SmartFiled System/======\n\n");
ALERT( at_console, "Create brush origin for %s,\n", STRING( pev->classname ));
ALERT( at_console, "if we want correctly set angles\n\n" );
}
pev->angles[1] = 0 - pev->angles[1];
if( pev->spawnflags & SF_START_ON ) TurnOff();
else TurnOn();
}
void CFuncWall :: TurnOff( void )
{
if(FClassnameIs(pev, "func_wall_toggle" ))
{
pev->solid = SOLID_NOT;
pev->effects |= EF_NODRAW;
UTIL_SetOrigin( this, pev->origin );
}
else pev->frame = 0;
m_iState = STATE_OFF;
}
void CFuncWall :: TurnOn( void )
{
if(FClassnameIs(pev, "func_wall_toggle" ))
{
pev->solid = SOLID_BSP;
pev->effects &= ~EF_NODRAW;
UTIL_SetOrigin( this, pev->origin );
}
else pev->frame = 1;
m_iState = STATE_ON;
}
void CFuncWall :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (useType == USE_TOGGLE)
{
if(m_iState == STATE_OFF) useType = USE_ON;
else useType = USE_OFF;
}
if (useType == USE_ON) TurnOn();
else if (useType == USE_OFF)TurnOff();
else if (useType == USE_SHOWINFO)
{
DEBUGHEAD;
Msg( "State: %s, Visible: %s\n", GetStringForState( GetState()), pev->effects & EF_NODRAW ? "No" : "Yes");
Msg( "Contents: %s, Texture frame: %.f\n", GetContentsString( pev->skin ), pev->frame);
}
}
//=======================================================================
// func_breakable - generic breakable brush.
//=======================================================================
class CFuncBreakable : public CBaseBrush
{
public:
void Spawn( void );
void Precache( void ){ CBaseBrush::Precache(); }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ){ if (IsBreakable()) Die(); }
};
LINK_ENTITY_TO_CLASS( func_breakable, CFuncBreakable );
void CFuncBreakable :: Spawn( void )
{
CBaseBrush::Spawn();
UTIL_SetModel( ENT(pev), pev->model );
if ( FBitSet( pev->spawnflags, SF_BREAK_TRIGGER_ONLY )) pev->takedamage = DAMAGE_NO;
else pev->takedamage = DAMAGE_YES;
}
//=======================================================================
// func_monsterclip - invisible contents that monster can't walk across
//=======================================================================
class CFuncClip : public CBaseEntity
{
public:
void Spawn( void )
{
pev->effects = EF_NODRAW;
pev->contents = CONTENTS_MONSTERCLIP;
UTIL_SetModel( ENT(pev), pev->model );
}
};
LINK_ENTITY_TO_CLASS( func_monsterclip, CFuncClip );
//=======================================================================
// func_lamp - switchable brush light.
//=======================================================================
class CFuncLamp : public CBaseBrush
{
public:
void Spawn( void );
void Precache( void ){ CBaseBrush::Precache(); }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
void EXPORT Flicker( void );
void Die( void );
};
LINK_ENTITY_TO_CLASS( func_lamp, CFuncLamp );
void CFuncLamp :: Spawn( void )
{
m_Material = Glass;
CBaseBrush::Spawn();
UTIL_SetModel( ENT(pev), pev->model );
if(pev->spawnflags & SF_START_ON) Use( this, this, USE_ON, 0 );
else Use( this, this, USE_OFF, 0 );
}
void CFuncLamp :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType )) return;
if(m_iState == STATE_DEAD && useType != USE_SHOWINFO)return;//lamp is broken
if (useType == USE_TOGGLE)
{
if(m_iState == STATE_OFF) useType = USE_ON;
else useType = USE_OFF;
}
if (useType == USE_ON)
{
if(m_flDelay)//make flickering delay
{
pev->frame = 0;//light texture is on
m_iState = STATE_TURN_ON;
LIGHT_STYLE(m_iStyle, "mmamammmmammamamaaamammma");
SetThink( Flicker );
SetNextThink(m_flDelay);
}
else
{ //instantly enable
m_iState = STATE_ON;
pev->frame = 0;//light texture is on
LIGHT_STYLE(m_iStyle, "k");
UTIL_FireTargets( pev->target, this, this, USE_ON );//lamp enable
}
}
else if (useType == USE_OFF)
{
pev->frame = 1;//light texture is off
LIGHT_STYLE(m_iStyle, "a");
UTIL_FireTargets( pev->target, this, this, USE_OFF );//lamp disable
m_iState = STATE_OFF;
}
else if (useType == USE_SET) Die();
else if (useType == USE_SHOWINFO)
{
DEBUGHEAD;
Msg( "State: %s, Light style %d\n", GetStringForState( GetState()), m_iStyle);
Msg( "Texture frame: %.f. Health %.f\n", pev->frame, pev->health);
}
}
void CFuncLamp::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
if(m_iState == STATE_DEAD) return;
const char *pTextureName;
Vector start = pevAttacker->origin + pevAttacker->view_ofs;
Vector end = start + vecDir * 1024;
edict_t *pWorld = ptr->pHit;
if ( pWorld )pTextureName = TRACE_TEXTURE( pWorld, start, end );
if(strstr(pTextureName, "+0~") || strstr(pTextureName, "~") )//take damage only at light texture
{
UTIL_Sparks( ptr->vecEndPos );
pev->oldorigin = ptr->vecEndPos;//save last point of damage
pev->takedamage = DAMAGE_YES;//inflict damage only at light texture
}
else pev->takedamage = DAMAGE_NO;
CBaseLogic::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
int CFuncLamp::TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
{
if ( bitsDamageType & DMG_BLAST ) flDamage *= 3;
else if ( bitsDamageType & DMG_CLUB) flDamage *= 2;
else if ( bitsDamageType & DMG_SONIC ) flDamage *= 1.2;
else if ( bitsDamageType & DMG_SHOCK ) flDamage *= 10;//!!!! over voltage
else if ( bitsDamageType & DMG_BULLET) flDamage *= 0.5;//half damage at bullet
pev->health -= flDamage;//calculate health
if (pev->health <= 0)
{
Die();
return 0;
}
CBaseBrush::DamageSound();
return 1;
}
void CFuncLamp::Die( void )
{
//lamp is random choose die style
if(m_iState == STATE_OFF)
{
pev->frame = 1;//light texture is off
LIGHT_STYLE(m_iStyle, "a");
DontThink();
}
else
{ //simple randomization
pev->impulse = RANDOM_LONG(1, 2);
SetThink( Flicker );
SetNextThink( 0.1 + (RANDOM_LONG(1, 2) * 0.1));
}
m_iState = STATE_DEAD;//lamp is die
pev->health = 0;//set health to NULL
pev->takedamage = DAMAGE_NO;
UTIL_FireTargets( pev->target, this, this, USE_OFF );//lamp broken
switch ( RANDOM_LONG(0,1))
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/glass/bustglass1.wav", 0.7, ATTN_IDLE); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/glass/bustglass2.wav", 0.8, ATTN_IDLE); break;
}
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
//spawn glass gibs
SFX_MakeGibs( m_idShard, vecSpot, pev->size, g_vecZero, 50, BREAK_GLASS);
}
void CFuncLamp :: Flicker( void )
{
if(m_iState == STATE_TURN_ON)//flickering on enable
{
LIGHT_STYLE(m_iStyle, "k");
UTIL_FireTargets( pev->target, this, this, USE_ON );//Lamp enabled
m_iState = STATE_ON;
DontThink();
return;
}
if(pev->impulse == 1)//fadeout on break
{
pev->frame = 1;//light texture is off
LIGHT_STYLE(m_iStyle, "a");
SetThink( NULL );
return;
}
if(pev->impulse == 2)//broken flickering
{
//make different flickering
switch ( RANDOM_LONG(0,3))
{
case 0: LIGHT_STYLE(m_iStyle, "abcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); break;
case 1: LIGHT_STYLE(m_iStyle, "acaaabaaaaaaaaaaaaaaaaaaaaaaaaaaa"); break;
case 2: LIGHT_STYLE(m_iStyle, "aaafbaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); break;
case 3: LIGHT_STYLE(m_iStyle, "aaaaaaaaaaaaagaaaaaaaaacaaaacaaaa"); break;
}
pev->frags = RANDOM_FLOAT(0.5, 10);
UTIL_Sparks( pev->oldorigin );
switch ( RANDOM_LONG(0, 2) )
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/spark1.wav", 0.4, ATTN_IDLE); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/spark2.wav", 0.3, ATTN_IDLE); break;
case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/spark3.wav", 0.35, ATTN_IDLE); break;
}
if(pev->frags > 6.5) pev->impulse = 1;//stop sparking obsolete
}
SetNextThink(pev->frags);
}
//=======================================================================
// func_conveyor - conveyor belt
//=======================================================================
class CFuncConveyor : public CBaseBrush
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void UpdateSpeed( float speed );
void TogglePhysic( BOOL enable );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
};
LINK_ENTITY_TO_CLASS( func_conveyor, CFuncConveyor );
void CFuncConveyor :: Spawn( void )
{
CBaseBrush::Spawn();
UTIL_LinearVector( this ); // movement direction
if( !m_pParent ) pev->flags |= FL_WORLDBRUSH;
UTIL_SetModel( ENT(pev), STRING( pev->model ));
if( pev->spawnflags & SF_NOTSOLID )
TogglePhysic( FALSE );
else TogglePhysic( TRUE );
// smart field system ®
if( pev->speed == 0 ) pev->speed = 100;
pev->frags = pev->speed; // save initial speed
if( pev->spawnflags & SF_START_ON || FStringNull( pev->targetname ))
Use( this, this, USE_ON, 0 );
}
void CFuncConveyor :: TogglePhysic( BOOL enable )
{
if( enable )
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_CONVEYOR;
}
else
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
}
}
void CFuncConveyor :: UpdateSpeed( float speed )
{
// save speed into framerate, and direction into body
pev->framerate = pev->speed;
pev->body = (pev->speed < 0 );
UTIL_FireTargets( pev->target, this, this, USE_TOGGLE, speed ); // conveyer change speed
}
void CFuncConveyor :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType )) return;
if( useType == USE_TOGGLE )
{
if( m_iState == STATE_ON )
useType = USE_OFF;
else useType = USE_ON;
}
if( useType == USE_ON )
{
UpdateSpeed( pev->speed );
if(!( pev->spawnflags & SF_NOTSOLID )) //don't push
pev->movetype = MOVETYPE_CONVEYOR;
m_iState = STATE_ON;
}
else if( useType == USE_OFF )
{
UpdateSpeed( 0.1 );
pev->movetype = MOVETYPE_NONE;
m_iState = STATE_OFF;
}
else if( useType == USE_SET ) // set new speed
{
if( value ) pev->speed = value; // set new speed ( can be negative )
else pev->speed = -pev->speed; // just reverse
if( m_iState == STATE_ON ) UpdateSpeed( pev->speed );
}
else if( useType == USE_RESET ) // restore initial speed
{
pev->speed = pev->frags;
if( m_iState == STATE_ON ) UpdateSpeed( pev->speed );
}
else if( useType == USE_SHOWINFO )
{
ALERT( at_console, "======/Xash Debug System/======\n" );
ALERT( at_console, "classname: %s\n", STRING( pev->classname ));
ALERT( at_console, "State: %s, Conveyor speed %f\n", GetStringForState( GetState()), pev->speed );
SHIFT;
}
}
//=======================================================================
// func_mirror - breakable mirror brush
//=======================================================================
class CFuncMirror : public CBaseBrush
{
public:
void Spawn( void );
void StartMessage( CBasePlayer *pPlayer );
void Precache( void ){ CBaseBrush::Precache(); }
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual STATE GetState( void ) { return pev->body ? STATE_ON:STATE_OFF; };
};
LINK_ENTITY_TO_CLASS( func_mirror, CFuncMirror );
void CFuncMirror::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "alpha"))
{
pev->sequence = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseBrush::KeyValue( pkvd );
}
void CFuncMirror :: Spawn( void )
{
m_Material = Glass;
CBaseBrush::Spawn();
if (!m_pParent) pev->flags = FL_WORLDBRUSH;
if(IsMultiplayer())
{
ALERT(at_console, "\n======/Xash SmartFiled System/======\n\n");
ALERT(at_console, "Sorry, %s don't working in multiplayer\nPlease, use static world mirrors\n", STRING(pev->classname));
SHIFT;
}
UTIL_SetModel( ENT(pev), pev->model );
if(pev->spawnflags & SF_NOTSOLID)//make illusionary wall
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
pev->body = 1;
}
void CFuncMirror :: StartMessage( CBasePlayer *pPlayer )
{
MESSAGE_BEGIN( MSG_ONE, gmsg.AddMirror, NULL, pPlayer->pev );
WRITE_SHORT( entindex() );
MESSAGE_END();
}
void CFuncMirror :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if( useType == USE_TOGGLE )
{
if( pev->body )
useType = USE_OFF;
else useType = USE_ON;
}
if( useType == USE_ON ) pev->body = TRUE;
if( useType == USE_OFF ) pev->body = FALSE;
if( useType == USE_SET ) Die();
if( useType == USE_SHOWINFO )
{
DEBUGHEAD;
ALERT(at_console, "State: %s, alpha %d\n", GetStringForState( GetState()), pev->sequence);
ALERT(at_console, "Renderamt: %.f, Health %.f\n", pev->renderamt, pev->health);
}
}
//=======================================================================
// func_monitor - A monitor that renders the view from a camera entity.
//=======================================================================
class CFuncMonitor : public CBaseBrush
{
public:
void Spawn( void );
void Precache( void ){ CBaseBrush::Precache(); }
void StartMessage( CBasePlayer *pPlayer );
void ChangeCamera( string_t newcamera );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual BOOL IsFuncScreen( void ) { return TRUE; }
virtual STATE GetState( void ) { return pev->body ? STATE_ON:STATE_OFF; };
virtual int ObjectCaps( void );
BOOL OnControls( entvars_t *pevTest );
CBaseEntity *pCamera;
};
LINK_ENTITY_TO_CLASS( func_monitor, CFuncMonitor );
void CFuncMonitor::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "camera"))
{
pev->target = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
if (FStrEq(pkvd->szKeyName, "type"))
{
pev->impulse = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
if (FStrEq(pkvd->szKeyName, "mode"))
{
pev->skin = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseBrush::KeyValue( pkvd );
}
void CFuncMonitor :: Spawn()
{
m_Material = Computer;
CBaseBrush::Spawn();
if(pev->spawnflags & SF_NOTSOLID)//make illusionary wall
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
UTIL_SetModel(ENT(pev), pev->model );
//enable monitor
if(pev->spawnflags & SF_START_ON)pev->body = 1;
}
int CFuncMonitor :: ObjectCaps( void )
{
if (pev->impulse)
return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE;
else return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION);
}
void CFuncMonitor::StartMessage( CBasePlayer *pPlayer )
{
//send monitor index
MESSAGE_BEGIN( MSG_ONE, gmsg.AddScreen, NULL, pPlayer->pev );
WRITE_SHORT( entindex() );
MESSAGE_END();
ChangeCamera( pev->target );
}
void CFuncMonitor::ChangeCamera( string_t newcamera )
{
pCamera = UTIL_FindEntityByTargetname( NULL, STRING( newcamera ));
if( pCamera ) pev->sequence = pCamera->entindex();
else if( pev->body ) pev->frame = 1; // noise if not found camera
// member newcamera name
pev->target = newcamera;
}
void CFuncMonitor :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType )) return;
if (useType == USE_TOGGLE)
{
if(pev->body) useType = USE_OFF;
else useType = USE_ON;
}
if (useType == USE_ON)pev->body = 1;
else if (useType == USE_OFF)
{
pev->body = 0;
pev->frame = 0;
}
else if (useType == USE_SET)
{
if(pActivator->IsPlayer())
{
if( value ) UTIL_SetView(pActivator, iStringNull, 0 );
else if(pev->body)
{
UTIL_SetView( pev->target, CAMERA_ON );
m_pController = (CBasePlayer*)pActivator;
m_pController->m_pMonitor = this;
if (m_pParent)
m_vecPlayerPos = m_pController->pev->origin - m_pParent->pev->origin;
else m_vecPlayerPos = m_pController->pev->origin;
}
}
}
else if (useType == USE_SHOWINFO)
{
DEBUGHEAD;
Msg( "State: %s, Mode: %s\n", GetStringForState( GetState()), pev->skin ? "Color" : "B&W");
Msg( "Type: %s. Camera Name: %s\n", pev->impulse ? "Duke Nukem Style" : "Half-Life 2 Style", STRING( pev->target) );
}
}
BOOL CFuncMonitor :: OnControls( entvars_t *pevTest )
{
if(m_pParent && ((m_vecPlayerPos + m_pParent->pev->origin) - pevTest->origin).Length() <= 30)
return TRUE;
else if((m_vecPlayerPos - pevTest->origin).Length() <= 30 )
return TRUE;
return FALSE;
}
//=======================================================================
// func_teleport - classic XASH portal
//=======================================================================
class CFuncTeleport : public CFuncMonitor
{
public:
void Spawn( void );
void Touch ( CBaseEntity *pOther );
void KeyValue( KeyValueData *pkvd );
void StartMessage( CBasePlayer *pPlayer );
};
LINK_ENTITY_TO_CLASS( func_portal, CFuncTeleport );
LINK_ENTITY_TO_CLASS( trigger_teleport, CFuncTeleport );
void CFuncTeleport :: Spawn( void )
{
pev->solid = SOLID_TRIGGER;
pev->movetype = MOVETYPE_NONE;
UTIL_SetModel(ENT(pev), pev->model);
if (FClassnameIs(pev, "func_portal")) //portal mode
{
pev->impulse = 0;//can't use teleport
pev->body = 1;//enabled
}
else SetBits( pev->effects, EF_NODRAW );//classic mode
}
void CFuncTeleport::StartMessage( CBasePlayer *pPlayer )
{
//send portal index
MESSAGE_BEGIN( MSG_ONE, gmsg.AddPortal, NULL, pPlayer->pev );
WRITE_SHORT( entindex() );
MESSAGE_END();
ChangeCamera( pev->target );
}
void CFuncTeleport :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "landmark"))
{
pev->message = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "firetarget"))
{
pev->netname = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
void CFuncTeleport :: Touch( CBaseEntity *pOther )
{
entvars_t* pevToucher = pOther->pev;
CBaseEntity *pTarget = NULL;
// only teleport monsters or clients or projectiles
if( !FBitSet( pevToucher->flags, FL_CLIENT|FL_MONSTER|FL_PROJECTILE ) && !pOther->IsPushable())
return;
if( IsLockedByMaster( pOther )) return;
pTarget = UTIL_FindEntityByTargetname( pTarget, STRING(pev->target) );
if ( !pTarget ) return;
CBaseEntity *pLandmark = UTIL_FindEntityByTargetname( NULL, STRING(pev->message) );
if ( pLandmark )
{
Vector vecOriginOffs = pTarget->pev->origin - pLandmark->pev->origin;
// do we need to rotate the entity?
if ( pLandmark->pev->angles != pTarget->pev->angles )
{
Vector vecVA;
float ydiff = pTarget->pev->angles.y - pLandmark->pev->angles.y;
// set new angle to face
pOther->pev->angles.y -= ydiff;
if( pOther->IsPlayer())
{
pOther->pev->angles.x = pOther->pev->viewangles.x;
SET_FIXANGLE( ENT( pOther->pev ), pOther->pev->angles );
pOther->pev->fixangle = TRUE;
}
// set new velocity
vecVA = UTIL_VecToAngles(pOther->pev->velocity);
vecVA.y += ydiff;
UTIL_MakeVectors(vecVA);
pOther->pev->velocity = gpGlobals->v_forward * pOther->pev->velocity.Length();
pOther->pev->velocity.z = -pOther->pev->velocity.z;
// set new origin
Vector vecPlayerOffs = pOther->pev->origin - pLandmark->pev->origin;
vecVA = UTIL_VecToAngles(vecPlayerOffs);
UTIL_MakeVectors(vecVA);
vecVA.y += ydiff;
UTIL_MakeVectors(vecVA);
Vector vecPlayerOffsNew = gpGlobals->v_forward * vecPlayerOffs.Length();
vecPlayerOffsNew.z = -vecPlayerOffsNew.z;
vecOriginOffs = vecOriginOffs + vecPlayerOffsNew - vecPlayerOffs;
}
UTIL_SetOrigin( pOther, pOther->pev->origin + vecOriginOffs );
}
else
{
Vector tmp = pTarget->pev->origin;
if( pOther->IsPlayer( ))
tmp.z -= pOther->pev->mins.z; // make origin adjustments
tmp.z++;
UTIL_SetOrigin( pOther, tmp );
pOther->pev->angles = pTarget->pev->angles;
pOther->pev->velocity = g_vecZero;
if( pOther->IsPlayer( ))
{
SET_FIXANGLE( ENT( pOther->pev ), pTarget->pev->angles );
pOther->pev->viewangles = pTarget->pev->angles;
pOther->pev->fixangle = TRUE;
}
}
ChangeCamera( pev->target ); // update PVS
pevToucher->flags &= ~FL_ONGROUND;
pevToucher->fixangle = TRUE;
pevToucher->teleport_time = gpGlobals->time + 0.1;
UTIL_FireTargets( pev->netname, pOther, this, USE_TOGGLE ); // fire target
}
//=======================================================================
// ANGULAR MOVING BRUSHES
//=======================================================================
//=======================================================================
// func_rotate - typically non moving wall. affect to physics
//=======================================================================
class CFuncRotating : public CBaseBrush
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData* pkvd);
void Touch ( CBaseEntity *pOther );
void Blocked( CBaseEntity *pOther );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think( void );
void PostActivate ( void );
void RampPitchVol ( void );
void SetSpeed( float newspeed );
void SetAngularImpulse( float impulse );
float flDegree;
};
LINK_ENTITY_TO_CLASS( func_rotating, CFuncRotating );
void CFuncRotating :: KeyValue( KeyValueData* pkvd)
{
if( FStrEq( pkvd->szKeyName, "spintime" ))
{
pev->frags = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
if( FStrEq( pkvd->szKeyName, "spawnorigin" ))
{
Vector tmp;
UTIL_StringToVector( tmp, pkvd->szValue );
if( tmp != g_vecZero ) pev->origin = tmp;
pkvd->fHandled = TRUE;
}
else CBaseBrush::KeyValue( pkvd );
}
void CFuncRotating :: Precache( void )
{
CBaseBrush::Precache();
int m_sounds = UTIL_LoadSoundPreset( m_iMoveSound );
switch( m_sounds )
{
case 1:
pev->noise3 = UTIL_PrecacheSound( "fans/fan1.wav" );
break;
case 2:
pev->noise3 = UTIL_PrecacheSound( "fans/fan2.wav" );
break;
case 3:
pev->noise3 = UTIL_PrecacheSound( "fans/fan3.wav" );
break;
case 4:
pev->noise3 = UTIL_PrecacheSound( "fans/fan4.wav" );
break;
case 5:
pev->noise3 = UTIL_PrecacheSound( "fans/fan5.wav" );
break;
case 0:
pev->noise3 = UTIL_PrecacheSound( "common/null.wav" );
break;
default:
pev->noise3 = UTIL_PrecacheSound( m_sounds );
break;
}
}
void CFuncRotating :: Spawn( void )
{
Precache();
CBaseBrush::Spawn();
if ( pev->spawnflags & 64 )
{
pev->solid = SOLID_NOT;
pev->contents = CONTENTS_NONE;
pev->movetype = MOVETYPE_PUSH;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
SetBits (pFlags, PF_ANGULAR);//enetity has angular velocity
m_iState = STATE_OFF;
m_pitch = PITCH_NORM - 1;
pev->dmg_save = 0;//cur speed is NULL
pev->button = pev->speed;//member start speed
AxisDir();
UTIL_SetOrigin(this, pev->origin);
UTIL_SetModel( ENT(pev), pev->model );
}
void CFuncRotating :: Touch ( CBaseEntity *pOther )
{
// don't hurt toucher - just apply velocity him
entvars_t *pevOther = pOther->pev;
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev) ).Normalize() * pev->dmg;
}
void CFuncRotating :: Blocked( CBaseEntity *pOther )
{
if(m_pParent && m_pParent->edict() && pFlags & PF_PARENTMOVE) m_pParent->Blocked( pOther );
UTIL_AssignAngles(this, pev->angles );
if ( gpGlobals->time < m_flBlockedTime)return;
m_flBlockedTime = gpGlobals->time + 0.1;
if((m_iState == STATE_OFF || m_iState == STATE_TURN_OFF) && pev->avelocity.Length() < 350)
{
SetAngularImpulse(-(pev->dmg_save + RANDOM_FLOAT( 5, 10)));
return;
}
if(pOther->TakeDamage( pev, pev, fabs(pev->speed), DMG_CRUSH ))//blocked entity died ?
{
SetAngularImpulse(-(pev->dmg_save + RANDOM_FLOAT( 10, 20)));
UTIL_FireTargets( pev->target, this, this, USE_SET );//damage
}
else if (RANDOM_LONG(0,1))
{
SetSpeed( 0 );//fan damaged - disable it
UTIL_FireTargets( pev->target, this, this, USE_SET );
//fan damaged( we can use counter and master for bloked broken fan)
}
if(!pOther->IsPlayer() && !pOther->IsMonster())//crash fan
{
if(IsBreakable())//if set strength - give damage myself
CBaseBrush::TakeDamage( pev, pev, fabs(pev->speed/100), DMG_CRUSH );//if we give damage
}
}
void CFuncRotating :: PostActivate ()
{
if( pev->spawnflags & SF_START_ON )
SetSpeed( pev->speed );
ClearBits( pev->spawnflags, SF_START_ON );
if(m_iState == STATE_ON ) // restore sound if needed
EMIT_SOUND_DYN( ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise3), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH | SND_CHANGE_VOL, m_pitch);
SetNextThink( 0.05 ); // force to think
}
void CFuncRotating :: RampPitchVol ( void )
{
float fpitch;
int pitch;
float speedfactor = fabs(pev->dmg_save/pev->button);
m_flVolume = speedfactor; //slowdown volume ramps down to 0
fpitch = PITCHMIN + (PITCHMAX - PITCHMIN) * speedfactor;
pitch = (int)fpitch;
if( pitch == PITCH_NORM ) pitch = PITCH_NORM-1;
// normalize volume and pitch
if( m_flVolume > 1.0 ) m_flVolume = 1.0;
if( m_flVolume < 0.0 ) m_flVolume = 0.0;
if( pitch < PITCHMIN ) pitch = PITCHMIN;
if( pitch > 255 ) pitch = 255;
m_pitch = pitch;//refresh pitch
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, (char *)STRING(pev->noise3), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH|SND_CHANGE_VOL, m_pitch );
}
void CFuncRotating :: SetSpeed( float newspeed )
{
float speedfactor = fabs(pev->dmg_save/pev->speed);//speedfactor
pev->armortype = gpGlobals->time; //starttime
pev->armorvalue = newspeed - pev->dmg_save; //speed offset
pev->scale = pev->dmg_save; //cur speed
//any speed change is turn on
//may be it's wrong, but nice working :)
if((pev->dmg_save > 0 && newspeed < 0) || (pev->dmg_save < 0 && newspeed > 0))//detect fast reverse
{
if(m_iState != STATE_OFF)pev->dmg_take = pev->frags * speedfactor;//fast reverse
else pev->dmg_take = pev->frags;//get normal time
m_iState = STATE_TURN_ON;
}
else if(newspeed == 0)
{
m_iState = STATE_TURN_OFF;
pev->dmg_take = pev->frags * 2.5 * speedfactor;//spin down is longer
}
else //just change speed, not zerocrossing
{
m_iState = STATE_TURN_ON;
pev->dmg_take = pev->frags;//get normal time
}
SetNextThink( 0.05 );//force to think
}
void CFuncRotating :: SetAngularImpulse( float impulse )
{
if( fabs(impulse) < 20)pev->scale = fabs(impulse / MAX_AVELOCITY);
else pev->scale = 0.01;//scale factor
m_iState = STATE_OFF;//impulse disable fan
pev->dmg_save = pev->dmg_save + impulse;//add our impulse to curspeed
//normalize avelocity
if(pev->dmg_save > MAX_AVELOCITY)pev->dmg_save = MAX_AVELOCITY;
pev->max_health = MatFrictionTable( m_Material);//set friction coefficient
SetNextThink(0.05);//pushable entity must thinking for apply velocity
}
void CFuncRotating :: Think( void )
{
if(m_iState == STATE_TURN_ON || m_iState == STATE_TURN_OFF)
{
flDegree = (gpGlobals->time - pev->armortype)/pev->dmg_take;
if (flDegree >= 1)//full spin
{
if(m_iState == STATE_TURN_ON)
{
m_iState = STATE_ON;
pev->dmg_save = pev->speed;//normalize speed
UTIL_SetAvelocity(this, pev->movedir * pev->speed);//set normalized avelocity
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise3), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH | SND_CHANGE_VOL, m_pitch);
UTIL_FireTargets( pev->target, this, this, USE_ON );//fan is full on
}
if(m_iState == STATE_TURN_OFF)
{
m_iState = STATE_OFF;
if (pev->movedir == Vector(0,0,1))
SetAngularImpulse(RANDOM_FLOAT(3.9, 6.8) * -pev->dmg_save);
//set small negative impulse for pretty phys effect :)
}
}
else
{
//calc new speed every frame
pev->dmg_save = pev->scale + pev->armorvalue * flDegree;
UTIL_SetAvelocity(this, pev->movedir * pev->dmg_save);
RampPitchVol(); //play pitched sound
}
}
if(m_iState == STATE_OFF)//apply impulse and friction
{
if(pev->dmg_save > 0)pev->dmg_save -= pev->max_health * pev->scale + 0.01;
else if(pev->dmg_save < 0)pev->dmg_save += pev->max_health * pev->scale + 0.01;
UTIL_SetAvelocity(this, pev->movedir * pev->dmg_save);
RampPitchVol(); //play pitched sound
}
//ALERT(at_console, "pev->avelocity %.2f %.2f %.2f\n", pev->avelocity.x, pev->avelocity.y, pev->avelocity.z);
SetNextThink( 0.05 );
if(pev->avelocity.Length() < 0.5)//watch for speed
{
UTIL_SetAvelocity(this, g_vecZero);//stop
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise3), 0, 0, SND_STOP, m_pitch);
DontThink();//break thinking
UTIL_FireTargets( pev->target, this, this, USE_OFF );//fan is full stopped
}
}
void CFuncRotating :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType )) return;
if (useType == USE_TOGGLE)
{
if(m_iState == STATE_TURN_OFF || m_iState == STATE_OFF) useType = USE_ON;
else useType = USE_OFF;
}
if (useType == USE_ON)SetSpeed( pev->speed );
else if(useType == USE_OFF)SetSpeed( 0 );
else if (useType == USE_SET)//reverse speed
{
//write new speed
if(value)
{
if(pev->speed < 0)
pev->speed = -value;
if(pev->speed > 0)
pev->speed = value;
}
else pev->speed = -pev->speed;//just reverse
//apply speed immediately if fan no off or not turning off
if(m_iState != STATE_OFF && m_iState != STATE_TURN_OFF)SetSpeed( pev->speed );
}
else if (useType == USE_SHOWINFO)
{
DEBUGHEAD;
ALERT(at_console, "State: %s, CurSpeed %.1f\n", GetStringForState( GetState()), pev->dmg_save);
ALERT(at_console, "SpinTime: %.2f, MaxSpeed %.f\n", pev->frags, pev->speed);
}
}
//=======================================================================
// func_pendulum - swinging brush (thanks for Lazarus Q2 mod)
//=======================================================================
#define SF_PENDULUM_STOPPING 0x8000000 // for internal use only
#define SF_PENDULUM_STOP_AT_TOP 32
// UNDONE: needs to be implement with Xash specifications
class CPendulum : public CBaseMover
{
public:
void Spawn ( void );
void Precache( void );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think( void );
void Blocked( CBaseEntity *pOther );
void KeyValue( KeyValueData *pkvd );
void Touch( CBaseEntity *pOther );
void SetImpulse( float speed );
void SetSwing( float speed );
void PostActivate( void );
int dir;//FIXME
};
LINK_ENTITY_TO_CLASS( func_pendulum, CPendulum );
void CPendulum :: KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "distance" ))
{
pev->scale = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
if( FStrEq( pkvd->szKeyName, "phase" ))
{
m_flDelay = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "damp" ))
{
pev->max_health = atof( pkvd->szValue ) / 10000;
if(pev->max_health > 0.1) pev->max_health = 0.1;
pkvd->fHandled = TRUE;
}
else CBaseBrush::KeyValue( pkvd );
}
void CPendulum :: Precache( void )
{
CBaseBrush::Precache();
int m_sounds = UTIL_LoadSoundPreset( m_iMoveSound );
switch( m_sounds )
{
case 1:
pev->noise3 = UTIL_PrecacheSound( "pendulum/swing1.wav" );
break;
case 2:
pev->noise3 = UTIL_PrecacheSound( "pendulum/swing2.wav" );
break;
case 0:
pev->noise3 = UTIL_PrecacheSound( "common/null.wav" );
break;
default:
pev->noise3 = UTIL_PrecacheSound( m_sounds );
break;
}
}
void CPendulum :: Spawn( void )
{
Precache();
CBaseBrush::Spawn();
if( pev->spawnflags & SF_NOTSOLID )
{
pev->solid = SOLID_NOT;
}
else
{
pev->solid = SOLID_BSP;
}
// set default distance
if( !pev->scale ) pev->scale = 90;
if( pev->scale >= 360 )
{
ALERT( at_console, "func_pendulum distance must be < 360\n" );
pev->scale = 359.0f;
}
if( !pev->speed ) pev->speed = 100;
if( !m_flRadius ) m_flRadius = 100;
if( !pev->mass ) pev->mass = 200;
if( m_flDelay > 1.0 ) m_flDelay -= (int)(m_flDelay);
if( !pev->dmg ) pev->dmg = 5;
// This is the damage delivered by the pendulum at max speed.
// Convert to a damage scale used in our damage equation.
float max_speed = pev->scale / 2 * M_PI / 180. * sqrt( CVAR_GET_FLOAT( "sv_gravity" ) * m_flRadius );
if( max_speed <= 200.0f )
{
pev->dmg = 0;
}
else
{
float dmg = (float)(pev->dmg) * 100.0f / (max_speed - 200.0f);
pev->dmg = (int)(dmg - 0.5f) + 1;
}
pev->movetype = MOVETYPE_PUSH;
SetBits( pFlags, PF_ANGULAR );
m_iState = STATE_OFF;
pev->spawnflags |= SF_PENDULUM_STOP_AT_TOP;
// don't change this!
if( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS ))
pev->impulse = PITCH; // around z-axis
else if( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS ))
pev->impulse = ROLL; // around x-axis
else pev->impulse = YAW; // around y-axis
if( !m_flAccel ) m_flAccel = 1.0f;
else if( m_flAccel > pev->speed )
m_flAccel = pev->speed;
if( !m_flDecel ) m_flDecel = 1.0f;
else if( m_flDecel > pev->speed )
m_flDecel = pev->speed;
pev->oldangles[pev->impulse] = pev->angles[pev->impulse] = pev->scale / 2;
UTIL_SetOrigin( this, pev->origin );
UTIL_SetModel( ENT( pev ), pev->model );
}
void CPendulum :: PostActivate( void )
{
if( pev->spawnflags & SF_START_ON )SetSwing( pev->speed );
ClearBits( pev->spawnflags, SF_START_ON);
if( m_iState == STATE_ON ) // restore sound if needed
SetNextThink( 0.05 ); // force to think
}
void CPendulum :: Touch ( CBaseEntity *pOther )
{
// don't hurt toucher - just apply velocity him
entvars_t *pevOther = pOther->pev;
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev) ).Normalize() * pev->dmg;
}
void CPendulum::Blocked( CBaseEntity *pOther )
{
// what can do pendulum on blocking ?
// Nothing :)
pev->armortype = gpGlobals->time;
}
void CPendulum :: SetImpulse( float speed )
{
if( dir == pev->impulse )
return;
dir = pev->impulse;
SetSwing( speed );
}
void CPendulum :: SetSwing( float speed )
{
if( m_iState == STATE_ON )
{
if( pev->spawnflags & SF_PENDULUM_STOP_AT_TOP )
{
m_iState = STATE_TURN_OFF;
}
else
{
UTIL_SetAvelocity( this, g_vecZero );
m_iState = STATE_OFF;
LINK_ENTITY( ENT( pev ));
}
}
else
{
m_iState = STATE_ON;
if( m_flDelay > 0 )
{
float delay = m_flDelay * 2.0 * M_PI * sqrt( m_flRadius / CVAR_GET_FLOAT( "sv_gravity" ));
delay = gpGlobals->frametime * (int)(10 * delay);
SetNextThink( delay );
pev->frags = g_ulFrameCount + delay * 10;
if(!( pev->spawnflags & SF_PENDULUM_STOP_AT_TOP ))
m_flDelay = 0;
}
else
{
if( pev->angles[pev->impulse] == pev->oldangles[pev->impulse] )
{
pev->frags = g_ulFrameCount;
}
else
{
float t;
t = acos( pev->angles[pev->impulse] / pev->oldangles[pev->impulse] );
t /= sqrt( CVAR_GET_FLOAT( "sv_gravity" ) / m_flRadius );
pev->frags = g_ulFrameCount - t * 10;
}
SetNextThink( 0 );
}
}
}
void CPendulum :: Think( void )
{
float this_angle;
float wave, sgor;
if( pev->speed < 20 )
{
if( pev->frags == 0 )
{
// then we just started moving again after being blocked
pev->avelocity[pev->impulse] = -pev->angles[pev->impulse];
pev->frags = g_ulFrameCount;
}
else
{
float next_angle;
next_angle = pev->angles[pev->impulse] + pev->avelocity[pev->impulse] * gpGlobals->frametime;
if(( next_angle >= 0 && pev->angles[pev->impulse] < 0) || (next_angle <= 0 && pev->angles[pev->impulse] > 0 ))
{
UTIL_SetAngles( this, g_vecZero );
UTIL_SetAvelocity( this, g_vecZero );
return;
}
}
SetNextThink( gpGlobals->frametime );
}
else
{
float old_velocity = pev->avelocity[pev->impulse];
if( !pev->frags ) pev->frags = g_ulFrameCount;
sgor = sqrt( CVAR_GET_FLOAT( "sv_gravity" ) / m_flRadius );
wave = sgor * (g_ulFrameCount - pev->frags ) * gpGlobals->frametime;
this_angle = pev->oldangles[pev->impulse] * cos( wave );
pev->avelocity[pev->impulse] = -pev->oldangles[pev->impulse] * sgor * sin( wave );
if( m_iState == STATE_TURN_OFF && ( cos(wave) > 0.0f ))
{
if((( old_velocity > 0 ) && (pev->avelocity[pev->impulse] <= 0 )) || ((old_velocity < 0) && (pev->avelocity[pev->impulse] >= 0)))
{
UTIL_AssignAngles( this, pev->angles );
UTIL_SetAvelocity( this, g_vecZero );
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise3), 0, 0, SND_STOP, m_pitch);
DontThink(); // break thinking
UTIL_FireTargets( pev->target, this, this, USE_OFF ); // pendulum is full stopped
m_iState = STATE_OFF;
return;
}
}
// FIXME: test
if( fabs(cos(wave)) < 0.01f )
{
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, STRING( pev->noise3 ), m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
}
pev->angles[pev->impulse] = this_angle;
SetNextThink( gpGlobals->frametime );
}
UTIL_SetAvelocity( this, pev->avelocity );
}
void CPendulum :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if( IsLockedByMaster( useType )) return;
if( useType == USE_TOGGLE )
{
if(m_iState == STATE_OFF) useType = USE_ON;
else useType = USE_OFF;
}
if( useType == USE_ON ) SetSwing( pev->speed );
else if( useType == USE_OFF ) m_iState = STATE_TURN_OFF;
else if( useType == USE_SET )
{
if( value ) pev->speed = value;
else SetImpulse( pev->speed );//any idea ???
}
else if( useType == USE_RESET ) SetImpulse( pev->speed );
else if(useType == USE_SHOWINFO)
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "State: %s, CurSpeed %.1f\n", GetStringForState( GetState()), pev->dmg_save);
ALERT(at_console, "Distance: %.2f, Swing Time %.f\n", pev->dmg_take, pev->frags);
}
}
//=======================================================================
// func_clock - simply rotating clock from Quake1.Scourge Of Armagon
//=======================================================================
class CFuncClock : public CBaseLogic
{
public:
void Spawn ( void );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void Think( void );
void PostActivate( void );
void KeyValue( KeyValueData *pkvd );
Vector curtime, finaltime;
int bellcount;//calc hours
};
LINK_ENTITY_TO_CLASS( func_clock, CFuncClock );
void CFuncClock :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "type"))
{
switch(atoi( pkvd->szValue ))
{
case 1: pev->impulse = MINUTES; break; //minutes
case 2: pev->impulse = HOURS; break; //hours
case 0: //default:
default: pev->impulse = SECONDS; break; //seconds
}
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "curtime"))
{
UTIL_StringToVector( curtime, pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "event"))
{
pev->netname = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
}
void CFuncClock :: Spawn ( void )
{
pev->solid = SOLID_NOT;
UTIL_SetModel( ENT(pev), pev->model );
// NOTE: I'm not have better idea than it
// So, field "angles" setting movedir only
// Turn your brush into your fucking worldcraft!
UTIL_AngularVector( this );
SetBits( pFlags, PF_ANGULAR );
if( pev->impulse == HOURS ) //do time operations
{
// normalize our time
if( curtime.x > 11 ) curtime.x = 0;
if( curtime.y > 59 ) curtime.y = 0;
if( curtime.z > 59 ) curtime.z = 0;
// member full hours
pev->team = curtime.x;
// calculate seconds
finaltime.z = curtime.z * (SECONDS / 60); // seconds
finaltime.y = curtime.y * (MINUTES / 60) + finaltime.z;
finaltime.x = curtime.x * (HOURS / 12) + finaltime.y;
}
}
void CFuncClock :: PostActivate( void )
{
// NOTE: We should be sure what all entities has be spawned
// else we called this from Activate()
if( pev->frags ) return;
if( pev->impulse == HOURS && curtime != g_vecZero ) // it's hour entity and time set
{
// try to find minutes and seconds entity
CBaseEntity *pEntity = NULL;
while( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, pev->size.z ))
{
if( FClassnameIs( pEntity, "func_clock" ))
{
//write start hours, minutes and seconds
if( pEntity->pev->impulse == HOURS ) pEntity->pev->dmg_take = finaltime.x;
if( pEntity->pev->impulse == MINUTES ) pEntity->pev->dmg_take = finaltime.y;
if( pEntity->pev->impulse == SECONDS ) pEntity->pev->dmg_take = finaltime.z;
}
}
}
pev->frags = 1; // clock init
m_iState = STATE_ON;// always on
SetNextThink( 0 ); // force to think
}
void CFuncClock :: Think ( void )
{
float seconds, ang, pos;
seconds = gpGlobals->time + pev->dmg_take;
pos = seconds / pev->impulse;
pos = pos - floor( pos );
ang = 360 * pos;
UTIL_SetAngles( this, pev->movedir * ang );
if(pev->impulse == HOURS) // play bell sound
{
pev->button = pev->angles.Length() / 30; // half hour
if ( pev->team != pev->button ) // new hour
{
// member new hour
bellcount = pev->team = pev->button;
if( bellcount == 0 ) bellcount = 12; // merge for 0.00.00
UTIL_FireTargets( pev->netname, this, this, USE_SET, bellcount ); // send hours info
UTIL_FireTargets( pev->netname, this, this, USE_ON );//activate bell or logic_generator
}
}
SetNextThink( 1 ); // refresh at one second
}
//=======================================================================
// func_healthcharger - brush healthcharger
//=======================================================================
class CWallCharger : public CBaseBrush
{
public:
void Spawn( );
void Precache( void );
void Think (void);
void KeyValue( KeyValueData* pkvd);
virtual int ObjectCaps( void )
{
int flags = CBaseBrush:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
if(m_iState == STATE_DEAD) return flags;
return flags | FCAP_CONTINUOUS_USE;
}
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
float m_flNextCharge;
float m_flSoundTime;
};
LINK_ENTITY_TO_CLASS(func_healthcharger, CWallCharger);
LINK_ENTITY_TO_CLASS(func_recharge, CWallCharger);
void CWallCharger :: KeyValue( KeyValueData* pkvd)
{
if (FStrEq(pkvd->szKeyName, "chargetime"))//affect only in multiplayer
{
pev->armortype = atof(pkvd->szValue);
if(pev->armortype <= 0 )pev->armortype = 1;
pkvd->fHandled = TRUE;
}
else CBaseBrush::KeyValue( pkvd );
}
void CWallCharger::Spawn()
{
Precache();
CBaseBrush::Spawn();
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
UTIL_SetOrigin(this, pev->origin);
UTIL_SetSize(pev, pev->mins, pev->maxs);
UTIL_SetModel(ENT(pev), pev->model );
m_iState = STATE_OFF;
pev->frame = 0;
//set capacity
if (FClassnameIs(this, "func_recharge"))
pev->frags = SUIT_CHARGER_CAP;
else pev->frags = HEATH_CHARGER_CAP;
pev->dmg_save = pev->frags;
}
void CWallCharger::Precache()
{
CBaseBrush::Precache();
if (FClassnameIs(this, "func_recharge"))
{
UTIL_PrecacheSound("items/suitcharge1.wav");
UTIL_PrecacheSound("items/suitchargeno1.wav");
UTIL_PrecacheSound("items/suitchargeok1.wav");
}
else
{
UTIL_PrecacheSound("items/medshot4.wav");
UTIL_PrecacheSound("items/medshotno1.wav");
UTIL_PrecacheSound("items/medcharge4.wav");
}
}
void CWallCharger::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType )) return;
if(useType == USE_SHOWINFO)//show info
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "State: %s, Volume %.1f\n", GetStringForState( GetState()), m_flVolume );
ALERT(at_console, "Texture frame: %.f. ChargeLevel: %.f\n", pev->frame, pev->dmg_save);
}
else if(pActivator && pActivator->IsPlayer())//take health
{
if (pev->dmg_save <= 0)
{
if (m_iState == STATE_IN_USE || m_iState == STATE_ON)
{
pev->frame = 1;
if (FClassnameIs(this, "func_recharge"))
STOP_SOUND( ENT(pev), CHAN_STATIC, "items/suitcharge1.wav" );
else STOP_SOUND( ENT(pev), CHAN_STATIC, "items/medcharge4.wav" );
m_iState = STATE_DEAD;//recharge in multiplayer
if(IsMultiplayer()) SetNextThink( 3);//delay before recahrge
else DontThink();
}
}
if((m_iState == STATE_DEAD) || (!(((CBasePlayer *)pActivator)->pev->weapons & ITEM_SUIT)))
{
if (m_flSoundTime <= gpGlobals->time)
{
m_flSoundTime = gpGlobals->time + 0.62;
if (FClassnameIs(this, "func_recharge"))
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/suitchargeno1.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
else EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/medshotno1.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
}
return;
}
SetNextThink( 0.25);//time before disable
if (m_flNextCharge >= gpGlobals->time) return;
if (m_iState == STATE_OFF)
{
m_iState = STATE_ON;
if (FClassnameIs(this, "func_recharge"))
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/suitchargeok1.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
else EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/medshot4.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
m_flSoundTime = 0.56 + gpGlobals->time;
}
if (m_iState == STATE_ON && m_flSoundTime <= gpGlobals->time)
{
m_iState = STATE_IN_USE;
if (FClassnameIs(this, "func_recharge"))
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
else EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/medcharge4.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
}
if (FClassnameIs(this, "func_recharge"))
{
if (m_hActivator->pev->armorvalue < 100)
{
UTIL_FireTargets( pev->target, this, this, USE_OFF, pev->dmg_save );//decrement counter
pev->dmg_save--;
m_hActivator->pev->armorvalue++;
}
}
else
{
if ( pActivator->TakeHealth(1, DMG_GENERIC ))
{
UTIL_FireTargets( pev->target, this, this, USE_OFF, pev->dmg_save );//decrement counter
pev->dmg_save--;
}
}
m_flNextCharge = gpGlobals->time + 0.1;
}
}
void CWallCharger :: Think( void )
{
if (m_iState == STATE_IN_USE || m_iState == STATE_ON)
{
if (FClassnameIs(this, "func_recharge"))
STOP_SOUND( ENT(pev), CHAN_STATIC, "items/suitcharge1.wav" );
else STOP_SOUND( ENT(pev), CHAN_STATIC, "items/medcharge4.wav" );
m_iState = STATE_OFF;
DontThink();
return;
}
if(m_iState == STATE_DEAD && IsMultiplayer())//recharge
{
pev->dmg_save++;//ressurection
UTIL_FireTargets( pev->target, this, this, USE_ON, pev->dmg_save );//increment counter
if(pev->dmg_save >= pev->frags)
{
//take full charge
if (FClassnameIs(this, "func_recharge"))
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/suitchargeok1.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
else EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "items/medshot4.wav", m_flVolume, ATTN_IDLE, SND_CHANGE_VOL, PITCH_NORM );
pev->dmg_save = pev->frags;//normalize
pev->frame = 0;//enable
m_iState = STATE_OFF;//can use
DontThink();
return;
}
}
SetNextThink( pev->armortype );
}
//=======================================================================
// func_button - standard linear button from Quake1
//=======================================================================
class CBaseButton : public CBaseMover
{
public:
void Spawn( void );
void PostSpawn( void );
void Precache( void );
virtual void KeyValue( KeyValueData* pkvd);
virtual int ObjectCaps( void )
{
int flags = CBaseMover:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
if(m_iState == STATE_DEAD) return flags;
return flags | FCAP_IMPULSE_USE | FCAP_ONLYDIRECT_USE;
}
void EXPORT ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT ShowInfo ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT ButtonTouch( CBaseEntity *pOther );
void EXPORT ButtonReturn( void );
void EXPORT ButtonDone( void );
void ButtonActivate( void );
};
LINK_ENTITY_TO_CLASS( func_button, CBaseButton );
void CBaseButton::KeyValue( KeyValueData *pkvd )
{
//rename standard fields for button
if (FStrEq(pkvd->szKeyName, "locksound"))
{
m_iStopSound = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "pushsound"))
{
m_iStartSound = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else CBaseMover::KeyValue( pkvd );
}
void CBaseButton::Precache( void )
{
CBaseBrush::Precache();//precache damage sound
int m_sounds = UTIL_LoadSoundPreset(m_iStartSound);
switch (m_sounds)//load pushed sounds (sound will play at activate or pushed button)
{
case 1: pev->noise = UTIL_PrecacheSound ("materials/buttons/blip1.wav");break;
case 2: pev->noise = UTIL_PrecacheSound ("materials/buttons/blip2.wav");break;
case 3: pev->noise = UTIL_PrecacheSound ("materials/buttons/blip3.wav");break;
case 4: pev->noise = UTIL_PrecacheSound ("materials/buttons/blip4.wav");break;
case 5: pev->noise = UTIL_PrecacheSound ("materials/buttons/blip5.wav");break;
case 6: pev->noise = UTIL_PrecacheSound ("materials/buttons/blip6.wav");break;
case 0: pev->noise = UTIL_PrecacheSound ("common/null.wav"); break;
default: pev->noise = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
}
if (!FStringNull(m_sMaster))//button has master
{
m_sounds = UTIL_LoadSoundPreset(m_iStopSound);
switch (m_sounds)//load locked sounds
{
case 1: pev->noise3 = UTIL_PrecacheSound ("materials/buttons/locked1.wav");break;
case 2: pev->noise3 = UTIL_PrecacheSound ("materials/buttons/locked2.wav");break;
case 0: pev->noise3 = UTIL_PrecacheSound ("common/null.wav"); break;
default: pev->noise3 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
}
}
}
void CBaseButton::Spawn( )
{
Precache();
CBaseBrush::Spawn();
if(pev->spawnflags & SF_NOTSOLID)//not solid button
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->contents = CONTENTS_NONE;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
UTIL_SetModel(ENT(pev), pev->model);
//determine work style
if(m_iMode == 0)//classic HL button - only USE
{
SetUse ( ButtonUse );
SetTouch ( NULL );
}
if(m_iMode == 1)//classic QUAKE button - only TOUCH
{
SetUse ( ShowInfo );//show info only
SetTouch ( ButtonTouch );
}
if(m_iMode == 2)//combo button - USE and TOUCH
{
SetUse ( ButtonUse );
SetTouch ( ButtonTouch );
}
//as default any button is toggleable, but if mapmaker set waittime > 0
//button will transformed into timebased button
//if waittime is -1 - button forever stay pressed
if(m_flWait == 0) pev->impulse = 1;//toggleable button
if (m_flLip == 0) m_flLip = 4;//standart offset from Quake1
m_iState = STATE_OFF;//disable at spawn
UTIL_LinearVector( this );//movement direction
m_vecPosition1 = pev->origin;
m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip));
// Is this a non-moving button?
if( pev->speed == 0 )m_vecPosition2 = m_vecPosition1;
}
void CBaseButton :: PostSpawn( void )
{
if (m_pParent) m_vecPosition1 = pev->origin - m_pParent->pev->origin;
else m_vecPosition1 = pev->origin;
m_vecPosition2 = m_vecPosition1 + (pev->movedir * (fabs( pev->movedir.x * (pev->size.x-2) ) + fabs( pev->movedir.y * (pev->size.y-2) ) + fabs( pev->movedir.z * (pev->size.z-2) ) - m_flLip));
// Is this a non-moving button?
if ( pev->speed == 0 ) m_vecPosition2 = m_vecPosition1;
}
void CBaseButton :: ShowInfo ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if(useType == USE_SHOWINFO)//show info
{
DEBUGHEAD;
ALERT(at_console, "State: %s, Speed %.2f\n", GetStringForState( GetState()), pev->speed );
ALERT(at_console, "Texture frame: %.f. WaitTime: %.2f\n", pev->frame, m_flWait);
}
}
void CBaseButton :: ButtonUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;//save our activator
if(IsLockedByMaster( useType ))//passed only USE_SHOWINFO
{
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise3), 1, ATTN_NORM);
return;
}
if(useType == USE_SHOWINFO)//show info
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "State: %s, Speed %.2f\n", GetStringForState( GetState()), pev->speed );
ALERT(at_console, "Texture frame: %.f. WaitTime: %.2f\n", pev->frame, m_flWait);
}
else if(m_iState != STATE_DEAD)//activate button
{
//NOTE: STATE_DEAD is better method for simulate m_flWait -1 without fucking SetThink()
if(pActivator && pActivator->IsPlayer())//code for player
{
if(pev->impulse == 1)//toggleable button
{
if (m_iState == STATE_TURN_ON || m_iState == STATE_TURN_OFF )return;//buton in-moving
else if( m_iState == STATE_ON )//button is active, disable
{
ButtonReturn();
}
else if( m_iState == STATE_OFF )//activate
{
ButtonActivate();
}
}
else //time based button
{
//can activate only disabled button
if( m_iState == STATE_OFF ) ButtonActivate();
else return;//does nothing :)
}
}
else //activate button from other entity
{
//NOTE: Across activation just fire output without changes
UTIL_FireTargets( pev->target, pActivator, pCaller, useType, value );
}
}
}
void CBaseButton:: ButtonTouch( CBaseEntity *pOther )
{
//make delay before retouching
if ( gpGlobals->time < m_flBlockedTime) return;
m_flBlockedTime = gpGlobals->time + 1.0;
if(pOther->IsPlayer())ButtonUse ( pOther, this, USE_TOGGLE, 1 );//player always sending 1
}
void CBaseButton::ButtonActivate( )
{
ASSERT(m_iState == STATE_OFF);
m_iState = STATE_TURN_ON;
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), 1, ATTN_NORM);
if(pev->speed)
{
SetMoveDone( ButtonDone );
LinearMove( m_vecPosition2, pev->speed);
}
else ButtonDone();//immediately switch
}
void CBaseButton::ButtonDone( void )
{
if(m_iState == STATE_TURN_ON)//turn on
{
m_iState = STATE_ON;
pev->frame = 1; // change skin
if(pev->impulse)
UTIL_FireTargets( pev->target, m_hActivator, this, USE_ON, 0 );//fire target
else UTIL_FireTargets( pev->target, m_hActivator, this, USE_TOGGLE, 0 );//fire target
if(m_flWait == -1)
{
m_iState = STATE_DEAD;//keep button in this position
return;
}
if(pev->impulse == 0)//time base button
{
SetThink( ButtonReturn );
SetNextThink( m_flWait );
}
}
if(m_iState == STATE_TURN_OFF)//turn off
{
m_iState = STATE_OFF;//just change state :)
if(pev->impulse)
UTIL_FireTargets( pev->target, m_hActivator, this, USE_OFF, 0 );//fire target
}
}
void CBaseButton::ButtonReturn( void )
{
ASSERT(m_iState == STATE_ON);
m_iState = STATE_TURN_OFF;
pev->frame = 0;// use normal textures
//make sound for toggleable button
if(pev->impulse) EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), 1, ATTN_NORM);
if(pev->speed)
{
SetMoveDone( ButtonDone );
LinearMove( m_vecPosition1, pev->speed);
}
else ButtonDone();//immediately switch
}
//=======================================================================
// func_lever - momentary rotating button
//=======================================================================
class CFuncLever : public CBaseMover
{
public:
void Spawn ( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void )
{
int flags = CBaseMover:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
if(m_iState == STATE_DEAD) return flags;
return flags | FCAP_CONTINUOUS_USE;
}
BOOL MaxRange( void )
{
if(pev->impulse == 1 && m_flMoveDistance > 0) return TRUE;
if(pev->impulse == -1 && m_flMoveDistance < 0) return TRUE;
return FALSE;
}
BOOL BackDir( void )//determine start direction
{
if( pev->dmg_save && m_flMoveDistance < 0) return TRUE;
if(!pev->dmg_save && m_flMoveDistance > 0) return TRUE;
return FALSE;
}
void PlayMoveSound( void );
void CalcValue( void );
void Think( void );
float mTimeToReverse, SendTime, nextplay;
};
LINK_ENTITY_TO_CLASS( func_lever, CFuncLever );
void CFuncLever::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "backspeed"))
{
pev->dmg_save = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "movesound"))
{
m_iMoveSound = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "stopsound"))
{
m_iStopSound = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else CBaseMover::KeyValue( pkvd );
}
void CFuncLever::Precache( void )
{
int m_sounds = UTIL_LoadSoundPreset(m_iMoveSound);
switch (m_sounds)//load pushed sounds (sound will play at activate or pushed button)
{
case 1: pev->noise = UTIL_PrecacheSound ("materials/buttons/lever1.wav");break;
case 2: pev->noise = UTIL_PrecacheSound ("materials/buttons/lever2.wav");break;
case 3: pev->noise = UTIL_PrecacheSound ("materials/buttons/lever3.wav");break;
case 4: pev->noise = UTIL_PrecacheSound ("materials/buttons/lever4.wav");break;
case 5: pev->noise = UTIL_PrecacheSound ("materials/buttons/lever5.wav");break;
case 0: pev->noise = UTIL_PrecacheSound ("common/null.wav"); break;
default: pev->noise = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
}
m_sounds = UTIL_LoadSoundPreset(m_iStopSound);
switch (m_sounds)//load locked sounds
{
case 1: pev->noise2 = UTIL_PrecacheSound ("materials/buttons/lstop1.wav");break;
case 0: pev->noise2 = UTIL_PrecacheSound ("common/null.wav"); break;
default: pev->noise2 = UTIL_PrecacheSound(m_sounds); break;//custom sound or sentence
}
}
void CFuncLever::Spawn( void )
{
Precache();
if(pev->spawnflags & SF_NOTSOLID)//not solid button
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
pev->contents = CONTENTS_NONE;
}
else
{
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
}
UTIL_SetModel(ENT(pev), pev->model);
UTIL_SetOrigin(this, pev->origin);
AxisDir();
SetBits (pFlags, PF_ANGULAR);
//Smart field system ®
if( pev->speed == 0 ) pev->speed = 100;//check null speed
if( pev->speed > 800) pev->speed = 800;//check max speed
if( fabs(m_flMoveDistance) < pev->speed/2)
{
if(m_flMoveDistance > 0)m_flMoveDistance = -pev->speed/2;
if(m_flMoveDistance < 0)m_flMoveDistance = pev->speed/2;
if(m_flMoveDistance ==0)m_flMoveDistance = 45;
}
//check for direction (right direction will be set at first called use)
if(BackDir())pev->impulse = -1;//backward dir
else pev->impulse = 1;//forward dir
}
void CFuncLever::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType ))//strange stuff...
{
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise2), 1, ATTN_NORM);
return;
}
if(useType == USE_SHOWINFO)
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "State: %s, Value %.2f\n", GetStringForState( GetState()), pev->ideal_yaw );
ALERT(at_console, "Distance: %.f. Speed %.2f\n", m_flMoveDistance, pev->speed );
}
else if(m_iState != STATE_DEAD)
{
if(pActivator && pActivator->IsPlayer())//code only for player
{
if(gpGlobals->time > mTimeToReverse && !pev->dmg_save)//don't switch if backspeed set
pev->impulse = -pev->impulse;
mTimeToReverse = gpGlobals->time + 0.3;//max time to change dir
pev->frags = pev->speed;//send speed
m_iState = STATE_IN_USE;//in use
SetNextThink(0.01);
}
}
}
void CFuncLever::Think( void )
{
UTIL_SetAvelocity(this, pev->movedir * pev->frags * pev->impulse);//set speed and dir
if(m_iState == STATE_TURN_OFF && pev->dmg_save)pev->frags = -pev->dmg_save;//backspeed
if(!pev->dmg_save)pev->frags -= pev->frags * 0.05;
CalcValue();
PlayMoveSound();
//wacthing code
if(pev->avelocity.Length() < 0.5)m_iState = STATE_OFF;//watch min speed
if(pev->ideal_yaw <= 0.01)//min range
{
if(pev->dmg_save && m_iState != STATE_IN_USE)
{
UTIL_SetAvelocity(this, g_vecZero);
m_iState = STATE_OFF;//off
}
else if(!MaxRange())UTIL_SetAvelocity(this, g_vecZero);
}
if(pev->ideal_yaw >= 0.99)//max range
{
if(pev->dmg_save && m_iState == STATE_IN_USE)
{
UTIL_SetAvelocity(this, g_vecZero);
if(pev->spawnflags & SF_START_ON)//Stay open flag
m_iState = STATE_DEAD;//dead
else m_iState = STATE_TURN_OFF;//must return
}
else if(MaxRange() && m_iState == STATE_IN_USE)
{
UTIL_SetAvelocity(this, g_vecZero);
if(pev->spawnflags & SF_START_ON)//Stay open flag
m_iState = STATE_DEAD;//dead
else m_iState = STATE_OFF;//dist is out
}
}
if(m_iState == STATE_IN_USE)m_iState = STATE_TURN_OFF;//try disable
SetNextThink(0.01);
if(m_iState == STATE_OFF || m_iState == STATE_DEAD)//STATE_DEAD is one end way
{
if(pev->dmg_save)//play sound only auto return
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise2), 1, ATTN_NORM);
UTIL_SetAvelocity(this, g_vecZero);
UTIL_AssignAngles( this, pev->angles );
DontThink();//break thinking
}
//ALERT(at_console, "Ideal Yaw %f\n", pev->ideal_yaw);
}
void CFuncLever::CalcValue( void )
{
//calc value to send
//pev->ideal_yaw = UTIL_CalcDistance(pev->angles) / fabs(m_flMoveDistance);
pev->ideal_yaw = pev->angles.Length() / fabs(m_flMoveDistance);
if(pev->ideal_yaw >= 1)pev->ideal_yaw = 1;//normalize value
else if(pev->ideal_yaw <= 0)pev->ideal_yaw = 0;//normalize value
if(gpGlobals->time > SendTime)
{
UTIL_FireTargets( pev->target, this, this, USE_SET, pev->ideal_yaw );//send value
SendTime = gpGlobals->time + 0.01;//time to nextsend
}
}
void CFuncLever::PlayMoveSound( void )
{
if(m_iState == STATE_TURN_OFF && pev->dmg_save )
nextplay = fabs((pev->dmg_save * 0.01) / (m_flMoveDistance * 0.01));
else nextplay = fabs((pev->frags * 0.01) / (m_flMoveDistance * 0.01));
nextplay = 1/(nextplay * 5);
if(gpGlobals->time > m_flBlockedTime)
{
EMIT_SOUND(ENT(pev), CHAN_VOICE, (char*)STRING(pev->noise), 1, ATTN_NORM);
m_flBlockedTime = gpGlobals->time + nextplay;
}
}
//=======================================================================
// func_ladder - makes an area vertically negotiable
//=======================================================================
class CLadder : public CBaseEntity
{
public:
void Spawn( void );
void Precache( void );
};
LINK_ENTITY_TO_CLASS( func_ladder, CLadder );
void CLadder :: Precache( void )
{
pev->solid = SOLID_NOT;
pev->contents = CONTENTS_LADDER;
pev->effects |= EF_NODRAW;
}
void CLadder :: Spawn( void )
{
Precache();
UTIL_SetModel(ENT(pev), pev->model); // set size and link into world
pev->movetype = MOVETYPE_PUSH;
}
//=======================================================================
// func_scaner - retinal scaner
//=======================================================================
class CFuncScaner : public CBaseBrush
{
public:
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
void Precache( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void PostActivate( void );
void Think( void );
BOOL VisionCheck( void );
BOOL CanSee(CBaseEntity *pLooker );
CBaseEntity *pSensor;
CBaseEntity *pLooker;
};
LINK_ENTITY_TO_CLASS( func_scaner, CFuncScaner );
void CFuncScaner::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "sensor"))
{
pev->message = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "acesslevel"))
{
pev->impulse = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "sense"))
{
pev->health = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
void CFuncScaner :: Precache( void )
{
CBaseBrush::Precache();
UTIL_PrecacheSound( "buttons/blip1.wav" );
UTIL_PrecacheSound( "buttons/blip2.wav" );
UTIL_PrecacheSound( "buttons/button7.wav" );
UTIL_PrecacheSound( "buttons/button11.wav" );
}
void CFuncScaner :: Spawn( void )
{
Precache();
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
if(!pev->health) pev->health = 15;
UTIL_SetModel(ENT(pev), pev->model);
UTIL_SetOrigin(this, pev->origin);
m_flDelay = 0.1;
SetNextThink( 1 );
}
void CFuncScaner :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (useType == USE_SHOWINFO)
{
DEBUGHEAD;
Msg( "State: %s, Acess Level %d\n", GetStringForState( GetState()), pev->impulse);
Msg( "Sense Corner: %g\n", pev->health);
}
else if (useType == USE_SET)
{
if(value)
{
pev->armorvalue = (int)value;//set new acess level for looker
pev->frame = 1;
}
}
}
void CFuncScaner :: Think( void )
{
if (VisionCheck() && m_iState != STATE_ON && m_iState != STATE_DEAD)
{
if(pev->armorvalue)
{
m_iState = STATE_DEAD;
pLooker->m_iAcessLevel = (int)pev->armorvalue;
pev->armorvalue = 0;
//change acess level sound
EMIT_SOUND( edict(), CHAN_BODY, "buttons/blip2.wav", 1.0, ATTN_NORM );
}
else if(m_iState == STATE_OFF)//scaner is off
{
//ok, we have seen entity
if(pLooker->pev->flags & FL_CLIENT) m_flDelay = 0.1;
else m_flDelay = 0.9;
m_iState = STATE_TURN_ON;
}
else if(m_iState == STATE_TURN_ON)//scaner is turn on
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/blip1.wav", 1.0, ATTN_NORM );
pev->frame = 1;
m_flDelay = 0.3;
pev->button++;
if(pev->button > 9)
{
m_iState = STATE_ON;
pev->team = pLooker->m_iAcessLevel;//save our level acess
pev->button = 0;
}
}
}
else if(m_iState == STATE_ON)//scaner is on
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/blip1.wav", 1.0, ATTN_NORM );
m_flDelay = 0.1;
pev->button++;
if(pev->button > 9)
{
m_iState = STATE_OFF;
pev->frame = 0;
pev->button = 0;
m_flDelay = 2.0;
if(pev->team >= pev->impulse )//acees level is math or higher ?
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/button7.wav", 1.0, ATTN_NORM );
UTIL_FireTargets( pev->target, this, this, USE_TOGGLE );
}
else
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/button11.wav", 1.0, ATTN_NORM );
UTIL_FireTargets( pev->netname, this, this, USE_TOGGLE );
}
}
}
else if(m_iState == STATE_TURN_ON)
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/button11.wav", 1.0, ATTN_NORM );
m_iState = STATE_OFF;
pev->button = 0;
pev->frame = 0;
m_flDelay = 2.0;
}
else if(m_iState == STATE_DEAD)
{
m_iState = STATE_OFF;
pev->button = 0;
m_flDelay = 2.0;
}
// is this a sensible rate?
SetNextThink( m_flDelay );
}
void CFuncScaner :: PostActivate( void )
{
if (pev->message)
{
pSensor = UTIL_FindEntityByTargetname(NULL, STRING(pev->message));
if(!pSensor) pSensor = this;
}
else pSensor = this;
}
BOOL CFuncScaner :: VisionCheck( void )
{
pLooker = UTIL_FindEntityInSphere( NULL, pSensor->pev->origin, 30 );
if (pLooker)
{
while( pLooker != NULL )
{
if(pLooker && pLooker->pev->flags & (FL_MONSTER | FL_CLIENT))
return CanSee(pLooker);//looker found
pLooker = UTIL_FindEntityInSphere( pLooker, pSensor->pev->origin, 30 );
}
}
return FALSE;//no lookers
}
BOOL CFuncScaner :: CanSee(CBaseEntity *pLooker )
{
if(!pSensor || !pLooker) return FALSE;
if ((pLooker->EyePosition() - pSensor->pev->origin).Length() > 30) return FALSE;
// copied from CBaseMonster's FInViewCone function
Vector2D vec2LOS;
float flDot, flComp = cos(pev->health/2 * M_PI/180.0);
UTIL_MakeVectors ( pLooker->pev->angles );
vec2LOS = ( pSensor->pev->origin - pLooker->pev->origin ).Make2D();
vec2LOS = vec2LOS.Normalize();
flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() );
if (flDot <= flComp) return FALSE;
return TRUE;
}
//=======================================================================
// volume of space that the player must stand in to control the train
//=======================================================================
class CFuncTrainControls : public CPointEntity
{
public:
void Spawn( void );
void PostSpawn( void );
};
LINK_ENTITY_TO_CLASS( func_traincontrols, CFuncTrainControls );
void CFuncTrainControls :: PostSpawn( void )
{
CBaseEntity *pTarget = NULL;
do
{
pTarget = UTIL_FindEntityByTargetname( pTarget, STRING(pev->target) );
} while ( pTarget && !FClassnameIs(pTarget->pev, "func_tracktrain") );
if ( !pTarget )
{
ALERT( at_console, "TrackTrainControls: No train %s\n", STRING(pev->target) );
return;
}
CFuncTrackTrain *ptrain = (CFuncTrackTrain*)pTarget;
ptrain->SetControls( pev );
UTIL_Remove( this );
}
void CFuncTrainControls :: Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
UTIL_SetModel( ENT(pev), pev->model );
UTIL_SetSize( pev, pev->mins, pev->maxs );
UTIL_SetOrigin( this, pev->origin );
}