Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1044 lines
27 KiB

7 years ago
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
/*
===== bmodels.cpp ========================================================
spawn, think, and use functions for entities that use brush models
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "doors.h"
#include "movewith.h"
extern DLL_GLOBAL Vector g_vecAttackDir;
#define SF_BRUSH_ACCDCC 16// brush should accelerate and decelerate when toggled
#define SF_BRUSH_HURT 32// rotating brush that inflicts pain based on rotation speed
#define SF_ROTATING_NOT_SOLID 64 // some special rotating objects are not solid.
// covering cheesy noise1, noise2, & noise3 fields so they make more sense (for rotating fans)
#define noiseStart noise1
#define noiseStop noise2
#define noiseRunning noise3
#define SF_PENDULUM_SWING 2 // spawnflag that makes a pendulum a rope swing.
//
// BModelOrigin - calculates origin of a bmodel from absmin/size because all bmodel origins are 0 0 0
//
Vector VecBModelOrigin( entvars_t* pevBModel )
{
return (pevBModel->absmin + pevBModel->absmax) * 0.5; //LRC - bug fix for rotating ents
// return pevBModel->absmin + ( pevBModel->size * 0.5 );
}
// =================== FUNC_WALL ==============================================
/*QUAKED func_wall (0 .5 .8) ?
This is just a solid wall if not inhibited
*/
class CFuncWall : public CBaseEntity
{
public:
8 years ago
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
7 years ago
virtual STATE GetState( void ) { return pev->frame?STATE_ON:STATE_OFF; };
// Bmodels don't go across transitions
8 years ago
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
7 years ago
int m_iStyle;
};
LINK_ENTITY_TO_CLASS( func_wall, CFuncWall )
7 years ago
8 years ago
void CFuncWall::Spawn( void )
7 years ago
{
8 years ago
pev->angles = g_vecZero;
pev->movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
pev->solid = SOLID_BSP;
SET_MODEL( ENT( pev ), STRING( pev->model ) );
7 years ago
// If it can't move/go away, it's really part of the world
if (!m_pMoveWith) //LRC
9 years ago
pev->flags |= FL_WORLDBRUSH;
7 years ago
//LRC
if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a");
else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z");
}
8 years ago
void CFuncWall::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
8 years ago
if( ShouldToggle( useType, (int)( pev->frame ) ) )
7 years ago
{
pev->frame = 1 - pev->frame;
if (m_iStyle >= 32)
{
if (pev->frame)
LIGHT_STYLE(m_iStyle, "z");
else
LIGHT_STYLE(m_iStyle, "a");
}
else if (m_iStyle <= -32)
{
if (pev->frame)
LIGHT_STYLE(-m_iStyle, "a");
else
LIGHT_STYLE(-m_iStyle, "z");
}
}
}
#define SF_WALL_START_OFF 0x0001
class CFuncWallToggle : public CFuncWall
{
public:
8 years ago
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void TurnOff( void );
void TurnOn( void );
BOOL IsOn( void );
7 years ago
virtual STATE GetState( void ) { return (pev->solid == SOLID_NOT)?STATE_OFF:STATE_ON; };
};
LINK_ENTITY_TO_CLASS( func_wall_toggle, CFuncWallToggle )
7 years ago
8 years ago
void CFuncWallToggle::Spawn( void )
7 years ago
{
CFuncWall::Spawn();
8 years ago
if( pev->spawnflags & SF_WALL_START_OFF )
7 years ago
TurnOff();
}
8 years ago
void CFuncWallToggle::TurnOff( void )
7 years ago
{
pev->solid = SOLID_NOT;
pev->effects |= EF_NODRAW;
UTIL_SetOrigin( this, pev->origin );
}
8 years ago
void CFuncWallToggle::TurnOn( void )
7 years ago
{
pev->solid = SOLID_BSP;
pev->effects &= ~EF_NODRAW;
UTIL_SetOrigin( this, pev->origin );
}
8 years ago
BOOL CFuncWallToggle::IsOn( void )
7 years ago
{
8 years ago
if( pev->solid == SOLID_NOT )
7 years ago
return FALSE;
return TRUE;
}
8 years ago
void CFuncWallToggle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
// int status = IsOn();
BOOL status = (GetState() == STATE_ON);
8 years ago
if( ShouldToggle( useType, status ) )
7 years ago
{
8 years ago
if( status )
7 years ago
TurnOff();
else
TurnOn();
}
}
8 years ago
#define SF_CONVEYOR_VISUAL 0x0001
7 years ago
#define SF_CONVEYOR_NOTSOLID 0x0002
class CFuncConveyor : public CFuncWall
{
public:
8 years ago
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void UpdateSpeed( float speed );
7 years ago
};
LINK_ENTITY_TO_CLASS( func_conveyor, CFuncConveyor )
8 years ago
void CFuncConveyor::Spawn( void )
7 years ago
{
SetMovedir( pev );
CFuncWall::Spawn();
8 years ago
if( !( pev->spawnflags & SF_CONVEYOR_VISUAL ) )
7 years ago
SetBits( pev->flags, FL_CONVEYOR );
// HACKHACK - This is to allow for some special effects
8 years ago
if( pev->spawnflags & SF_CONVEYOR_NOTSOLID )
7 years ago
{
pev->solid = SOLID_NOT;
pev->skin = 0; // Don't want the engine thinking we've got special contents on this brush
}
8 years ago
if( pev->speed == 0 )
7 years ago
pev->speed = 100;
UpdateSpeed( pev->speed );
}
// HACKHACK -- This is ugly, but encode the speed in the rendercolor to avoid adding more data to the network stream
8 years ago
void CFuncConveyor::UpdateSpeed( float speed )
7 years ago
{
// Encode it as an integer with 4 fractional bits
8 years ago
int speedCode = (int)( fabs( speed ) * 16.0 );
7 years ago
8 years ago
if( speed < 0 )
7 years ago
pev->rendercolor.x = 1;
else
pev->rendercolor.x = 0;
8 years ago
pev->rendercolor.y = speedCode >> 8;
pev->rendercolor.z = speedCode & 0xFF;
7 years ago
}
8 years ago
void CFuncConveyor::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
pev->speed = -pev->speed;
UpdateSpeed( pev->speed );
}
// =================== FUNC_ILLUSIONARY ==============================================
/*QUAKED func_illusionary (0 .5 .8) ?
A simple entity that looks solid but lets you walk through it.
*/
class CFuncIllusionary : public CBaseToggle
{
public:
void Spawn( void );
void EXPORT SloshTouch( CBaseEntity *pOther );
void KeyValue( KeyValueData *pkvd );
8 years ago
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
7 years ago
};
LINK_ENTITY_TO_CLASS( func_illusionary, CFuncIllusionary )
7 years ago
8 years ago
void CFuncIllusionary::KeyValue( KeyValueData *pkvd )
7 years ago
{
// LRC- surely it just parses this automatically? pev values are handled by the engine.
8 years ago
if( FStrEq( pkvd->szKeyName, "skin" ) )//skin is used for content type
7 years ago
{
pev->skin = atoi( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
else
CBaseToggle::KeyValue( pkvd );
}
8 years ago
void CFuncIllusionary::Spawn( void )
7 years ago
{
pev->angles = g_vecZero;
pev->movetype = MOVETYPE_NONE;
pev->solid = SOLID_NOT;// always solid_not
8 years ago
SET_MODEL( ENT( pev ), STRING( pev->model ) );
7 years ago
// I'd rather eat the network bandwidth of this than figure out how to save/restore
// these entities after they have been moved to the client, or respawn them ala Quake
// Perhaps we can do this in deathmatch only.
// MAKE_STATIC(ENT(pev));
}
// =================== FUNC_SHINE ==============================================
//LRC - shiny surfaces
class CFuncShine : public CBaseEntity
{
public:
void Spawn( void );
void Activate( void );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void DesiredAction( void );
void EXPORT Think( void );
};
LINK_ENTITY_TO_CLASS( func_shine, CFuncShine )
7 years ago
extern int gmsgAddShine;
void CFuncShine :: Spawn( void )
{
pev->solid = SOLID_NOT;// always solid_not
SET_MODEL( ENT(pev), STRING(pev->model) );
pev->effects |= EF_NODRAW;
// not that we actually need to precache it here, but we do need to make sure it exists
PRECACHE_MODEL( STRING(pev->message) );
7 years ago
}
void CFuncShine :: Activate( void )
{
// ALERT(at_console, "Activate shine\n");
CBaseEntity::Activate();
UTIL_DesiredAction(this);
}
void CFuncShine :: DesiredAction( void )
{
if (pev->message && pev->renderamt)
{
// ALERT(at_console, "Prepare think\n");
pev->nextthink = gpGlobals->time + 1.5;
}
}
void CFuncShine :: Think( void )
{
// ALERT(at_console, "Think shine\n");
MESSAGE_BEGIN(MSG_BROADCAST, gmsgAddShine, NULL);
WRITE_BYTE(pev->scale);
WRITE_BYTE(pev->renderamt);
WRITE_COORD(pev->absmin.x + 2); // take off 2: mins values are padded, but we just want to hug the surface
WRITE_COORD(pev->absmax.x - 2);
WRITE_COORD(pev->absmin.y + 2);
WRITE_COORD(pev->absmax.y - 2);
WRITE_COORD(pev->absmin.z + 2);
WRITE_STRING(STRING(pev->message));
MESSAGE_END();
}
// -------------------------------------------------------------------------------
//
// Monster only clip brush
//
// This brush will be solid for any entity who has the FL_MONSTERCLIP flag set
// in pev->flags
//
// otherwise it will be invisible and not solid. This can be used to keep
// specific monsters out of certain areas
//
// -------------------------------------------------------------------------------
class CFuncMonsterClip : public CFuncWall
{
public:
8 years ago
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) {} // Clear out func_wall's use function
7 years ago
};
LINK_ENTITY_TO_CLASS( func_monsterclip, CFuncMonsterClip )
7 years ago
void CFuncMonsterClip::Spawn( void )
{
CFuncWall::Spawn();
8 years ago
if( CVAR_GET_FLOAT( "showtriggers" ) == 0 )
7 years ago
pev->effects = EF_NODRAW;
pev->flags |= FL_MONSTERCLIP;
}
// =================== FUNC_ROTATING ==============================================
class CFuncRotating : public CBaseEntity
{
public:
// basic functions
void Spawn( void );
void Precache( void );
8 years ago
void EXPORT SpinUp( void );
void EXPORT SpinDown( void );
7 years ago
void KeyValue( KeyValueData* pkvd);
8 years ago
void EXPORT HurtTouch( CBaseEntity *pOther );
7 years ago
void EXPORT RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT WaitForStart (); //LRC - get round 1.1.0.8's bizarre behaviour on startup
void EXPORT Rotate( void );
8 years ago
void RampPitchVol(int fUp );
7 years ago
void Blocked( CBaseEntity *pOther );
8 years ago
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
7 years ago
float m_flFanFriction;
float m_flAttenuation;
float m_flVolume;
float m_pitch;
8 years ago
int m_sounds;
7 years ago
float m_fCurSpeed; //LRC - during spin-up and spin-down, this is
// the current speed factor (between 0 and 1).
// storing this here lets us avoid the hassle of deriving it
// from pev->avelocity.
STATE m_iState; //LRC
virtual STATE GetState( void ) { return m_iState; }; //LRC
};
8 years ago
TYPEDESCRIPTION CFuncRotating::m_SaveData[] =
7 years ago
{
DEFINE_FIELD( CFuncRotating, m_flFanFriction, FIELD_FLOAT ),
DEFINE_FIELD( CFuncRotating, m_flAttenuation, FIELD_FLOAT ),
DEFINE_FIELD( CFuncRotating, m_flVolume, FIELD_FLOAT ),
DEFINE_FIELD( CFuncRotating, m_pitch, FIELD_FLOAT ),
DEFINE_FIELD( CFuncRotating, m_sounds, FIELD_INTEGER ),
DEFINE_FIELD( CFuncRotating, m_fCurSpeed, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CFuncRotating, CBaseEntity )
7 years ago
LINK_ENTITY_TO_CLASS( func_rotating, CFuncRotating )
7 years ago
8 years ago
void CFuncRotating::KeyValue( KeyValueData* pkvd )
7 years ago
{
8 years ago
if( FStrEq( pkvd->szKeyName, "fanfriction" ) )
7 years ago
{
8 years ago
m_flFanFriction = atof( pkvd->szValue ) / 100;
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "Volume" ) )
7 years ago
{
8 years ago
m_flVolume = atof( pkvd->szValue ) / 10.0;
7 years ago
8 years ago
if( m_flVolume > 1.0 )
7 years ago
m_flVolume = 1.0;
8 years ago
if( m_flVolume < 0.0 )
7 years ago
m_flVolume = 0.0;
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "spawnorigin" ) )
7 years ago
{
Vector tmp;
UTIL_StringToVector( (float *)tmp, pkvd->szValue );
8 years ago
if( tmp != g_vecZero )
7 years ago
pev->origin = tmp;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "sounds" ) )
7 years ago
{
8 years ago
m_sounds = atoi( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "axes"))
{
UTIL_StringToVector( (float *)(pev->movedir), pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
/*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS
You need to have an origin brush as part of this entity. The
center of that brush will be
the point around which it is rotated. It will rotate around the Z
axis by default. You can
check either the X_AXIS or Y_AXIS box to change that.
"speed" determines how fast it moves; default value is 100.
"dmg" damage to inflict when blocked (2 default)
REVERSE will cause the it to rotate in the opposite direction.
*/
8 years ago
void CFuncRotating::Spawn()
7 years ago
{
m_iState = STATE_OFF;
m_fCurSpeed = 0; //LRC
// set final pitch. Must not be PITCH_NORM, since we
// plan on pitch shifting later.
m_pitch = PITCH_NORM - 1;
// maintain compatibility with previous maps
8 years ago
if( m_flVolume == 0.0 )
7 years ago
m_flVolume = 1.0;
// if the designer didn't set a sound attenuation, default to one.
m_flAttenuation = ATTN_NORM;
8 years ago
if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_SMALLRADIUS) )
7 years ago
{
m_flAttenuation = ATTN_IDLE;
}
8 years ago
else if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_MEDIUMRADIUS ) )
7 years ago
{
m_flAttenuation = ATTN_STATIC;
}
8 years ago
else if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_LARGERADIUS ) )
7 years ago
{
m_flAttenuation = ATTN_NORM;
}
// prevent divide by zero if level designer forgets friction!
if ( m_flFanFriction <= 0 ) //LRC - ensure it's not negative
{
m_flFanFriction = 1;
}
8 years ago
7 years ago
if (pev->movedir == g_vecZero)
{
8 years ago
if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS ) )
pev->movedir = Vector( 0, 0, 1 );
else if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS ) )
pev->movedir = Vector( 1, 0, 0 );
9 years ago
else
8 years ago
pev->movedir = Vector( 0, 1, 0 ); // y-axis
7 years ago
}
// check for reverse rotation
8 years ago
if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_BACKWARDS ) )
7 years ago
pev->movedir = pev->movedir * -1;
// some rotating objects like fake volumetric lights will not be solid.
8 years ago
if( FBitSet( pev->spawnflags, SF_ROTATING_NOT_SOLID ) )
7 years ago
{
pev->solid = SOLID_NOT;
pev->skin = CONTENTS_EMPTY;
8 years ago
pev->movetype = MOVETYPE_PUSH;
7 years ago
}
else
{
8 years ago
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
7 years ago
}
UTIL_SetOrigin(this, pev->origin);
SET_MODEL( ENT( pev ), STRING( pev->model ) );
7 years ago
9 years ago
SetUse( &CFuncRotating::RotatingUse );
7 years ago
// did level designer forget to assign speed?
8 years ago
if( pev->speed <= 0 )
7 years ago
pev->speed = 0;
// Removed this per level designers request. -- JAY
8 years ago
// if( pev->dmg == 0 )
7 years ago
// pev->dmg = 2;
// instant-use brush?
//LRC - start immediately if unnamed, too.
if ( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT) || FStringNull(pev->targetname) )
8 years ago
{
7 years ago
SetThink(&CFuncRotating :: WaitForStart );
SetNextThink( 1.5 ); // leave a magic delay for client to start up
8 years ago
}
7 years ago
// can this brush inflict pain?
8 years ago
if( FBitSet( pev->spawnflags, SF_BRUSH_HURT ) )
7 years ago
{
9 years ago
SetTouch( &CFuncRotating::HurtTouch );
7 years ago
}
8 years ago
Precache();
9 years ago
}
7 years ago
8 years ago
void CFuncRotating::Precache( void )
7 years ago
{
const char* szSoundFile = STRING( pev->message );
BOOL NullSound = FALSE;
7 years ago
// set up fan sounds
8 years ago
if( !FStringNull( pev->message ) && strlen( szSoundFile ) > 0 )
7 years ago
{
// if a path is set for a wave, use it
}
else
7 years ago
{
// otherwise use preset sound
8 years ago
switch( m_sounds )
7 years ago
{
case 1:
szSoundFile = "fans/fan1.wav";
7 years ago
break;
case 2:
szSoundFile = "fans/fan2.wav";
7 years ago
break;
case 3:
szSoundFile = "fans/fan3.wav";
7 years ago
break;
case 4:
szSoundFile = "fans/fan4.wav";
7 years ago
break;
case 5:
szSoundFile = "fans/fan5.wav";
7 years ago
break;
case 0:
default:
szSoundFile = "common/null.wav";
NullSound = TRUE;
break;
7 years ago
}
}
8 years ago
if( !NullSound )
PRECACHE_SOUND( szSoundFile );
pev->noiseRunning = MAKE_STRING( szSoundFile );
8 years ago
if( pev->avelocity != g_vecZero )
7 years ago
{
// if fan was spinning, and we went through transition or save/restore,
// make sure we restart the sound. 1.5 sec delay is magic number. KDB
9 years ago
SetThink( &CFuncRotating::SpinUp );
7 years ago
SetNextThink( 1.5 );
}
}
void CFuncRotating :: WaitForStart()
{
if (gpGlobals->time > 1) // has the client started yet?
{
SUB_CallUseToggle();
}
else
{
SetNextThink( 0.1 );
}
}
//
// Touch - will hurt others based on how fast the brush is spinning
//
8 years ago
void CFuncRotating::HurtTouch( CBaseEntity *pOther )
7 years ago
{
8 years ago
entvars_t *pevOther = pOther->pev;
7 years ago
// we can't hurt this thing, so we're not concerned with it
8 years ago
if( !pevOther->takedamage )
7 years ago
return;
// calculate damage based on rotation speed
pev->dmg = m_fCurSpeed / 10; //LRC
// pev->dmg = pev->avelocity.Length() / 10;
8 years ago
pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH );
pevOther->velocity = ( pevOther->origin - VecBModelOrigin( pev ) ).Normalize() * pev->dmg;
7 years ago
}
//
// RampPitchVol - ramp pitch and volume up to final values, based on difference
// between how fast we're going vs how fast we plan to go
//
#define FANPITCHMIN 30
#define FANPITCHMAX 100
8 years ago
void CFuncRotating::RampPitchVol( int fUp )
7 years ago
{
float fvol;
float fpitch;
int pitch;
float speedfactor = m_fCurSpeed/pev->speed;
8 years ago
7 years ago
fvol = m_flVolume * speedfactor; // slowdown volume ramps down to 0
fpitch = FANPITCHMIN + (FANPITCHMAX - FANPITCHMIN) * speedfactor;
8 years ago
pitch = (int)fpitch;
if( pitch == PITCH_NORM )
pitch = PITCH_NORM - 1;
7 years ago
// change the fan's vol and pitch
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
8 years ago
fvol, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch );
7 years ago
}
//
// SpinUp - accelerates a non-moving func_rotating up to it's speed
//
8 years ago
void CFuncRotating::SpinUp( void )
7 years ago
{
//Vector vecAVel;//rotational velocity
SetNextThink( 0.1 );
m_fCurSpeed = m_fCurSpeed + ( pev->speed * m_flFanFriction );
UTIL_SetAvelocity(this, pev->movedir * m_fCurSpeed);
//pev->avelocity = pev->avelocity + ( pev->movedir * ( pev->speed * m_flFanFriction ) );
//vecAVel = pev->avelocity;// cache entity's rotational velocity
// if we've met or exceeded target speed, set target speed and stop thinking
if ( m_fCurSpeed >= pev->speed )
{
m_iState = STATE_ON;
m_fCurSpeed = pev->speed;
UTIL_SetAvelocity(this, pev->movedir * pev->speed);
//pev->avelocity = pev->movedir * pev->speed;// set speed in case we overshot
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
8 years ago
m_flVolume, m_flAttenuation, SND_CHANGE_PITCH | SND_CHANGE_VOL, FANPITCHMAX );
9 years ago
SetThink( &CFuncRotating::Rotate );
7 years ago
Rotate();
}
else
{
8 years ago
RampPitchVol( TRUE );
7 years ago
}
}
//
// SpinDown - decelerates a moving func_rotating to a standstill.
//
8 years ago
void CFuncRotating::SpinDown( void )
7 years ago
{
SetNextThink( 0.1 );
m_fCurSpeed = m_fCurSpeed - ( pev->speed * m_flFanFriction );
UTIL_SetAvelocity(this, pev->movedir * m_fCurSpeed);
//pev->avelocity = pev->avelocity - ( pev->movedir * ( pev->speed * m_flFanFriction ) );//spin down slower than spinup
// if we've met or exceeded target speed, set target speed and stop thinking
if (m_fCurSpeed <= 0)
{
m_iState = STATE_OFF;
m_fCurSpeed = 0;
UTIL_SetAvelocity(this, g_vecZero);
//pev->avelocity = g_vecZero;// set speed in case we overshot
8 years ago
7 years ago
// stop sound, we're done
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, STRING( pev->noiseRunning /* Stop */ ),
0, 0, SND_STOP, (int)m_pitch );
7 years ago
9 years ago
SetThink( &CFuncRotating::Rotate );
7 years ago
Rotate();
}
else
{
8 years ago
RampPitchVol( FALSE );
7 years ago
}
}
8 years ago
void CFuncRotating::Rotate( void )
7 years ago
{
SetNextThink( 10 );
}
//=========================================================
// Rotating Use - when a rotating brush is triggered
//=========================================================
8 years ago
void CFuncRotating::RotatingUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
if (!ShouldToggle(useType)) return;
// is this a brush that should accelerate and decelerate when turned on/off (fan)?
8 years ago
if( FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) )
7 years ago
{
// fan is spinning, so stop it.
if ( m_fCurSpeed != 0 )
// if ( pev->avelocity != g_vecZero )
{
m_iState = STATE_TURN_OFF;
9 years ago
SetThink( &CFuncRotating::SpinDown );
//EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, STRING( pev->noiseStop ),
8 years ago
// m_flVolume, m_flAttenuation, 0, m_pitch );
7 years ago
SetNextThink( 0.1 );
}
else// fan is not moving, so start it
{
m_iState = STATE_TURN_ON;
9 years ago
SetThink( &CFuncRotating::SpinUp );
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, STRING( pev->noiseRunning ),
8 years ago
0.01, m_flAttenuation, 0, FANPITCHMIN );
7 years ago
SetNextThink( 0.1 );
}
}
else // if ( !FBitSet ( pev->spawnflags, SF_BRUSH_ACCDCC ) )//this is a normal start/stop brush.
{
if ( m_fCurSpeed != 0 ) //LRC
// if ( pev->avelocity != g_vecZero )
{
m_iState = STATE_OFF;
// play stopping sound here
9 years ago
SetThink( &CFuncRotating::SpinDown );
// EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, STRING( pev->noiseStop ),
8 years ago
// m_flVolume, m_flAttenuation, 0, m_pitch );
7 years ago
SetNextThink( 0.1 );
// pev->avelocity = g_vecZero;
}
else
{
m_iState = STATE_ON;
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning),
8 years ago
m_flVolume, m_flAttenuation, 0, FANPITCHMAX );
7 years ago
//LRC
m_fCurSpeed = pev->speed;
UTIL_SetAvelocity(this, pev->movedir * pev->speed);
// pev->avelocity = pev->movedir * pev->speed;
9 years ago
SetThink( &CFuncRotating::Rotate );
7 years ago
Rotate();
}
}
}
//
// RotatingBlocked - An entity has blocked the brush
//
8 years ago
void CFuncRotating::Blocked( CBaseEntity *pOther )
7 years ago
{
8 years ago
pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH );
7 years ago
}
//#endif
class CPendulum : public CBaseEntity
{
public:
8 years ago
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
7 years ago
void EXPORT SwingThink( void );
8 years ago
void EXPORT PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
7 years ago
void EXPORT StopThink( void );
8 years ago
void Touch( CBaseEntity *pOther );
void EXPORT RopeTouch( CBaseEntity *pOther );// this touch func makes the pendulum a rope
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
void Blocked( CBaseEntity *pOther );
7 years ago
virtual STATE GetState( void ) { return (pev->speed)?STATE_ON:STATE_OFF; }
8 years ago
static TYPEDESCRIPTION m_SaveData[];
float m_accel; // Acceleration
float m_distance;
float m_time;
float m_damp;
float m_maxSpeed;
float m_dampSpeed;
vec3_t m_center;
vec3_t m_start;
7 years ago
};
LINK_ENTITY_TO_CLASS( func_pendulum, CPendulum )
7 years ago
TYPEDESCRIPTION CPendulum::m_SaveData[] =
7 years ago
{
DEFINE_FIELD( CPendulum, m_accel, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_distance, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_time, FIELD_TIME ),
DEFINE_FIELD( CPendulum, m_damp, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_maxSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_dampSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_center, FIELD_VECTOR ),
DEFINE_FIELD( CPendulum, m_start, FIELD_VECTOR ),
};
IMPLEMENT_SAVERESTORE( CPendulum, CBaseEntity )
7 years ago
8 years ago
void CPendulum::KeyValue( KeyValueData *pkvd )
7 years ago
{
8 years ago
if( FStrEq( pkvd->szKeyName, "distance" ) )
7 years ago
{
8 years ago
m_distance = atof( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "axes"))
{
UTIL_StringToVector( (float*)(pev->movedir), pkvd->szValue );
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "damp" ) )
7 years ago
{
8 years ago
m_damp = atof( pkvd->szValue ) * 0.001;
7 years ago
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
8 years ago
void CPendulum::Spawn( void )
7 years ago
{
// set the axis of rotation
8 years ago
CBaseToggle::AxisDir( pev );
7 years ago
8 years ago
if( FBitSet( pev->spawnflags, SF_DOOR_PASSABLE ) )
pev->solid = SOLID_NOT;
7 years ago
else
8 years ago
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
7 years ago
UTIL_SetOrigin(this, pev->origin);
8 years ago
SET_MODEL( ENT( pev ), STRING( pev->model ) );
7 years ago
8 years ago
if( m_distance == 0 )
7 years ago
return;
8 years ago
if( pev->speed == 0 )
7 years ago
pev->speed = 100;
8 years ago
m_accel = ( pev->speed * pev->speed ) / ( 2 * fabs( m_distance ) ); // Calculate constant acceleration from speed and distance
7 years ago
m_maxSpeed = pev->speed;
m_start = pev->angles;
8 years ago
m_center = pev->angles + ( m_distance * 0.5 ) * pev->movedir;
7 years ago
8 years ago
if( FBitSet( pev->spawnflags, SF_BRUSH_ROTATE_INSTANT ) )
{
7 years ago
SetThink(&CPendulum :: SUB_CallUseToggle );
SetNextThink( 0.1 );
}
pev->speed = 0;
9 years ago
SetUse( &CPendulum::PendulumUse );
7 years ago
8 years ago
if( FBitSet( pev->spawnflags, SF_PENDULUM_SWING ) )
7 years ago
{
9 years ago
SetTouch( &CPendulum::RopeTouch );
7 years ago
}
}
8 years ago
void CPendulum::PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
if (!ShouldToggle(useType)) return;
8 years ago
if( pev->speed ) // Pendulum is moving, stop it and auto-return if necessary
7 years ago
{
8 years ago
if( FBitSet( pev->spawnflags, SF_PENDULUM_AUTO_RETURN ) )
7 years ago
{
8 years ago
float delta;
7 years ago
8 years ago
delta = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles, m_start );
7 years ago
UTIL_SetAvelocity(this, m_maxSpeed * pev->movedir); //LRC
//pev->avelocity = m_maxSpeed * pev->movedir;
SetNextThink(delta / m_maxSpeed);
SetThink(&CPendulum ::StopThink);
}
else
{
pev->speed = 0; // Dead stop
DontThink();
UTIL_SetAvelocity(this, g_vecZero); //LRC
//pev->avelocity = g_vecZero;
}
}
else
{
SetNextThink(0.1); // start the pendulum moving
SetThink(&CPendulum ::SwingThink);
m_time = gpGlobals->time; // Save time to calculate dt
m_dampSpeed = m_maxSpeed;
}
}
void CPendulum :: StopThink( void )
{
UTIL_SetAngles(this, m_start); //LRC
//pev->angles = m_start;
pev->speed = 0;
DontThink();
UTIL_SetAvelocity(this, g_vecZero); //LRC
//pev->avelocity = g_vecZero;
}
void CPendulum::Blocked( CBaseEntity *pOther )
{
m_time = gpGlobals->time;
}
void CPendulum :: SwingThink( void )
{
float delta, dt;
8 years ago
delta = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles, m_center );
7 years ago
dt = gpGlobals->time - m_time; // How much time has passed?
m_time = gpGlobals->time; // Remember the last time called
8 years ago
if( delta > 0 && m_accel > 0 )
7 years ago
pev->speed -= m_accel * dt; // Integrate velocity
else
pev->speed += m_accel * dt;
8 years ago
if( pev->speed > m_maxSpeed )
7 years ago
pev->speed = m_maxSpeed;
8 years ago
else if( pev->speed < -m_maxSpeed )
7 years ago
pev->speed = -m_maxSpeed;
// scale the destdelta vector by the time spent traveling to get velocity
UTIL_SetAvelocity(this, pev->speed * pev->movedir); //LRC
//pev->avelocity = pev->speed * pev->movedir;
// ALERT(at_console, "m_damp %f, m_dampSpeed %f\n", m_damp, m_dampSpeed);
// ALERT(at_console, "SwingThink: delta %f, dt %f, speed %f, avel %f %f %f\n", delta, dt, pev->speed, pev->avelocity.x, pev->avelocity.y, pev->avelocity.z);
// Call this again
SetNextThink(0.1);
SetThink(&CPendulum ::SwingThink);
// if (m_pMoveWith) // correct MoveWith problems associated with fast-thinking entities
// UTIL_AssignOrigin(this, m_vecMoveWithOffset + m_pMoveWith->pev->origin);
8 years ago
if( m_damp )
7 years ago
{
m_dampSpeed -= m_damp * m_dampSpeed * dt;
8 years ago
if( m_dampSpeed < 30.0 )
7 years ago
{
UTIL_SetAngles(this, m_center); //LRC
//pev->angles = m_center;
pev->speed = 0;
ALERT(at_console, "**CANCELLING pendulum think!\n");
7 years ago
DontThink();
UTIL_SetAvelocity(this, g_vecZero); //LRC
//pev->avelocity = g_vecZero;
}
8 years ago
else if( pev->speed > m_dampSpeed )
7 years ago
pev->speed = m_dampSpeed;
8 years ago
else if( pev->speed < -m_dampSpeed )
7 years ago
pev->speed = -m_dampSpeed;
}
}
8 years ago
void CPendulum::Touch( CBaseEntity *pOther )
7 years ago
{
8 years ago
entvars_t *pevOther = pOther->pev;
7 years ago
8 years ago
if( pev->dmg <= 0 )
7 years ago
return;
// we can't hurt this thing, so we're not concerned with it
8 years ago
if( !pevOther->takedamage )
7 years ago
return;
// calculate damage based on rotation speed
float damage = pev->dmg * pev->speed * 0.01;
8 years ago
if( damage < 0 )
7 years ago
damage = -damage;
pOther->TakeDamage( pev, pev, damage, DMG_CRUSH );
8 years ago
pevOther->velocity = ( pevOther->origin - VecBModelOrigin( pev ) ).Normalize() * damage;
7 years ago
}
8 years ago
void CPendulum::RopeTouch( CBaseEntity *pOther )
7 years ago
{
8 years ago
entvars_t *pevOther = pOther->pev;
7 years ago
8 years ago
if( !pOther->IsPlayer() )
{
// not a player!
ALERT( at_console, "Not a client\n" );
7 years ago
return;
}
8 years ago
if( ENT( pevOther ) == pev->enemy )
{
// this player already on the rope.
7 years ago
return;
}
pev->enemy = pOther->edict();
pevOther->velocity = g_vecZero;
pevOther->movetype = MOVETYPE_NONE;
}