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.
1581 lines
43 KiB
1581 lines
43 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ==== |
|
// |
|
// Purpose: Implements buttons. |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "doors.h" |
|
#include "ndebugoverlay.h" |
|
#include "spark.h" |
|
#include "vstdlib/random.h" |
|
#include "engine/IEngineSound.h" |
|
#include "tier1/strtools.h" |
|
#include "buttons.h" |
|
#include "eventqueue.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
void PlayLockSounds( CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton ); |
|
string_t MakeButtonSound( int sound ); // get string of button sound number |
|
|
|
|
|
#define SF_BUTTON_DONTMOVE 1 |
|
#define SF_ROTBUTTON_NOTSOLID 1 |
|
#define SF_BUTTON_TOGGLE 32 // button stays pushed until reactivated |
|
#define SF_BUTTON_TOUCH_ACTIVATES 256 // Button fires when touched. |
|
#define SF_BUTTON_DAMAGE_ACTIVATES 512 // Button fires when damaged. |
|
#define SF_BUTTON_USE_ACTIVATES 1024 // Button fires when used. |
|
#define SF_BUTTON_LOCKED 2048 // Whether the button is initially locked. |
|
#define SF_BUTTON_SPARK_IF_OFF 4096 // button sparks in OFF state |
|
#define SF_BUTTON_JIGGLE_ON_USE_LOCKED 8192 // whether to jiggle if someone uses us when we're locked |
|
|
|
BEGIN_DATADESC( CBaseButton ) |
|
|
|
DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ), |
|
DEFINE_FIELD( m_fStayPushed, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_fRotating, FIELD_BOOLEAN ), |
|
|
|
DEFINE_FIELD( m_bLockedSound, FIELD_CHARACTER ), |
|
DEFINE_FIELD( m_bLockedSentence, FIELD_CHARACTER ), |
|
DEFINE_FIELD( m_bUnlockedSound, FIELD_CHARACTER ), |
|
DEFINE_FIELD( m_bUnlockedSentence, FIELD_CHARACTER ), |
|
DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ), |
|
DEFINE_FIELD( m_flUseLockedTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_bSolidBsp, FIELD_BOOLEAN ), |
|
|
|
DEFINE_KEYFIELD( m_sounds, FIELD_INTEGER, "sounds" ), |
|
|
|
// DEFINE_FIELD( m_ls, FIELD_SOUNDNAME ), // This is restored in Precache() |
|
// DEFINE_FIELD( m_nState, FIELD_INTEGER ), |
|
|
|
// Function Pointers |
|
DEFINE_FUNCTION( ButtonTouch ), |
|
DEFINE_FUNCTION( ButtonSpark ), |
|
DEFINE_FUNCTION( TriggerAndWait ), |
|
DEFINE_FUNCTION( ButtonReturn ), |
|
DEFINE_FUNCTION( ButtonBackHome ), |
|
DEFINE_FUNCTION( ButtonUse ), |
|
|
|
// Inputs |
|
DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "Press", InputPress ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "PressIn", InputPressIn ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "PressOut", InputPressOut ), |
|
|
|
// Outputs |
|
DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ), |
|
DEFINE_OUTPUT( m_OnPressed, "OnPressed" ), |
|
DEFINE_OUTPUT( m_OnUseLocked, "OnUseLocked" ), |
|
DEFINE_OUTPUT( m_OnIn, "OnIn" ), |
|
DEFINE_OUTPUT( m_OnOut, "OnOut" ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
LINK_ENTITY_TO_CLASS( func_button, CBaseButton ); |
|
|
|
IMPLEMENT_SERVERCLASS_ST( CBaseButton, DT_BaseButton ) |
|
END_SEND_TABLE() |
|
|
|
|
|
void CBaseButton::Precache( void ) |
|
{ |
|
// get door button sounds, for doors which require buttons to open |
|
if (m_bLockedSound) |
|
{ |
|
m_ls.sLockedSound = MakeButtonSound( (int)m_bLockedSound ); |
|
PrecacheScriptSound(m_ls.sLockedSound.ToCStr()); |
|
} |
|
|
|
if (m_bUnlockedSound) |
|
{ |
|
m_ls.sUnlockedSound = MakeButtonSound( (int)m_bUnlockedSound ); |
|
PrecacheScriptSound(m_ls.sUnlockedSound.ToCStr()); |
|
} |
|
|
|
// get sentence group names, for doors which are directly 'touched' to open |
|
|
|
switch (m_bLockedSentence) |
|
{ |
|
case 1: m_ls.sLockedSentence = MAKE_STRING("NA"); break; // access denied |
|
case 2: m_ls.sLockedSentence = MAKE_STRING("ND"); break; // security lockout |
|
case 3: m_ls.sLockedSentence = MAKE_STRING("NF"); break; // blast door |
|
case 4: m_ls.sLockedSentence = MAKE_STRING("NFIRE"); break; // fire door |
|
case 5: m_ls.sLockedSentence = MAKE_STRING("NCHEM"); break; // chemical door |
|
case 6: m_ls.sLockedSentence = MAKE_STRING("NRAD"); break; // radiation door |
|
case 7: m_ls.sLockedSentence = MAKE_STRING("NCON"); break; // gen containment |
|
case 8: m_ls.sLockedSentence = MAKE_STRING("NH"); break; // maintenance door |
|
case 9: m_ls.sLockedSentence = MAKE_STRING("NG"); break; // broken door |
|
|
|
default: m_ls.sLockedSentence = NULL_STRING; break; |
|
} |
|
|
|
switch (m_bUnlockedSentence) |
|
{ |
|
case 1: m_ls.sUnlockedSentence = MAKE_STRING("EA"); break; // access granted |
|
case 2: m_ls.sUnlockedSentence = MAKE_STRING("ED"); break; // security door |
|
case 3: m_ls.sUnlockedSentence = MAKE_STRING("EF"); break; // blast door |
|
case 4: m_ls.sUnlockedSentence = MAKE_STRING("EFIRE"); break; // fire door |
|
case 5: m_ls.sUnlockedSentence = MAKE_STRING("ECHEM"); break; // chemical door |
|
case 6: m_ls.sUnlockedSentence = MAKE_STRING("ERAD"); break; // radiation door |
|
case 7: m_ls.sUnlockedSentence = MAKE_STRING("ECON"); break; // gen containment |
|
case 8: m_ls.sUnlockedSentence = MAKE_STRING("EH"); break; // maintenance door |
|
|
|
default: m_ls.sUnlockedSentence = NULL_STRING; break; |
|
} |
|
|
|
if ( m_sNoise != NULL_STRING ) |
|
{ |
|
PrecacheScriptSound( STRING( m_sNoise ) ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Cache user-entity-field values until spawn is called. |
|
// Input : szKeyName - |
|
// szValue - |
|
// Output : Returns true if handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseButton::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
if (FStrEq(szKeyName, "locked_sound")) |
|
{ |
|
m_bLockedSound = atof(szValue); |
|
} |
|
else if (FStrEq(szKeyName, "locked_sentence")) |
|
{ |
|
m_bLockedSentence = atof(szValue); |
|
} |
|
else if (FStrEq(szKeyName, "unlocked_sound")) |
|
{ |
|
m_bUnlockedSound = atof(szValue); |
|
} |
|
else if (FStrEq(szKeyName, "unlocked_sentence")) |
|
{ |
|
m_bUnlockedSentence = atof(szValue); |
|
} |
|
else |
|
{ |
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Locks the button. If locked, the button will play the locked sound |
|
// when the player tries to use it. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::Lock() |
|
{ |
|
m_bLocked = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Unlocks the button, making it able to be pressed again. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::Unlock() |
|
{ |
|
m_bLocked = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Locks the button. If locked, the button will play the locked sound |
|
// when the player tries to use it. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::InputLock( inputdata_t &inputdata ) |
|
{ |
|
Lock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Unlocks the button, making it able to be pressed again. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::InputUnlock( inputdata_t &inputdata ) |
|
{ |
|
Unlock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Presses or unpresses the button. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::Press( CBaseEntity *pActivator, BUTTON_CODE eCode ) |
|
{ |
|
if ( ( eCode == BUTTON_PRESS ) && ( m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( ( eCode == BUTTON_ACTIVATE ) && ( m_toggle_state == TS_GOING_UP || m_toggle_state == TS_AT_TOP ) ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( ( eCode == BUTTON_RETURN ) && ( m_toggle_state == TS_GOING_DOWN || m_toggle_state == TS_AT_BOTTOM ) ) |
|
{ |
|
return; |
|
} |
|
|
|
// FIXME: consolidate all the button press code into one place! |
|
if (m_bLocked) |
|
{ |
|
// play button locked sound |
|
PlayLockSounds(this, &m_ls, TRUE, TRUE); |
|
return; |
|
} |
|
|
|
// Temporarily disable the touch function, until movement is finished. |
|
SetTouch( NULL ); |
|
|
|
if ( ( eCode == BUTTON_PRESS ) && ( m_toggle_state == TS_AT_TOP ) || |
|
( ( eCode == BUTTON_RETURN ) && ( m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP ) ) ) |
|
{ |
|
if ( m_sNoise != NULL_STRING ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_VOICE; |
|
ep.m_pSoundName = (char*)STRING(m_sNoise); |
|
ep.m_flVolume = 1; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
m_OnPressed.FireOutput(pActivator, this); |
|
ButtonReturn(); |
|
} |
|
else if ( ( eCode == BUTTON_PRESS ) || |
|
( ( eCode == BUTTON_ACTIVATE ) && ( m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN ) ) ) |
|
{ |
|
m_OnPressed.FireOutput(pActivator, this); |
|
ButtonActivate(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Presses the button. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::InputPress( inputdata_t &inputdata ) |
|
{ |
|
Press( inputdata.pActivator, BUTTON_PRESS ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Presses the button, sending it to the top/pressed position. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::InputPressIn( inputdata_t &inputdata ) |
|
{ |
|
Press( inputdata.pActivator, BUTTON_ACTIVATE ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Unpresses the button, sending it to the unpressed/bottom position. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::InputPressOut( inputdata_t &inputdata ) |
|
{ |
|
Press( inputdata.pActivator, BUTTON_RETURN ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: We have been damaged. Possibly activate, depending on our flags. |
|
// Input : pInflictor - |
|
// pAttacker - |
|
// flDamage - |
|
// bitsDamageType - |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
int CBaseButton::OnTakeDamage( const CTakeDamageInfo &info ) |
|
{ |
|
m_OnDamaged.FireOutput(m_hActivator, this); |
|
|
|
// dvsents2: remove obselete health keyvalue from func_button |
|
if (!HasSpawnFlags(SF_BUTTON_DAMAGE_ACTIVATES) && (m_iHealth == 0)) |
|
{ |
|
return(0); |
|
} |
|
|
|
BUTTON_CODE code = ButtonResponseToTouch(); |
|
|
|
if ( code == BUTTON_NOTHING ) |
|
return 0; |
|
|
|
m_hActivator = info.GetAttacker(); |
|
|
|
// dvsents2: why would activator be NULL here? |
|
if ( m_hActivator == NULL ) |
|
return 0; |
|
|
|
if (m_bLocked) |
|
{ |
|
return(0); |
|
} |
|
|
|
// Temporarily disable the touch function, until movement is finished. |
|
SetTouch( NULL ); |
|
|
|
if ( code == BUTTON_RETURN ) |
|
{ |
|
if ( m_sNoise != NULL_STRING ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_VOICE; |
|
ep.m_pSoundName = (char*)STRING(m_sNoise); |
|
ep.m_flVolume = 1; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
m_OnPressed.FireOutput(m_hActivator, this); |
|
ButtonReturn(); |
|
} |
|
else |
|
{ |
|
// code == BUTTON_ACTIVATE |
|
m_OnPressed.FireOutput(m_hActivator, this); |
|
ButtonActivate( ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
void CBaseButton::Spawn( ) |
|
{ |
|
//---------------------------------------------------- |
|
//determine sounds for buttons |
|
//a sound of 0 should not make a sound |
|
//---------------------------------------------------- |
|
if ( m_sounds ) |
|
{ |
|
m_sNoise = MakeButtonSound( m_sounds ); |
|
PrecacheScriptSound(m_sNoise.ToCStr()); |
|
} |
|
else |
|
{ |
|
m_sNoise = NULL_STRING; |
|
} |
|
|
|
Precache(); |
|
|
|
if ( HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state |
|
{ |
|
SetThink ( &CBaseButton::ButtonSpark ); |
|
SetNextThink( gpGlobals->curtime + 0.5f );// no hurry, make sure everything else spawns |
|
} |
|
|
|
// Convert movedir from angles to a vector |
|
QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z ); |
|
AngleVectors( angMoveDir, &m_vecMoveDir ); |
|
|
|
SetMoveType( MOVETYPE_PUSH ); |
|
SetSolid( SOLID_BSP ); |
|
SetModel( STRING( GetModelName() ) ); |
|
|
|
if (m_flSpeed == 0) |
|
{ |
|
m_flSpeed = 40; |
|
} |
|
|
|
m_takedamage = DAMAGE_YES; |
|
|
|
if (m_flWait == 0) |
|
{ |
|
m_flWait = 1; |
|
} |
|
|
|
if (m_flLip == 0) |
|
{ |
|
m_flLip = 4; |
|
} |
|
|
|
m_toggle_state = TS_AT_BOTTOM; |
|
m_vecPosition1 = GetLocalOrigin(); |
|
|
|
// Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big |
|
Vector vecButtonOBB = CollisionProp()->OBBSize(); |
|
vecButtonOBB -= Vector( 2, 2, 2 ); |
|
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * (DotProductAbs( m_vecMoveDir, vecButtonOBB ) - m_flLip)); |
|
|
|
// Is this a non-moving button? |
|
if ( ((m_vecPosition2 - m_vecPosition1).Length() < 1) || HasSpawnFlags(SF_BUTTON_DONTMOVE) ) |
|
{ |
|
m_vecPosition2 = m_vecPosition1; |
|
} |
|
|
|
m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); |
|
m_fRotating = FALSE; |
|
|
|
if (HasSpawnFlags(SF_BUTTON_LOCKED)) |
|
{ |
|
m_bLocked = true; |
|
} |
|
|
|
// |
|
// If using activates the button, set its use function. |
|
// |
|
if (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES)) |
|
{ |
|
SetUse(&CBaseButton::ButtonUse); |
|
} |
|
else |
|
{ |
|
SetUse(NULL); |
|
} |
|
|
|
// |
|
// If touching activates the button, set its touch function. |
|
// |
|
if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) |
|
{ |
|
SetTouch( &CBaseButton::ButtonTouch ); |
|
} |
|
else |
|
{ |
|
SetTouch ( NULL ); |
|
} |
|
|
|
CreateVPhysics(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
bool CBaseButton::CreateVPhysics() |
|
{ |
|
VPhysicsInitShadow( false, false ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Button sound table. |
|
// Also used by CBaseDoor to get 'touched' door lock/unlock sounds |
|
// Input : sound - index of sound to look up. |
|
// Output : Returns a pointer to the corresponding sound file. |
|
//----------------------------------------------------------------------------- |
|
string_t MakeButtonSound( int sound ) |
|
{ |
|
char tmp[1024]; |
|
Q_snprintf( tmp, sizeof(tmp), "Buttons.snd%d", sound ); |
|
return AllocPooledString(tmp); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Think function that emits sparks at random intervals. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::ButtonSpark ( void ) |
|
{ |
|
SetThink ( &CBaseButton::ButtonSpark ); |
|
SetNextThink( gpGlobals->curtime + 0.1 + random->RandomFloat ( 0, 1.5 ) );// spark again at random interval |
|
|
|
DoSpark( this, WorldSpaceCenter(), 1, 1, true, vec3_origin ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when someone uses us whilst we are locked. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseButton::OnUseLocked( CBaseEntity *pActivator ) |
|
{ |
|
PlayLockSounds(this, &m_ls, TRUE, TRUE); |
|
|
|
if ( gpGlobals->curtime > m_flUseLockedTime ) |
|
{ |
|
m_OnUseLocked.FireOutput( pActivator, this ); |
|
m_flUseLockedTime = gpGlobals->curtime + 0.5; |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Use function that starts the button moving. |
|
// Input : pActivator - |
|
// pCaller - |
|
// useType - |
|
// value - |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::ButtonUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) |
|
{ |
|
// Ignore touches if button is moving, or pushed-in and waiting to auto-come-out. |
|
// UNDONE: Should this use ButtonResponseToTouch() too? |
|
if (m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) |
|
return; |
|
|
|
if (m_bLocked) |
|
{ |
|
OnUseLocked( pActivator ); |
|
return; |
|
} |
|
|
|
m_hActivator = pActivator; |
|
|
|
if ( m_toggle_state == TS_AT_TOP) |
|
{ |
|
// |
|
// If it's a toggle button it can return now. Otherwise, it will either |
|
// return on its own or will stay pressed indefinitely. |
|
// |
|
if ( HasSpawnFlags(SF_BUTTON_TOGGLE)) |
|
{ |
|
if ( m_sNoise != NULL_STRING ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_VOICE; |
|
ep.m_pSoundName = (char*)STRING(m_sNoise); |
|
ep.m_flVolume = 1; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
m_OnPressed.FireOutput(m_hActivator, this); |
|
ButtonReturn(); |
|
} |
|
} |
|
else |
|
{ |
|
m_OnPressed.FireOutput(m_hActivator, this); |
|
ButtonActivate( ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns a code indicating how the button should respond to being touched. |
|
// Output : Returns one of the following: |
|
// BUTTON_NOTHING - do nothing |
|
// BUTTON_RETURN - |
|
// BUTTON_ACTIVATE - act as if pressed |
|
//----------------------------------------------------------------------------- |
|
CBaseButton::BUTTON_CODE CBaseButton::ButtonResponseToTouch( void ) |
|
{ |
|
// Ignore touches if button is moving, or pushed-in and waiting to auto-come-out. |
|
if (m_toggle_state == TS_GOING_UP || |
|
m_toggle_state == TS_GOING_DOWN || |
|
(m_toggle_state == TS_AT_TOP && !m_fStayPushed && !HasSpawnFlags(SF_BUTTON_TOGGLE) ) ) |
|
return BUTTON_NOTHING; |
|
|
|
if (m_toggle_state == TS_AT_TOP) |
|
{ |
|
if ( HasSpawnFlags(SF_BUTTON_TOGGLE) && !m_fStayPushed) |
|
{ |
|
return BUTTON_RETURN; |
|
} |
|
} |
|
else |
|
return BUTTON_ACTIVATE; |
|
|
|
return BUTTON_NOTHING; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Touch function that activates the button if it responds to touch. |
|
// Input : pOther - The entity that touched us. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::ButtonTouch( CBaseEntity *pOther ) |
|
{ |
|
// Ignore touches by anything but players |
|
if ( !pOther->IsPlayer() ) |
|
return; |
|
|
|
m_hActivator = pOther; |
|
|
|
BUTTON_CODE code = ButtonResponseToTouch(); |
|
|
|
if ( code == BUTTON_NOTHING ) |
|
return; |
|
|
|
if (!UTIL_IsMasterTriggered(m_sMaster, pOther) || m_bLocked) |
|
{ |
|
// play button locked sound |
|
PlayLockSounds(this, &m_ls, TRUE, TRUE); |
|
return; |
|
} |
|
|
|
// Temporarily disable the touch function, until movement is finished. |
|
SetTouch( NULL ); |
|
|
|
if ( code == BUTTON_RETURN ) |
|
{ |
|
if ( m_sNoise != NULL_STRING ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_VOICE; |
|
ep.m_pSoundName = (char*)STRING(m_sNoise); |
|
ep.m_flVolume = 1; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
m_OnPressed.FireOutput(m_hActivator, this); |
|
ButtonReturn(); |
|
} |
|
else |
|
{ |
|
// code == BUTTON_ACTIVATE |
|
m_OnPressed.FireOutput(m_hActivator, this); |
|
ButtonActivate( ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Starts the button moving "in/up". |
|
// Input : *pOther - |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::ButtonActivate( void ) |
|
{ |
|
if ( m_sNoise != NULL_STRING ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_VOICE; |
|
ep.m_pSoundName = (char*)STRING(m_sNoise); |
|
ep.m_flVolume = 1; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked) |
|
{ |
|
// button is locked, play locked sound |
|
PlayLockSounds(this, &m_ls, TRUE, TRUE); |
|
return; |
|
} |
|
else |
|
{ |
|
// button is unlocked, play unlocked sound |
|
PlayLockSounds(this, &m_ls, FALSE, TRUE); |
|
} |
|
|
|
ASSERT(m_toggle_state == TS_AT_BOTTOM); |
|
m_toggle_state = TS_GOING_UP; |
|
|
|
SetMoveDone( &CBaseButton::TriggerAndWait ); |
|
if (!m_fRotating) |
|
LinearMove( m_vecPosition2, m_flSpeed); |
|
else |
|
AngularMove( m_vecAngle2, m_flSpeed); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Enables or disables the use capability based on our spawnflags. |
|
//----------------------------------------------------------------------------- |
|
int CBaseButton::ObjectCaps(void) |
|
{ |
|
return((BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | |
|
(HasSpawnFlags(SF_BUTTON_USE_ACTIVATES) ? (FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS) : 0)); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Button has reached the "pressed/top" position. Fire its OnIn output, |
|
// and pause before returning to "unpressed/bottom". |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::TriggerAndWait( void ) |
|
{ |
|
ASSERT(m_toggle_state == TS_GOING_UP); |
|
|
|
if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked) |
|
{ |
|
return; |
|
} |
|
|
|
m_toggle_state = TS_AT_TOP; |
|
|
|
// |
|
// Re-instate touches if the button is of the toggle variety. |
|
// |
|
if (m_fStayPushed || HasSpawnFlags(SF_BUTTON_TOGGLE ) ) |
|
{ |
|
if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) |
|
{ |
|
SetTouch(&CBaseButton::ButtonTouch); |
|
} |
|
else |
|
{ |
|
// BUGBUG: ALL buttons no longer respond to touch |
|
SetTouch (NULL); |
|
} |
|
} |
|
|
|
// |
|
// If button automatically comes back out, start it moving out. |
|
// |
|
else |
|
{ |
|
SetNextThink( gpGlobals->curtime + m_flWait ); |
|
SetThink( &CBaseButton::ButtonReturn ); |
|
} |
|
|
|
m_nState = 1; // use alternate textures |
|
|
|
m_OnIn.FireOutput(m_hActivator, this); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Starts the button moving "out/down". |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::ButtonReturn( void ) |
|
{ |
|
ASSERT(m_toggle_state == TS_AT_TOP); |
|
m_toggle_state = TS_GOING_DOWN; |
|
|
|
SetMoveDone( &CBaseButton::ButtonBackHome ); |
|
if (!m_fRotating) |
|
LinearMove( m_vecPosition1, m_flSpeed); |
|
else |
|
AngularMove( m_vecAngle1, m_flSpeed); |
|
|
|
m_nState = 0; // use normal textures |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Button has returned to the "unpressed/bottom" position. Fire its |
|
// OnOut output and stop moving. |
|
//----------------------------------------------------------------------------- |
|
void CBaseButton::ButtonBackHome( void ) |
|
{ |
|
ASSERT(m_toggle_state == TS_GOING_DOWN); |
|
m_toggle_state = TS_AT_BOTTOM; |
|
|
|
m_OnOut.FireOutput(m_hActivator, this); |
|
|
|
// |
|
// Re-instate touch method, movement cycle is complete. |
|
// |
|
if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) |
|
{ |
|
SetTouch( &CBaseButton::ButtonTouch ); |
|
} |
|
else |
|
{ |
|
// BUGBUG: ALL buttons no longer respond to touch |
|
SetTouch ( NULL ); |
|
} |
|
|
|
// reset think for a sparking button |
|
if (HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) ) |
|
{ |
|
SetThink ( &CBaseButton::ButtonSpark ); |
|
SetNextThink( gpGlobals->curtime + 0.5f );// no hurry |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
int CBaseButton::DrawDebugTextOverlays() |
|
{ |
|
int text_offset = BaseClass::DrawDebugTextOverlays(); |
|
|
|
if (m_debugOverlays & OVERLAY_TEXT_BIT) |
|
{ |
|
static char *pszStates[] = |
|
{ |
|
"Pressed", |
|
"Unpressed", |
|
"Pressing...", |
|
"Unpressing...", |
|
"<UNKNOWN STATE>", |
|
}; |
|
|
|
char tempstr[255]; |
|
|
|
int nState = m_toggle_state; |
|
if ( ( nState < 0 ) || ( nState > 3 ) ) |
|
{ |
|
nState = 4; |
|
} |
|
|
|
Q_snprintf( tempstr, sizeof(tempstr), "State: %s", pszStates[nState] ); |
|
EntityText( text_offset, tempstr, 0 ); |
|
text_offset++; |
|
|
|
Q_snprintf( tempstr, sizeof(tempstr), "%s", m_bLocked ? "Locked" : "Unlocked" ); |
|
EntityText( text_offset, tempstr, 0 ); |
|
text_offset++; |
|
} |
|
return text_offset; |
|
} |
|
|
|
|
|
// |
|
// Rotating button (aka "lever") |
|
// |
|
LINK_ENTITY_TO_CLASS( func_rot_button, CRotButton ); |
|
|
|
|
|
void CRotButton::Spawn( void ) |
|
{ |
|
//---------------------------------------------------- |
|
//determine sounds for buttons |
|
//a sound of 0 should not make a sound |
|
//---------------------------------------------------- |
|
if ( m_sounds ) |
|
{ |
|
m_sNoise = MakeButtonSound( m_sounds ); |
|
PrecacheScriptSound(m_sNoise.ToCStr()); |
|
} |
|
else |
|
{ |
|
m_sNoise = NULL_STRING; |
|
} |
|
|
|
// set the axis of rotation |
|
CBaseToggle::AxisDir(); |
|
|
|
// check for clockwise rotation |
|
if ( HasSpawnFlags( SF_DOOR_ROTATE_BACKWARDS) ) |
|
{ |
|
m_vecMoveAng = m_vecMoveAng * -1; |
|
} |
|
|
|
SetMoveType( MOVETYPE_PUSH ); |
|
|
|
SetSolid( SOLID_VPHYSICS ); |
|
if ( HasSpawnFlags( SF_ROTBUTTON_NOTSOLID ) ) |
|
{ |
|
AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
} |
|
|
|
SetModel( STRING( GetModelName() ) ); |
|
|
|
if (m_flSpeed == 0) |
|
m_flSpeed = 40; |
|
|
|
if (m_flWait == 0) |
|
m_flWait = 1; |
|
|
|
if (m_iHealth > 0) |
|
{ |
|
m_takedamage = DAMAGE_YES; |
|
} |
|
|
|
m_toggle_state = TS_AT_BOTTOM; |
|
m_vecAngle1 = GetLocalAngles(); |
|
m_vecAngle2 = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance; |
|
ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating button start/end positions are equal\n"); |
|
|
|
m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); |
|
m_fRotating = TRUE; |
|
|
|
SetUse(&CRotButton::ButtonUse); |
|
|
|
// |
|
// If touching activates the button, set its touch function. |
|
// |
|
if (!HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) |
|
{ |
|
SetTouch ( NULL ); |
|
} |
|
else |
|
{ |
|
SetTouch( &CRotButton::ButtonTouch ); |
|
} |
|
|
|
CreateVPhysics(); |
|
} |
|
|
|
|
|
bool CRotButton::CreateVPhysics( void ) |
|
{ |
|
VPhysicsInitShadow( false, false ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CMomentaryRotButton spawnflags |
|
//----------------------------------------------------------------------------- |
|
#define SF_MOMENTARY_DOOR 1 |
|
#define SF_MOMENTARY_NOT_USABLE 2 |
|
#define SF_MOMENTARY_AUTO_RETURN 16 |
|
|
|
|
|
BEGIN_DATADESC( CMomentaryRotButton ) |
|
|
|
DEFINE_FIELD( m_lastUsed, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_start, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_end, FIELD_VECTOR ), |
|
DEFINE_FIELD( m_IdealYaw, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ), |
|
DEFINE_FIELD( m_bUpdateTarget, FIELD_BOOLEAN ), |
|
|
|
DEFINE_KEYFIELD( m_direction, FIELD_INTEGER, "StartDirection" ), |
|
DEFINE_KEYFIELD( m_returnSpeed, FIELD_FLOAT, "returnspeed" ), |
|
DEFINE_KEYFIELD( m_flStartPosition, FIELD_FLOAT, "StartPosition"), |
|
DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ), |
|
|
|
// Function Pointers |
|
DEFINE_FUNCTION( UseMoveDone ), |
|
DEFINE_FUNCTION( ReturnMoveDone ), |
|
DEFINE_FUNCTION( SetPositionMoveDone ), |
|
DEFINE_FUNCTION( UpdateThink ), |
|
|
|
// Inputs |
|
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPosition", InputSetPosition ), |
|
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPositionImmediately", InputSetPositionImmediately ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "_DisableUpdateTarget", InputDisableUpdateTarget ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "_EnableUpdateTarget", InputEnableUpdateTarget ), |
|
|
|
// Outputs |
|
DEFINE_OUTPUT( m_Position, "Position" ), |
|
DEFINE_OUTPUT( m_OnUnpressed, "OnUnpressed" ), |
|
DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ), |
|
DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ), |
|
DEFINE_OUTPUT( m_OnReachedPosition, "OnReachedPosition" ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), |
|
DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ) |
|
|
|
END_DATADESC() |
|
|
|
|
|
LINK_ENTITY_TO_CLASS( momentary_rot_button, CMomentaryRotButton ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when spawning, after keyvalues have been handled. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::Spawn( void ) |
|
{ |
|
CBaseToggle::AxisDir(); |
|
|
|
m_bUpdateTarget = true; |
|
|
|
if ( m_flSpeed == 0 ) |
|
{ |
|
m_flSpeed = 100; |
|
} |
|
|
|
// Clamp start position and issue bounds warning |
|
if (m_flStartPosition < 0.0 || m_flStartPosition > 1.0) |
|
{ |
|
Warning("WARNING: Momentary door (%s) start position not between 0 and 1. Clamping.\n",GetDebugName()); |
|
m_flStartPosition = clamp(m_IdealYaw, 0, 1); |
|
} |
|
|
|
// Check direction fields (for backward compatibility) |
|
if (m_direction != 1 && m_direction != -1) |
|
{ |
|
m_direction = 1; |
|
} |
|
|
|
if (m_flMoveDistance < 0) |
|
{ |
|
m_vecMoveAng = m_vecMoveAng * -1; |
|
m_flMoveDistance = -m_flMoveDistance; |
|
} |
|
|
|
m_start = GetLocalAngles() - m_vecMoveAng * m_flMoveDistance * m_flStartPosition; |
|
m_end = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance * (1-m_flStartPosition); |
|
|
|
m_IdealYaw = m_flStartPosition; |
|
|
|
// Force start direction at end points |
|
if (m_flStartPosition == 0.0) |
|
{ |
|
m_direction = -1; |
|
} |
|
else if (m_flStartPosition == 1.0) |
|
{ |
|
m_direction = 1; |
|
} |
|
|
|
if (HasSpawnFlags(SF_BUTTON_LOCKED)) |
|
{ |
|
m_bLocked = true; |
|
} |
|
|
|
if ( HasSpawnFlags( SF_BUTTON_USE_ACTIVATES ) ) |
|
{ |
|
if ( m_sounds ) |
|
{ |
|
m_sNoise = MakeButtonSound( m_sounds ); |
|
PrecacheScriptSound(m_sNoise.ToCStr()); |
|
} |
|
else |
|
{ |
|
m_sNoise = NULL_STRING; |
|
} |
|
|
|
m_lastUsed = 0; |
|
UpdateTarget(0,this); |
|
} |
|
|
|
SetSolid( SOLID_VPHYSICS ); |
|
if (HasSpawnFlags(SF_ROTBUTTON_NOTSOLID)) |
|
{ |
|
AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
} |
|
|
|
SetMoveType( MOVETYPE_PUSH ); |
|
SetModel( STRING( GetModelName() ) ); |
|
|
|
CreateVPhysics(); |
|
|
|
// Slam the object back to solid - if we really want it to be solid. |
|
if ( m_bSolidBsp ) |
|
{ |
|
SetSolid( SOLID_BSP ); |
|
} |
|
|
|
m_bDisabled = false; |
|
} |
|
|
|
int CMomentaryRotButton::ObjectCaps( void ) |
|
{ |
|
int flags = BaseClass::ObjectCaps(); |
|
if (!HasSpawnFlags(SF_BUTTON_USE_ACTIVATES)) |
|
{ |
|
return flags; |
|
} |
|
else |
|
{ |
|
return (flags | FCAP_CONTINUOUS_USE | FCAP_USE_IN_RADIUS); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CMomentaryRotButton::CreateVPhysics( void ) |
|
{ |
|
VPhysicsInitShadow( false, false ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::PlaySound( void ) |
|
{ |
|
if ( m_sNoise == NULL_STRING ) |
|
return; |
|
|
|
CPASAttenuationFilter filter( this ); |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_VOICE; |
|
ep.m_pSoundName = (char*)STRING(m_sNoise); |
|
ep.m_flVolume = 1; |
|
ep.m_SoundLevel = SNDLVL_NORM; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns a given angular position as a value along our motion from 0 to 1. |
|
// Input : vecAngles - |
|
//----------------------------------------------------------------------------- |
|
float CMomentaryRotButton::GetPos( const QAngle &vecAngles ) |
|
{ |
|
float flScale = 1; |
|
if (( m_vecMoveAng[0] < 0 ) || ( m_vecMoveAng[1] < 0 ) || ( m_vecMoveAng[2] < 0 )) |
|
{ |
|
flScale = -1; |
|
} |
|
|
|
float flPos = flScale * CBaseToggle::AxisDelta( m_spawnflags, vecAngles, m_start ) / m_flMoveDistance; |
|
return( clamp( flPos, 0, 1 )); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : flPosition |
|
//------------------------------------------------------------------------------ |
|
void CMomentaryRotButton::InputSetPosition( inputdata_t &inputdata ) |
|
{ |
|
m_IdealYaw = clamp( inputdata.value.Float(), 0, 1 ); |
|
|
|
float flCurPos = GetPos( GetLocalAngles() ); |
|
if ( flCurPos < m_IdealYaw ) |
|
{ |
|
// Moving forward (from start to end). |
|
SetLocalAngularVelocity( m_flSpeed * m_vecMoveAng ); |
|
m_direction = 1; |
|
} |
|
else if ( flCurPos > m_IdealYaw ) |
|
{ |
|
// Moving backward (from end to start). |
|
SetLocalAngularVelocity( -m_flSpeed * m_vecMoveAng ); |
|
m_direction = -1; |
|
} |
|
else |
|
{ |
|
// We're there already; nothing to do. |
|
SetLocalAngularVelocity( vec3_angle ); |
|
return; |
|
} |
|
|
|
SetMoveDone( &CMomentaryRotButton::SetPositionMoveDone ); |
|
|
|
SetThink( &CMomentaryRotButton::UpdateThink ); |
|
SetNextThink( gpGlobals->curtime ); |
|
|
|
// |
|
// Think again in 0.1 seconds or the time that it will take us to reach our movement goal, |
|
// whichever is the shorter interval. This prevents us from overshooting and stuttering when we |
|
// are told to change position in very small increments. |
|
// |
|
QAngle vecNewAngles = m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ); |
|
float flAngleDelta = fabs( AxisDelta( m_spawnflags, vecNewAngles, GetLocalAngles() )); |
|
float dt = flAngleDelta / m_flSpeed; |
|
if ( dt < TICK_INTERVAL ) |
|
{ |
|
dt = TICK_INTERVAL; |
|
float speed = flAngleDelta / TICK_INTERVAL; |
|
SetLocalAngularVelocity( speed * m_vecMoveAng * m_direction ); |
|
} |
|
dt = clamp( dt, TICK_INTERVAL, TICK_INTERVAL * 6); |
|
|
|
SetMoveDoneTime( dt ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : |
|
// Input : flPosition |
|
//------------------------------------------------------------------------------ |
|
void CMomentaryRotButton::InputSetPositionImmediately( inputdata_t &inputdata ) |
|
{ |
|
m_IdealYaw = clamp( inputdata.value.Float(), 0, 1 ); |
|
SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: Turns off target updates so that we can change the wheel's position |
|
// without changing the target's position. Used for jiggling when locked. |
|
//------------------------------------------------------------------------------ |
|
void CMomentaryRotButton::InputDisableUpdateTarget( inputdata_t &inputdata ) |
|
{ |
|
m_bUpdateTarget = false; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: Turns target updates back on (after jiggling). |
|
//------------------------------------------------------------------------------ |
|
void CMomentaryRotButton::InputEnableUpdateTarget( inputdata_t &inputdata ) |
|
{ |
|
m_bUpdateTarget = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Locks the button. If locked, the button will play the locked sound |
|
// when the player tries to use it. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::Lock() |
|
{ |
|
BaseClass::Lock(); |
|
|
|
SetLocalAngularVelocity( vec3_angle ); |
|
SetMoveDoneTime( -1 ); |
|
SetMoveDone( NULL ); |
|
|
|
SetNextThink( TICK_NEVER_THINK ); |
|
SetThink( NULL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Unlocks the button, making it able to be pressed again. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::Unlock() |
|
{ |
|
BaseClass::Unlock(); |
|
|
|
SetMoveDone( &CMomentaryRotButton::ReturnMoveDone ); |
|
|
|
// Delay before autoreturn. |
|
SetMoveDoneTime( 0.1f ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Fires the appropriate outputs at the extremes of motion. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::OutputMovementComplete( void ) |
|
{ |
|
if (m_IdealYaw == 1.0) |
|
{ |
|
m_OnFullyClosed.FireOutput(this, this); |
|
} |
|
else if (m_IdealYaw == 0.0) |
|
{ |
|
m_OnFullyOpen.FireOutput(this, this); |
|
} |
|
|
|
m_OnReachedPosition.FireOutput( this, this ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: MoveDone function for the SetPosition input handler. Tracks our |
|
// progress toward a movement goal and updates our outputs. |
|
//------------------------------------------------------------------------------ |
|
void CMomentaryRotButton::SetPositionMoveDone(void) |
|
{ |
|
float flCurPos = GetPos( GetLocalAngles() ); |
|
|
|
if ((( flCurPos >= m_IdealYaw ) && ( m_direction == 1 )) || |
|
(( flCurPos <= m_IdealYaw ) && ( m_direction == -1 ))) |
|
{ |
|
// |
|
// We reached or surpassed our movement goal. |
|
// |
|
SetLocalAngularVelocity( vec3_angle ); |
|
// BUGBUG: Won't this get the player stuck? |
|
SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) ); |
|
SetNextThink( TICK_NEVER_THINK ); |
|
SetMoveDoneTime( -1 ); |
|
UpdateTarget( m_IdealYaw, this ); |
|
OutputMovementComplete(); |
|
return; |
|
} |
|
|
|
// TODO: change this to use a Think function like ReturnThink. |
|
QAngle vecNewAngles = m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ); |
|
float flAngleDelta = fabs( AxisDelta( m_spawnflags, vecNewAngles, GetLocalAngles() )); |
|
float dt = flAngleDelta / m_flSpeed; |
|
if ( dt < TICK_INTERVAL ) |
|
{ |
|
dt = TICK_INTERVAL; |
|
float speed = flAngleDelta / TICK_INTERVAL; |
|
SetLocalAngularVelocity( speed * m_vecMoveAng * m_direction ); |
|
} |
|
dt = clamp( dt, TICK_INTERVAL, TICK_INTERVAL * 6); |
|
|
|
SetMoveDoneTime( dt ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pActivator - |
|
// pCaller - |
|
// useType - |
|
// value - |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) |
|
{ |
|
if ( m_bDisabled == true ) |
|
return; |
|
|
|
if (m_bLocked) |
|
{ |
|
if ( OnUseLocked( pActivator ) && HasSpawnFlags( SF_BUTTON_JIGGLE_ON_USE_LOCKED ) ) |
|
{ |
|
// Jiggle two degrees. |
|
float flDist = 2.0 / m_flMoveDistance; |
|
|
|
// Must be first! |
|
g_EventQueue.AddEvent( this, "_DisableUpdateTarget", 0, this, this ); |
|
|
|
variant_t value; |
|
value.SetFloat( flDist ); |
|
g_EventQueue.AddEvent( this, "SetPosition", value, 0.01, this, this ); |
|
|
|
value.SetFloat( 0.0 ); |
|
g_EventQueue.AddEvent( this, "SetPosition", value, 0.1, this, this ); |
|
|
|
value.SetFloat( 0.5 * flDist ); |
|
g_EventQueue.AddEvent( this, "SetPosition", value, 0.2, this, this ); |
|
|
|
value.SetFloat( 0.0 ); |
|
g_EventQueue.AddEvent( this, "SetPosition", value, 0.3, this, this ); |
|
|
|
// Must be last! And must be late enough to cover the settling time. |
|
g_EventQueue.AddEvent( this, "_EnableUpdateTarget", 0.5, this, this ); |
|
} |
|
|
|
return; |
|
} |
|
|
|
// |
|
// Reverse our direction and play movement sound every time the player |
|
// pauses between uses. |
|
// |
|
bool bPlaySound = false; |
|
|
|
if ( !m_lastUsed ) |
|
{ |
|
bPlaySound = true; |
|
m_direction = -m_direction; |
|
|
|
//Alert that we've been pressed |
|
m_OnPressed.FireOutput( m_hActivator, this ); |
|
} |
|
|
|
m_lastUsed = 1; |
|
|
|
float flPos = GetPos( GetLocalAngles() ); |
|
UpdateSelf( flPos, bPlaySound ); |
|
|
|
// |
|
// Think every frame while we are moving. |
|
// HACK: Don't reset the think time if we already have a pending think. |
|
// This works around an issue with host_thread_mode > 0 when the player's |
|
// clock runs ahead of the server. |
|
// |
|
if ( !m_pfnThink ) |
|
{ |
|
SetThink( &CMomentaryRotButton::UpdateThink ); |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles changing direction at the extremes of our range of motion |
|
// and updating our avelocity while being used by the player. |
|
// Input : value - Number from 0 to 1 indicating our desired position within |
|
// our range of motion, 0 = start, 1 = end. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::UpdateSelf( float value, bool bPlaySound ) |
|
{ |
|
// |
|
// Set our move clock to 0.1 seconds in the future so we stop spinning unless we are |
|
// used again before then. |
|
// |
|
SetMoveDoneTime( 0.1 ); |
|
|
|
// |
|
// If we hit the end, zero our avelocity and snap to the end angles. |
|
// |
|
if ( m_direction > 0 && value >= 1.0 ) |
|
{ |
|
SetLocalAngularVelocity( vec3_angle ); |
|
SetLocalAngles( m_end ); |
|
|
|
m_OnFullyClosed.FireOutput(this, this); |
|
return; |
|
} |
|
// |
|
// If we returned to the start, zero our avelocity and snap to the start angles. |
|
// |
|
else if ( m_direction < 0 && value <= 0 ) |
|
{ |
|
SetLocalAngularVelocity( vec3_angle ); |
|
SetLocalAngles( m_start ); |
|
|
|
m_OnFullyOpen.FireOutput(this, this); |
|
return; |
|
} |
|
|
|
if ( bPlaySound ) |
|
{ |
|
PlaySound(); |
|
} |
|
|
|
SetLocalAngularVelocity( ( m_direction * m_flSpeed ) * m_vecMoveAng ); |
|
SetMoveDone( &CMomentaryRotButton::UseMoveDone ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Updates the value of our position, firing any targets. |
|
// Input : value - New position, from 0 - 1. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::UpdateTarget( float value, CBaseEntity *pActivator ) |
|
{ |
|
if ( !m_bUpdateTarget ) |
|
return; |
|
|
|
if (m_Position.Get() != value) |
|
{ |
|
m_Position.Set(value, pActivator, this); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles the end of motion caused by player use. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::UseMoveDone( void ) |
|
{ |
|
SetLocalAngularVelocity( vec3_angle ); |
|
|
|
// Make sure our targets stop where we stopped. |
|
float flPos = GetPos( GetLocalAngles() ); |
|
UpdateTarget( flPos, this ); |
|
|
|
// Alert that we've been unpressed |
|
m_OnUnpressed.FireOutput( m_hActivator, this ); |
|
|
|
m_lastUsed = 0; |
|
|
|
if ( !HasSpawnFlags( SF_BUTTON_TOGGLE ) && m_returnSpeed > 0 ) |
|
{ |
|
SetMoveDone( &CMomentaryRotButton::ReturnMoveDone ); |
|
m_direction = -1; |
|
|
|
// Delay before autoreturn. |
|
SetMoveDoneTime( 0.1f ); |
|
} |
|
else |
|
{ |
|
SetThink( NULL ); |
|
SetMoveDone( NULL ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: MoveDone function for rotating back to the start position. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::ReturnMoveDone( void ) |
|
{ |
|
float value = GetPos( GetLocalAngles() ); |
|
if ( value <= 0 ) |
|
{ |
|
// |
|
// Got back to the start, stop spinning. |
|
// |
|
SetLocalAngularVelocity( vec3_angle ); |
|
SetLocalAngles( m_start ); |
|
|
|
UpdateTarget( 0, NULL ); |
|
|
|
SetMoveDoneTime( -1 ); |
|
SetMoveDone( NULL ); |
|
|
|
SetNextThink( TICK_NEVER_THINK ); |
|
SetThink( NULL ); |
|
} |
|
else |
|
{ |
|
SetLocalAngularVelocity( -m_returnSpeed * m_vecMoveAng ); |
|
SetMoveDoneTime( 0.1f ); |
|
|
|
SetThink( &CMomentaryRotButton::UpdateThink ); |
|
SetNextThink( gpGlobals->curtime + 0.01f ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Think function for updating target as we move. |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::UpdateThink( void ) |
|
{ |
|
float value = GetPos( GetLocalAngles() ); |
|
UpdateTarget( value, NULL ); |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Draw any debug text overlays |
|
// Output : Current text offset from the top |
|
//----------------------------------------------------------------------------- |
|
int CMomentaryRotButton::DrawDebugTextOverlays(void) |
|
{ |
|
int text_offset = BaseClass::DrawDebugTextOverlays(); |
|
|
|
if (m_debugOverlays & OVERLAY_TEXT_BIT) |
|
{ |
|
char tempstr[255]; |
|
|
|
Q_snprintf(tempstr,sizeof(tempstr),"QAngle: %.2f %.2f %.2f", GetLocalAngles()[0], GetLocalAngles()[1], GetLocalAngles()[2]); |
|
EntityText(text_offset,tempstr,0); |
|
text_offset++; |
|
|
|
Q_snprintf(tempstr,sizeof(tempstr),"AVelocity: %.2f %.2f %.2f", GetLocalAngularVelocity()[0], GetLocalAngularVelocity()[1], GetLocalAngularVelocity()[2]); |
|
EntityText(text_offset,tempstr,0); |
|
text_offset++; |
|
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",m_IdealYaw); |
|
EntityText(text_offset,tempstr,0); |
|
text_offset++; |
|
|
|
float flCurPos = GetPos(GetLocalAngles()); |
|
Q_snprintf(tempstr,sizeof(tempstr),"Current Pos: %3.3f",flCurPos); |
|
EntityText(text_offset,tempstr,0); |
|
text_offset++; |
|
|
|
Q_snprintf(tempstr,sizeof(tempstr),"Direction: %s",(m_direction == 1) ? "Forward" : "Backward"); |
|
EntityText(text_offset,tempstr,0); |
|
text_offset++; |
|
|
|
} |
|
return text_offset; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input hander that starts the spawner |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::InputEnable( inputdata_t &inputdata ) |
|
{ |
|
Enable(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input hander that stops the spawner |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::InputDisable( inputdata_t &inputdata ) |
|
{ |
|
Disable(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Start the spawner |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::Enable( void ) |
|
{ |
|
m_bDisabled = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stop the spawner |
|
//----------------------------------------------------------------------------- |
|
void CMomentaryRotButton::Disable( void ) |
|
{ |
|
m_bDisabled = true; |
|
}
|
|
|