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.

1765 lines
46 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.
*
****/
/*
===== buttons.cpp ========================================================
button-related code
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "saverestore.h"
#include "doors.h"
#define SF_BUTTON_DONTMOVE 1
8 years ago
#define SF_ROTBUTTON_NOTSOLID 1
7 years ago
#define SF_BUTTON_ONLYDIRECT 16 //LRC - button can't be used through walls.
#define SF_BUTTON_TOGGLE 32 // button stays pushed until reactivated
8 years ago
#define SF_BUTTON_SPARK_IF_OFF 64 // button sparks in OFF state
7 years ago
#define SF_BUTTON_NOT_SOLID 128 // button isn't solid
#define SF_BUTTON_TOUCH_ONLY 256 // button must be touched to be used.
#define SF_BUTTON_USEKEY 512 // change the reaction of the button to the USE key.
// (i.e. if it's meant to be ignored, don't ignore it; otherwise ignore it.)
#define SF_GLOBAL_SET 1 // Set global state to initial state on spawn
class CEnvGlobal : public CPointEntity
{
public:
8 years ago
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
7 years ago
8 years ago
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
7 years ago
8 years ago
string_t m_globalstate;
int m_triggermode;
int m_initialstate;
7 years ago
};
TYPEDESCRIPTION CEnvGlobal::m_SaveData[] =
{
DEFINE_FIELD( CEnvGlobal, m_globalstate, FIELD_STRING ),
DEFINE_FIELD( CEnvGlobal, m_triggermode, FIELD_INTEGER ),
DEFINE_FIELD( CEnvGlobal, m_initialstate, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CEnvGlobal, CBaseEntity )
7 years ago
LINK_ENTITY_TO_CLASS( env_global, CEnvGlobal )
7 years ago
void CEnvGlobal::KeyValue( KeyValueData *pkvd )
{
pkvd->fHandled = TRUE;
8 years ago
if( FStrEq( pkvd->szKeyName, "globalstate" ) ) // State name
7 years ago
m_globalstate = ALLOC_STRING( pkvd->szValue );
8 years ago
else if( FStrEq( pkvd->szKeyName, "triggermode" ) )
7 years ago
m_triggermode = atoi( pkvd->szValue );
8 years ago
else if( FStrEq( pkvd->szKeyName, "initialstate" ) )
7 years ago
m_initialstate = atoi( pkvd->szValue );
8 years ago
else
7 years ago
CPointEntity::KeyValue( pkvd );
}
void CEnvGlobal::Spawn( void )
{
8 years ago
if( !m_globalstate )
7 years ago
{
8 years ago
REMOVE_ENTITY( ENT( pev ) );
7 years ago
return;
}
8 years ago
if( FBitSet( pev->spawnflags, SF_GLOBAL_SET ) )
7 years ago
{
8 years ago
if( !gGlobalState.EntityInTable( m_globalstate ) )
7 years ago
gGlobalState.EntityAdd( m_globalstate, gpGlobals->mapname, (GLOBALESTATE)m_initialstate );
}
}
void CEnvGlobal::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
GLOBALESTATE oldState = gGlobalState.EntityGetState( m_globalstate );
GLOBALESTATE newState;
switch( m_triggermode )
{
case 0:
newState = GLOBAL_OFF;
break;
case 1:
newState = GLOBAL_ON;
break;
case 2:
newState = GLOBAL_DEAD;
break;
default:
case 3:
8 years ago
if( oldState == GLOBAL_ON )
7 years ago
newState = GLOBAL_OFF;
8 years ago
else if( oldState == GLOBAL_OFF )
7 years ago
newState = GLOBAL_ON;
else
newState = oldState;
}
8 years ago
if( gGlobalState.EntityInTable( m_globalstate ) )
7 years ago
gGlobalState.EntitySetState( m_globalstate, newState );
else
gGlobalState.EntityAdd( m_globalstate, gpGlobals->mapname, newState );
}
//==================================================
//LRC- a simple entity, just maintains a state
//==================================================
#define SF_ENVSTATE_START_ON 1
#define SF_ENVSTATE_DEBUG 2
class CEnvState : public CPointEntity
{
public:
void Spawn( void );
void Think( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
BOOL IsLockedByMaster( void ) { return !UTIL_IsMasterTriggered(m_sMaster, NULL); };
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
virtual STATE GetState() { return m_iState; }
static TYPEDESCRIPTION m_SaveData[];
STATE m_iState;
float m_fTurnOnTime;
float m_fTurnOffTime;
int m_sMaster;
};
void CEnvState::Spawn( void )
{
if (pev->spawnflags & SF_ENVSTATE_START_ON)
m_iState = STATE_ON;
else
m_iState = STATE_OFF;
}
TYPEDESCRIPTION CEnvState::m_SaveData[] =
{
DEFINE_FIELD( CEnvState, m_iState, FIELD_INTEGER ),
DEFINE_FIELD( CEnvState, m_fTurnOnTime, FIELD_INTEGER ),
DEFINE_FIELD( CEnvState, m_fTurnOffTime, FIELD_INTEGER ),
DEFINE_FIELD( CEnvState, m_sMaster, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CEnvState, CPointEntity );
LINK_ENTITY_TO_CLASS( env_state, CEnvState );
void CEnvState::KeyValue( KeyValueData *pkvd )
{
pkvd->fHandled = TRUE;
if ( FStrEq(pkvd->szKeyName, "turnontime") )
m_fTurnOnTime = atof( pkvd->szValue );
else if ( FStrEq(pkvd->szKeyName, "turnofftime") )
m_fTurnOffTime = atof( pkvd->szValue );
else if ( FStrEq(pkvd->szKeyName, "master") )
m_sMaster = ALLOC_STRING( pkvd->szValue );
else
CPointEntity::KeyValue( pkvd );
}
void CEnvState::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (!ShouldToggle(useType) || IsLockedByMaster())
{
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" ",STRING(pev->targetname));
7 years ago
if (IsLockedByMaster())
ALERT(at_console,"ignored trigger %s; locked by master \"%s\".\n",GetStringForUseType(useType),STRING(m_sMaster));
7 years ago
else if (useType == USE_ON)
ALERT(at_console,"ignored trigger USE_ON; already on\n");
7 years ago
else if (useType == USE_OFF)
ALERT(at_console,"ignored trigger USE_OFF; already off\n");
7 years ago
else
ALERT(at_console,"ignored trigger %s.\n",GetStringForUseType(useType));
7 years ago
}
return;
}
switch (GetState())
{
case STATE_ON:
case STATE_TURN_ON:
if (m_fTurnOffTime)
{
m_iState = STATE_TURN_OFF;
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" triggered; will turn off in %f seconds.\n", STRING(pev->targetname), m_fTurnOffTime);
7 years ago
}
SetNextThink( m_fTurnOffTime );
}
else
{
m_iState = STATE_OFF;
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" triggered, turned off", STRING(pev->targetname));
7 years ago
if (pev->target)
{
ALERT(at_console,": firing \"%s\"",STRING(pev->target));
7 years ago
if (pev->noise2)
ALERT(at_console," and \"%s\"",STRING(pev->noise2));
7 years ago
}
else if (pev->noise2)
ALERT(at_console,": firing \"%s\"",STRING(pev->noise2));
ALERT(at_console,".\n");
7 years ago
}
FireTargets(STRING(pev->target),pActivator,this,USE_OFF,0);
FireTargets(STRING(pev->noise2),pActivator,this,USE_TOGGLE,0);
DontThink();
}
break;
case STATE_OFF:
case STATE_TURN_OFF:
if (m_fTurnOnTime)
{
m_iState = STATE_TURN_ON;
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" triggered; will turn on in %f seconds.\n", STRING(pev->targetname), m_fTurnOnTime);
7 years ago
}
SetNextThink( m_fTurnOnTime );
}
else
{
m_iState = STATE_ON;
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" triggered, turned on",STRING(pev->targetname));
7 years ago
if (pev->target)
{
ALERT(at_console,": firing \"%s\"",STRING(pev->target));
7 years ago
if (pev->noise1)
ALERT(at_console," and \"%s\"",STRING(pev->noise1));
7 years ago
}
else if (pev->noise1)
ALERT(at_console,": firing \"%s\"", STRING(pev->noise1));
ALERT(at_console,".\n");
7 years ago
}
FireTargets(STRING(pev->target),pActivator,this,USE_ON,0);
FireTargets(STRING(pev->noise1),pActivator,this,USE_TOGGLE,0);
DontThink();
}
break;
case STATE_IN_USE:
break;
default:
break;
}
}
void CEnvState::Think( void )
{
if (m_iState == STATE_TURN_ON)
{
m_iState = STATE_ON;
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" turned itself on",STRING(pev->targetname));
7 years ago
if (pev->target)
{
ALERT(at_console,": firing %s",STRING(pev->target));
7 years ago
if (pev->noise1)
ALERT(at_console," and %s",STRING(pev->noise1));
7 years ago
}
else if (pev->noise1)
ALERT(at_console,": firing %s",STRING(pev->noise1));
ALERT(at_console,".\n");
7 years ago
}
FireTargets(STRING(pev->target),this,this,USE_ON,0);
FireTargets(STRING(pev->noise1),this,this,USE_TOGGLE,0);
}
else if (m_iState == STATE_TURN_OFF)
{
m_iState = STATE_OFF;
if (pev->spawnflags & SF_ENVSTATE_DEBUG)
{
ALERT(at_console,"DEBUG: env_state \"%s\" turned itself off",STRING(pev->targetname));
7 years ago
if (pev->target)
ALERT(at_console,": firing %s",STRING(pev->target));
7 years ago
if (pev->noise2)
ALERT(at_console," and %s",STRING(pev->noise2));
7 years ago
else if (pev->noise2)
ALERT(at_console,": firing %s",STRING(pev->noise2));
ALERT(at_console,".\n");
7 years ago
}
FireTargets(STRING(pev->target),this,this,USE_OFF,0);
FireTargets(STRING(pev->noise2),this,this,USE_TOGGLE,0);
}
}
//===========================
//LRC- the evil multisource...
//===========================
TYPEDESCRIPTION CMultiSource::m_SaveData[] =
{
//!!!BUGBUG FIX
DEFINE_ARRAY( CMultiSource, m_rgEntities, FIELD_EHANDLE, MS_MAX_TARGETS ),
DEFINE_ARRAY( CMultiSource, m_rgTriggered, FIELD_INTEGER, MS_MAX_TARGETS ),
DEFINE_FIELD( CMultiSource, m_iTotal, FIELD_INTEGER ),
DEFINE_FIELD( CMultiSource, m_globalstate, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CMultiSource, CBaseEntity )
LINK_ENTITY_TO_CLASS( multisource, CMultiSource )
7 years ago
//
// Cache user-entity-field values until spawn is called.
//
void CMultiSource::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "style" ) ||
FStrEq( pkvd->szKeyName, "height" ) ||
FStrEq( pkvd->szKeyName, "killtarget" ) ||
FStrEq( pkvd->szKeyName, "value1" ) ||
FStrEq( pkvd->szKeyName, "value2" ) ||
FStrEq( pkvd->szKeyName, "value3" ) )
7 years ago
pkvd->fHandled = TRUE;
8 years ago
else if( FStrEq( pkvd->szKeyName, "globalstate" ) )
7 years ago
{
m_globalstate = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
8 years ago
else
7 years ago
CPointEntity::KeyValue( pkvd );
}
#define SF_MULTI_FIREONCLOSE 1
#define SF_MULTI_INIT 2
void CMultiSource::Spawn()
{
// set up think for later registration
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
SetNextThink( 0.1 );
pev->spawnflags |= SF_MULTI_INIT; // Until it's initialized
8 years ago
SetThink( &CMultiSource::Register );
7 years ago
}
void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int i = 0;
// Find the entity in our list
8 years ago
while( i < m_iTotal )
if( m_rgEntities[i++] == pCaller )
7 years ago
break;
// if we didn't find it, report error and leave
8 years ago
if( i > m_iTotal )
7 years ago
{
if (pCaller->pev->targetname)
ALERT(at_console, "multisource \"%s\": Used by non-member %s \"%s\"\n", STRING(pev->targetname), STRING(pCaller->pev->classname), STRING(pCaller->pev->targetname));
7 years ago
else
ALERT(at_console, "multisource \"%s\": Used by non-member %s\n", STRING(pev->targetname), STRING(pCaller->pev->classname));
7 years ago
return;
}
// CONSIDER: a Use input to the multisource always toggles. Could check useType for ON/OFF/TOGGLE
// LRC- On could be meaningful. Off, sadly, can't work in the obvious manner.
// LRC (09/06/01)- er... why not?
// LRC (28/04/02)- that depends what the "obvious" manner is.
// store the state before the change, so we can compare it to the new state
STATE s = GetState();
// do the change
8 years ago
m_rgTriggered[i - 1] ^= 1;
7 years ago
// did we change state?
if ( s == GetState() )
return;
if ( s == STATE_ON && pev->netname)
{
// the change disabled me and I have a "fire on disable" field
ALERT( at_aiconsole, "Multisource %s deactivated (%d inputs)\n", STRING(pev->targetname), m_iTotal );
if ( m_globalstate )
FireTargets( STRING(pev->netname), NULL, this, USE_OFF, 0 );
else
FireTargets( STRING(pev->netname), NULL, this, USE_TOGGLE, 0 );
}
else if ( s == STATE_OFF )
{
// the change activated me
8 years ago
ALERT( at_aiconsole, "Multisource %s enabled (%d inputs)\n", STRING( pev->targetname ), m_iTotal );
7 years ago
USE_TYPE useType = USE_TOGGLE;
8 years ago
if( m_globalstate )
7 years ago
useType = USE_ON;
SUB_UseTargets( NULL, useType, 0 );
}
}
//LRC- while we're in STATE_OFF, mastered entities can't do anything.
STATE CMultiSource::GetState( void )
{
// Is everything triggered?
int i = 0;
// Still initializing?
8 years ago
if( pev->spawnflags & SF_MULTI_INIT )
7 years ago
return STATE_OFF;
8 years ago
while( i < m_iTotal )
7 years ago
{
8 years ago
if( m_rgTriggered[i] == 0 )
7 years ago
break;
i++;
}
8 years ago
if( i == m_iTotal )
7 years ago
{
8 years ago
if( !m_globalstate || gGlobalState.EntityGetState( m_globalstate ) == GLOBAL_ON )
7 years ago
return STATE_ON;
}
8 years ago
7 years ago
return STATE_OFF;
}
/*
8 years ago
void CMultiSource::Register( void )
7 years ago
{
8 years ago
edict_t *pentTarget = NULL;
7 years ago
m_iTotal = 0;
memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) );
SetThink(&CMultiSource::SUB_DoNothing);
// search for all entities which target this multisource (pev->targetname)
8 years ago
pentTarget = FIND_ENTITY_BY_STRING( NULL, "target", STRING( pev->targetname ) );
7 years ago
8 years ago
while( !FNullEnt( pentTarget ) && ( m_iTotal < MS_MAX_TARGETS ) )
7 years ago
{
8 years ago
CBaseEntity *pTarget = CBaseEntity::Instance( pentTarget );
if( pTarget )
7 years ago
m_rgEntities[m_iTotal++] = pTarget;
8 years ago
pentTarget = FIND_ENTITY_BY_STRING( pentTarget, "target", STRING( pev->targetname ) );
7 years ago
}
8 years ago
pentTarget = FIND_ENTITY_BY_STRING( NULL, "classname", "multi_manager" );
while( !FNullEnt( pentTarget ) && ( m_iTotal < MS_MAX_TARGETS ) )
7 years ago
{
8 years ago
CBaseEntity *pTarget = CBaseEntity::Instance( pentTarget );
if( pTarget && pTarget->HasTarget( pev->targetname ) )
7 years ago
m_rgEntities[m_iTotal++] = pTarget;
pentTarget = FIND_ENTITY_BY_STRING( pentTarget, "classname", "multi_manager" );
}
pev->spawnflags &= ~SF_MULTI_INIT;
}
*/
void CMultiSource::Register(void)
{
m_iTotal = 0;
memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) );
SetThink(&CMultiSource::SUB_DoNothing);
// search for all entities which target this multisource (pev->targetname)
CBaseEntity *pTarget = UTIL_FindEntityByTarget( NULL, STRING(pev->targetname) );
while (pTarget && (m_iTotal < MS_MAX_TARGETS))
{
m_rgEntities[m_iTotal++] = pTarget;
pTarget = UTIL_FindEntityByTarget( pTarget, STRING(pev->targetname));
}
pTarget = UTIL_FindEntityByClassname(NULL, "multi_manager");
while (pTarget && (m_iTotal < MS_MAX_TARGETS))
{
if ( pTarget->HasTarget(pev->targetname) )
m_rgEntities[m_iTotal++] = pTarget;
pTarget = UTIL_FindEntityByClassname( pTarget, "multi_manager" );
}
if (m_iTotal >= MS_MAX_TARGETS)
{
ALERT(at_console,"WARNING: There are too many entities targetting multisource \"%s\". (limit is %d)\n", STRING(pev->targetname), MS_MAX_TARGETS);
7 years ago
}
pev->spawnflags &= ~SF_MULTI_INIT;
}
//===================================
// func_button (= CBaseButton)
//===================================
//LRC - moved here from cbase.h to use the spawnflags defined in this file
// Buttons that don't take damage can be IMPULSE used
int CBaseButton::ObjectCaps( void )
{
return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) |
(pev->takedamage?0:FCAP_IMPULSE_USE) |
(pev->spawnflags & SF_BUTTON_ONLYDIRECT?FCAP_ONLYDIRECT_USE:0);
}
9 years ago
// CBaseButton
7 years ago
TYPEDESCRIPTION CBaseButton::m_SaveData[] =
{
DEFINE_FIELD( CBaseButton, m_fStayPushed, FIELD_BOOLEAN ),
DEFINE_FIELD( CBaseButton, m_fRotating, FIELD_BOOLEAN ),
DEFINE_FIELD( CBaseButton, m_sounds, FIELD_INTEGER ),
DEFINE_FIELD( CBaseButton, m_bLockedSound, FIELD_CHARACTER ),
DEFINE_FIELD( CBaseButton, m_bLockedSentence, FIELD_CHARACTER ),
DEFINE_FIELD( CBaseButton, m_bUnlockedSound, FIELD_CHARACTER ),
DEFINE_FIELD( CBaseButton, m_bUnlockedSentence, FIELD_CHARACTER ),
DEFINE_FIELD( CBaseButton, m_strChangeTarget, FIELD_STRING ),
8 years ago
//DEFINE_FIELD( CBaseButton, m_ls, FIELD_??? ), // This is restored in Precache()
7 years ago
};
IMPLEMENT_SAVERESTORE( CBaseButton, CBaseToggle )
7 years ago
void CBaseButton::Precache( void )
{
const char *pszSound;
7 years ago
8 years ago
if( FBitSet( pev->spawnflags, SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state
7 years ago
{
8 years ago
PRECACHE_SOUND( "buttons/spark1.wav" );
PRECACHE_SOUND( "buttons/spark2.wav" );
PRECACHE_SOUND( "buttons/spark3.wav" );
PRECACHE_SOUND( "buttons/spark4.wav" );
PRECACHE_SOUND( "buttons/spark5.wav" );
PRECACHE_SOUND( "buttons/spark6.wav" );
7 years ago
}
// get door button sounds, for doors which require buttons to open
8 years ago
if( m_bLockedSound )
7 years ago
{
pszSound = ButtonSound( (int)m_bLockedSound );
8 years ago
PRECACHE_SOUND( pszSound );
m_ls.sLockedSound = MAKE_STRING( pszSound );
7 years ago
}
8 years ago
if( m_bUnlockedSound )
7 years ago
{
pszSound = ButtonSound( (int)m_bUnlockedSound );
8 years ago
PRECACHE_SOUND( pszSound );
m_ls.sUnlockedSound = MAKE_STRING( pszSound );
7 years ago
}
// get sentence group names, for doors which are directly 'touched' to open
8 years ago
switch( m_bLockedSentence )
9 years ago
{
8 years ago
case 1: // access denied
m_ls.sLockedSentence = MAKE_STRING( "NA" );
break;
case 2: // security lockout
m_ls.sLockedSentence = MAKE_STRING( "ND" );
break;
case 3: // blast door
m_ls.sLockedSentence = MAKE_STRING( "NF" );
break;
case 4: // fire door
m_ls.sLockedSentence = MAKE_STRING( "NFIRE" );
break;
case 5: // chemical door
m_ls.sLockedSentence = MAKE_STRING( "NCHEM" );
break;
case 6: // radiation door
m_ls.sLockedSentence = MAKE_STRING( "NRAD" );
break;
case 7: // gen containment
m_ls.sLockedSentence = MAKE_STRING( "NCON" );
break;
case 8: // maintenance door
m_ls.sLockedSentence = MAKE_STRING( "NH" );
break;
case 9: // broken door
m_ls.sLockedSentence = MAKE_STRING( "NG" );
break;
default:
m_ls.sLockedSentence = 0;
break;
7 years ago
}
8 years ago
switch( m_bUnlockedSentence )
7 years ago
{
8 years ago
case 1: // access granted
m_ls.sUnlockedSentence = MAKE_STRING( "EA" );
break;
case 2: // security door
m_ls.sUnlockedSentence = MAKE_STRING( "ED" );
8 years ago
break;
case 3: // blast door
m_ls.sUnlockedSentence = MAKE_STRING( "EF" );
8 years ago
break;
case 4: // fire door
m_ls.sUnlockedSentence = MAKE_STRING( "EFIRE" );
8 years ago
break;
case 5: // chemical door
m_ls.sUnlockedSentence = MAKE_STRING( "ECHEM" );
8 years ago
break;
case 6: // radiation door
m_ls.sUnlockedSentence = MAKE_STRING( "ERAD" );
8 years ago
break;
case 7: // gen containment
m_ls.sUnlockedSentence = MAKE_STRING( "ECON" );
8 years ago
break;
case 8: // maintenance door
m_ls.sUnlockedSentence = MAKE_STRING( "EH" );
8 years ago
break;
default:
m_ls.sUnlockedSentence = 0;
break;
7 years ago
}
}
//
// Cache user-entity-field values until spawn is called.
//
void CBaseButton::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "changetarget" ) )
7 years ago
{
8 years ago
m_strChangeTarget = ALLOC_STRING( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "locked_sound" ) )
7 years ago
{
m_bLockedSound = atoi( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "locked_sentence" ) )
7 years ago
{
m_bLockedSentence = atoi( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "unlocked_sound" ) )
7 years ago
{
m_bUnlockedSound = atoi( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "unlocked_sentence" ) )
7 years ago
{
m_bUnlockedSentence = atoi( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
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
CBaseToggle::KeyValue( pkvd );
}
//
// ButtonShot
//
8 years ago
int CBaseButton::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
7 years ago
{
BUTTON_CODE code = ButtonResponseToTouch();
8 years ago
if( code == BUTTON_NOTHING )
7 years ago
return 0;
// Temporarily disable the touch function, until movement is finished.
SetTouch( NULL );
m_hActivator = CBaseEntity::Instance( pevAttacker );
if( m_hActivator == 0 )
7 years ago
return 0;
8 years ago
if( code == BUTTON_RETURN )
7 years ago
{
EMIT_SOUND( ENT( pev ), CHAN_VOICE, STRING( pev->noise ), 1, ATTN_NORM );
7 years ago
// Toggle buttons fire when they get back to their "home" position
8 years ago
if( !( pev->spawnflags & SF_BUTTON_TOGGLE ) )
7 years ago
SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 );
ButtonReturn();
}
else // code == BUTTON_ACTIVATE
8 years ago
ButtonActivate();
7 years ago
return 0;
}
/*QUAKED func_button (0 .5 .8) ?
When a button is touched, it moves some distance in the direction of it's angle,
triggers all of it's targets, waits some time, then returns to it's original position
where it can be triggered again.
"angle" determines the opening direction
"target" all entities with a matching targetname will be used
"speed" override the default 40 speed
"wait" override the default 1 second wait (-1 = never return)
"lip" override the default 4 pixel lip remaining at end of move
"health" if set, the button must be killed instead of touched
"sounds"
0) steam metal
1) wooden clunk
2) metallic click
3) in-out
*/
LINK_ENTITY_TO_CLASS( func_button, CBaseButton )
7 years ago
8 years ago
void CBaseButton::Spawn()
7 years ago
{
const char *pszSound;
7 years ago
//----------------------------------------------------
//determine sounds for buttons
//a sound of 0 should not make a sound
//----------------------------------------------------
pszSound = ButtonSound( m_sounds );
8 years ago
PRECACHE_SOUND( pszSound );
pev->noise = MAKE_STRING( pszSound );
7 years ago
Precache();
8 years ago
if( FBitSet( pev->spawnflags, SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state
7 years ago
{
9 years ago
SetThink( &CBaseButton::ButtonSpark );
7 years ago
SetNextThink( 0.5 );// no hurry, make sure everything else spawns
}
8 years ago
SetMovedir( pev );
7 years ago
8 years ago
pev->movetype = MOVETYPE_PUSH;
7 years ago
if ( FBitSet ( pev->spawnflags, SF_BUTTON_NOT_SOLID ) )
{
pev->solid = SOLID_NOT;
pev->skin = CONTENTS_EMPTY;
}
else
{
8 years ago
pev->solid = SOLID_BSP;
7 years ago
}
8 years ago
SET_MODEL( ENT( pev ), STRING( pev->model ) );
9 years ago
7 years ago
//LRC
if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "z");
else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "a");
8 years ago
if( pev->speed == 0 )
7 years ago
pev->speed = 40;
8 years ago
if( pev->health > 0 )
7 years ago
{
pev->takedamage = DAMAGE_YES;
}
8 years ago
if( m_flWait == 0 )
7 years ago
m_flWait = 1;
8 years ago
if( m_flLip == 0 )
7 years ago
m_flLip = 4;
m_toggle_state = TS_AT_BOTTOM;
m_vecPosition1 = pev->origin;
// Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
8 years ago
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 ) );
7 years ago
// Is this a non-moving button?
8 years ago
if( ( ( m_vecPosition2 - m_vecPosition1 ).Length() < 1 ) || ( pev->spawnflags & SF_BUTTON_DONTMOVE ) )
7 years ago
m_vecPosition2 = m_vecPosition1;
8 years ago
m_fStayPushed = m_flWait == -1 ? TRUE : FALSE;
7 years ago
m_fRotating = FALSE;
// if the button is flagged for USE button activation only, take away it's touch function and add a use function
8 years ago
if( FBitSet( pev->spawnflags, SF_BUTTON_TOUCH_ONLY ) ) // touchable button
7 years ago
{
9 years ago
SetTouch( &CBaseButton::ButtonTouch );
7 years ago
if ( !FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) )
SetUse(&CBaseButton:: ButtonUse_IgnorePlayer );
else
SetUse(&CBaseButton:: ButtonUse );
}
else
{
9 years ago
SetTouch( NULL );
7 years ago
if ( FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) )
SetUse(&CBaseButton:: ButtonUse_IgnorePlayer );
else
9 years ago
SetUse( &CBaseButton::ButtonUse );
7 years ago
}
}
//LRC
void CBaseButton :: PostSpawn( void )
{
if (m_pMoveWith)
m_vecPosition1 = pev->origin - m_pMoveWith->pev->origin;
else
m_vecPosition1 = pev->origin;
// Subtract 2 from size because the engine expands bboxes by 1 in all directions
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 ( ((m_vecPosition2 - m_vecPosition1).Length() < 1) || (pev->spawnflags & SF_BUTTON_DONTMOVE) )
m_vecPosition2 = m_vecPosition1;
}
// Button sound table.
// Also used by CBaseDoor to get 'touched' door lock/unlock sounds
8 years ago
const char *ButtonSound( int sound )
7 years ago
{
const char *pszSound;
9 years ago
8 years ago
switch( sound )
9 years ago
{
8 years ago
case 0:
pszSound = "common/null.wav";
break;
case 1:
pszSound = "buttons/button1.wav";
break;
case 2:
pszSound = "buttons/button2.wav";
break;
case 3:
pszSound = "buttons/button3.wav";
break;
case 4:
pszSound = "buttons/button4.wav";
break;
case 5:
pszSound = "buttons/button5.wav";
break;
case 6:
pszSound = "buttons/button6.wav";
break;
case 7:
pszSound = "buttons/button7.wav";
break;
case 8:
pszSound = "buttons/button8.wav";
break;
case 9:
pszSound = "buttons/button9.wav";
break;
case 10:
pszSound = "buttons/button10.wav";
break;
case 11:
pszSound = "buttons/button11.wav";
break;
case 12:
pszSound = "buttons/latchlocked1.wav";
break;
case 13:
pszSound = "buttons/latchunlocked1.wav";
break;
case 14:
pszSound = "buttons/lightswitch2.wav";
break;
7 years ago
// next 6 slots reserved for any additional sliding button sounds we may add
8 years ago
case 21:
pszSound = "buttons/lever1.wav";
break;
case 22:
pszSound = "buttons/lever2.wav";
break;
case 23:
pszSound = "buttons/lever3.wav";
break;
case 24:
pszSound = "buttons/lever4.wav";
break;
case 25:
pszSound = "buttons/lever5.wav";
break;
default:
pszSound = "buttons/button9.wav";
break;
7 years ago
}
return pszSound;
}
//
// Makes flagged buttons spark when turned off
//
8 years ago
void DoSpark( entvars_t *pev, const Vector &location )
7 years ago
{
Vector tmp = location + pev->size * 0.5;
UTIL_Sparks( tmp );
8 years ago
float flVolume = RANDOM_FLOAT( 0.25 , 0.75 ) * 0.4;//random volume range
switch( (int)( RANDOM_FLOAT( 0, 1 ) * 6 ) )
7 years ago
{
8 years ago
case 0:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/spark1.wav", flVolume, ATTN_NORM );
break;
case 1:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/spark2.wav", flVolume, ATTN_NORM );
break;
case 2:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/spark3.wav", flVolume, ATTN_NORM );
break;
case 3:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/spark4.wav", flVolume, ATTN_NORM );
break;
case 4:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM );
break;
case 5:
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM );
break;
7 years ago
}
}
8 years ago
void CBaseButton::ButtonSpark( void )
7 years ago
{
9 years ago
SetThink( &CBaseButton::ButtonSpark );
7 years ago
SetNextThink( 0.1 + RANDOM_FLOAT ( 0, 1.5 ) );// spark again at random interval
DoSpark( pev, pev->mins );
}
//
// Button's Use function
//
8 years ago
void CBaseButton::ButtonUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
// Ignore touches if button is moving, or pushed-in and waiting to auto-come-out.
// UNDONE: Should this use ButtonResponseToTouch() too?
8 years ago
if( m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN )
return;
7 years ago
m_hActivator = pActivator;
8 years ago
if( m_toggle_state == TS_AT_TOP )
7 years ago
{
8 years ago
if( !m_fStayPushed && FBitSet( pev->spawnflags, SF_BUTTON_TOGGLE ) )
7 years ago
{
EMIT_SOUND( ENT( pev ), CHAN_VOICE, STRING( pev->noise ), 1, ATTN_NORM );
8 years ago
7 years ago
//SUB_UseTargets( m_eoActivator );
ButtonReturn();
}
}
else
8 years ago
ButtonActivate();
7 years ago
}
//LRC - they had it set up so that a touch-only button couldn't even be triggered!?
void CBaseButton::ButtonUse_IgnorePlayer ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if ( !pCaller || !pCaller->IsPlayer() )
ButtonUse( pActivator, pCaller, useType, value );
}
CBaseButton::BUTTON_CODE CBaseButton::ButtonResponseToTouch( void )
{
// Ignore touches if button is moving, or pushed-in and waiting to auto-come-out.
8 years ago
if( m_toggle_state == TS_GOING_UP ||
7 years ago
m_toggle_state == TS_GOING_DOWN ||
8 years ago
( m_toggle_state == TS_AT_TOP && !m_fStayPushed && !FBitSet(pev->spawnflags, SF_BUTTON_TOGGLE ) ) )
7 years ago
return BUTTON_NOTHING;
8 years ago
if( m_toggle_state == TS_AT_TOP )
7 years ago
{
8 years ago
if( ( FBitSet( pev->spawnflags, SF_BUTTON_TOGGLE ) ) && !m_fStayPushed )
7 years ago
{
return BUTTON_RETURN;
}
}
else
return BUTTON_ACTIVATE;
return BUTTON_NOTHING;
}
//
// Touching a button simply "activates" it.
//
8 years ago
void CBaseButton::ButtonTouch( CBaseEntity *pOther )
7 years ago
{
// Ignore touches by anything but players
8 years ago
if( !FClassnameIs( pOther->pev, "player" ) )
7 years ago
return;
m_hActivator = pOther;
BUTTON_CODE code = ButtonResponseToTouch();
8 years ago
if( code == BUTTON_NOTHING )
7 years ago
return;
8 years ago
if( !UTIL_IsMasterTriggered( m_sMaster, pOther ) )
7 years ago
{
// play button locked sound
8 years ago
PlayLockSounds( pev, &m_ls, TRUE, TRUE );
7 years ago
return;
}
// Temporarily disable the touch function, until movement is finished.
SetTouch( NULL );
8 years ago
if( code == BUTTON_RETURN )
7 years ago
{
EMIT_SOUND( ENT( pev ), CHAN_VOICE, STRING( pev->noise ), 1, ATTN_NORM );
7 years ago
SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 );
ButtonReturn();
}
else // code == BUTTON_ACTIVATE
8 years ago
ButtonActivate();
7 years ago
}
//
// Starts the button moving "in/up".
//
8 years ago
void CBaseButton::ButtonActivate()
7 years ago
{
EMIT_SOUND( ENT( pev ), CHAN_VOICE, STRING( pev->noise ), 1, ATTN_NORM );
8 years ago
if( !UTIL_IsMasterTriggered( m_sMaster, m_hActivator ) )
7 years ago
{
// button is locked, play locked sound
8 years ago
PlayLockSounds( pev, &m_ls, TRUE, TRUE );
7 years ago
return;
}
else
{
// button is unlocked, play unlocked sound
8 years ago
PlayLockSounds( pev, &m_ls, FALSE, TRUE );
7 years ago
}
8 years ago
ASSERT( m_toggle_state == TS_AT_BOTTOM );
7 years ago
m_toggle_state = TS_GOING_UP;
//LRC - unhelpfully, SF_BUTTON_DONTMOVE is the same value as
// SF_ROTBUTTON_NOTSOLID, so we have to assume that a rotbutton will
// never be DONTMOVE.
if (pev->spawnflags & SF_BUTTON_DONTMOVE && !m_fRotating)
{
TriggerAndWait();
}
else
{
9 years ago
SetMoveDone( &CBaseButton::TriggerAndWait );
8 years ago
if( !m_fRotating )
LinearMove( m_vecPosition2, pev->speed );
9 years ago
else
8 years ago
AngularMove( m_vecAngle2, pev->speed );
9 years ago
}
7 years ago
}
//
// Button has reached the "in/up" position. Activate its "targets", and pause before "popping out".
//
void CBaseButton::TriggerAndWait( void )
{
8 years ago
ASSERT( m_toggle_state == TS_GOING_UP );
7 years ago
8 years ago
if( !UTIL_IsMasterTriggered( m_sMaster, m_hActivator ) )
7 years ago
return;
m_toggle_state = TS_AT_TOP;
8 years ago
7 years ago
pev->frame = 1; // use alternate textures
//LRC
if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "a");
else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "z");
SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 );
// If button automatically comes back out, start it moving out.
// Else re-instate touch method
8 years ago
if( m_fStayPushed || FBitSet( pev->spawnflags, SF_BUTTON_TOGGLE ) )
7 years ago
{
8 years ago
if( !FBitSet( pev->spawnflags, SF_BUTTON_TOUCH_ONLY ) ) // this button only works if USED, not touched!
7 years ago
{
// ALL buttons are now use only
9 years ago
SetTouch( NULL );
7 years ago
}
else
9 years ago
SetTouch( &CBaseButton::ButtonTouch );
7 years ago
}
else
{
9 years ago
SetThink( &CBaseButton::ButtonReturn );
7 years ago
if ( m_flWait )
{
SetNextThink( m_flWait );
}
else
{
ButtonReturn();
}
}
9 years ago
}
7 years ago
//
// Starts the button moving "out/down".
//
void CBaseButton::ButtonReturn( void )
{
8 years ago
ASSERT( m_toggle_state == TS_AT_TOP );
7 years ago
m_toggle_state = TS_GOING_DOWN;
pev->frame = 0; // use normal textures
//LRC
if (m_iStyle >= 32) LIGHT_STYLE(m_iStyle, "z");
else if (m_iStyle <= -32) LIGHT_STYLE(-m_iStyle, "a");
if (pev->spawnflags & SF_BUTTON_DONTMOVE)
{
ButtonBackHome();
}
else
{
9 years ago
SetMoveDone( &CBaseButton::ButtonBackHome );
8 years ago
if( !m_fRotating )
LinearMove( m_vecPosition1, pev->speed );
9 years ago
else
8 years ago
AngularMove( m_vecAngle1, pev->speed );
7 years ago
}
}
//
// Button has returned to start state. Quiesce it.
//
void CBaseButton::ButtonBackHome( void )
{
8 years ago
ASSERT( m_toggle_state == TS_GOING_DOWN );
7 years ago
m_toggle_state = TS_AT_BOTTOM;
8 years ago
if( FBitSet( pev->spawnflags, SF_BUTTON_TOGGLE ) )
7 years ago
{
//EMIT_SOUND( ENT( pev ), CHAN_VOICE, STRING( pev->noise ), 1, ATTN_NORM );
7 years ago
SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 );
}
8 years ago
if( !FStringNull( pev->target ) )
7 years ago
{
CBaseEntity *pTarget = NULL;
8 years ago
for( ;; )
7 years ago
{
pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target), m_hActivator);
if (FNullEnt(pTarget))
break;
if (!FClassnameIs(pTarget->pev, "multisource"))
// LRC- hmm... I see. On returning, a button will only turn off multisources.
continue;
9 years ago
pTarget->Use( m_hActivator, this, USE_TOGGLE, 0 );
7 years ago
}
}
// Re-instate touch method, movement cycle is complete.
8 years ago
if( !FBitSet( pev->spawnflags, SF_BUTTON_TOUCH_ONLY ) ) // this button only works if USED, not touched!
7 years ago
{
// All buttons are now use only
9 years ago
SetTouch( NULL );
7 years ago
}
else
9 years ago
SetTouch( &CBaseButton::ButtonTouch );
7 years ago
// reset think for a sparking button
8 years ago
if( FBitSet( pev->spawnflags, SF_BUTTON_SPARK_IF_OFF ) )
7 years ago
{
9 years ago
SetThink( &CBaseButton::ButtonSpark );
7 years ago
SetNextThink( 0.5 );// no hurry.
}
else
{
DontThink();
}
}
//
// Rotating button (aka "lever")
//
class CRotButton : public CBaseButton
{
public:
void Spawn( void );
void PostSpawn( void ) {} // don't use the moveWith fix from CBaseButton
virtual void KeyValue( KeyValueData* pkvd);
};
LINK_ENTITY_TO_CLASS( func_rot_button, CRotButton )
7 years ago
void CRotButton::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "axes"))
{
UTIL_StringToVector( (float*)(pev->movedir), pkvd->szValue );
pkvd->fHandled = TRUE;
}
else
CBaseButton::KeyValue( pkvd );
}
void CRotButton::Spawn( void )
{
const char *pszSound;
7 years ago
//----------------------------------------------------
//determine sounds for buttons
//a sound of 0 should not make a sound
//----------------------------------------------------
pszSound = ButtonSound( m_sounds );
8 years ago
PRECACHE_SOUND( pszSound );
pev->noise = MAKE_STRING( pszSound );
7 years ago
// set the axis of rotation
CBaseToggle::AxisDir( pev );
// check for clockwise rotation
8 years ago
if( FBitSet( pev->spawnflags, SF_DOOR_ROTATE_BACKWARDS ) )
7 years ago
pev->movedir = pev->movedir * -1;
8 years ago
pev->movetype = MOVETYPE_PUSH;
7 years ago
8 years ago
if( pev->spawnflags & SF_ROTBUTTON_NOTSOLID )
pev->solid = SOLID_NOT;
7 years ago
else
8 years ago
pev->solid = SOLID_BSP;
7 years ago
8 years ago
SET_MODEL( ENT( pev ), STRING( pev->model ) );
7 years ago
8 years ago
if( pev->speed == 0 )
7 years ago
pev->speed = 40;
8 years ago
if( m_flWait == 0 )
7 years ago
m_flWait = 1;
8 years ago
if( pev->health > 0 )
7 years ago
{
pev->takedamage = DAMAGE_YES;
}
m_toggle_state = TS_AT_BOTTOM;
8 years ago
m_vecAngle1 = pev->angles;
m_vecAngle2 = pev->angles + pev->movedir * m_flMoveDistance;
ASSERTSZ( m_vecAngle1 != m_vecAngle2, "rotating button start/end positions are equal" );
7 years ago
8 years ago
m_fStayPushed = m_flWait == -1 ? TRUE : FALSE;
7 years ago
m_fRotating = TRUE;
// if the button is flagged for USE button activation only, take away it's touch function and add a use function
8 years ago
if( !FBitSet( pev->spawnflags, SF_BUTTON_TOUCH_ONLY ) )
7 years ago
{
9 years ago
SetTouch( NULL );
7 years ago
if ( FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) )
SetUse(&CRotButton:: ButtonUse_IgnorePlayer );
else
SetUse(&CRotButton:: ButtonUse );
}
else // touchable button
{
SetTouch(&CRotButton:: ButtonTouch );
if ( !FBitSet ( pev->spawnflags, SF_BUTTON_USEKEY ) )
SetUse(&CRotButton:: ButtonUse_IgnorePlayer );
else
SetUse(&CRotButton:: ButtonUse );
}
9 years ago
//SetTouch( &ButtonTouch );
7 years ago
}
// Make this button behave like a door (HACKHACK)
// This will disable use and make the button solid
// rotating buttons were made SOLID_NOT by default since their were some
// collision problems with them...
#define SF_MOMENTARY_DOOR 0x0001
class CMomentaryRotButton : public CBaseToggle
{
public:
8 years ago
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
virtual int ObjectCaps( void )
7 years ago
{
8 years ago
int flags = CBaseToggle::ObjectCaps() & ( ~FCAP_ACROSS_TRANSITION );
if( pev->spawnflags & SF_MOMENTARY_DOOR )
7 years ago
return flags;
return flags | FCAP_CONTINUOUS_USE;
}
8 years ago
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT Off( void );
void EXPORT Return( void );
void UpdateSelf( float value );
void UpdateSelfReturn( float value );
void UpdateAllButtons( float value, int start );
void PlaySound( void );
void UpdateTarget( float value );
static CMomentaryRotButton *Instance( edict_t *pent ) { return (CMomentaryRotButton *)GET_PRIVATE( pent ); };
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int m_lastUsed;
int m_direction;
float m_returnSpeed;
vec3_t m_start;
vec3_t m_end;
int m_sounds;
7 years ago
};
7 years ago
TYPEDESCRIPTION CMomentaryRotButton::m_SaveData[] =
{
DEFINE_FIELD( CMomentaryRotButton, m_lastUsed, FIELD_INTEGER ),
DEFINE_FIELD( CMomentaryRotButton, m_direction, FIELD_INTEGER ),
DEFINE_FIELD( CMomentaryRotButton, m_returnSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CMomentaryRotButton, m_start, FIELD_VECTOR ),
DEFINE_FIELD( CMomentaryRotButton, m_end, FIELD_VECTOR ),
DEFINE_FIELD( CMomentaryRotButton, m_sounds, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CMomentaryRotButton, CBaseToggle )
7 years ago
LINK_ENTITY_TO_CLASS( momentary_rot_button, CMomentaryRotButton )
7 years ago
void CMomentaryRotButton::Spawn( void )
{
CBaseToggle::AxisDir( pev );
8 years ago
if( pev->speed == 0 )
7 years ago
pev->speed = 100;
8 years ago
if( m_flMoveDistance < 0 )
7 years ago
{
m_start = pev->angles + pev->movedir * m_flMoveDistance;
m_end = pev->angles;
m_direction = 1; // This will toggle to -1 on the first use()
m_flMoveDistance = -m_flMoveDistance;
}
else
{
m_start = pev->angles;
m_end = pev->angles + pev->movedir * m_flMoveDistance;
m_direction = -1; // This will toggle to +1 on the first use()
}
8 years ago
if( pev->spawnflags & SF_MOMENTARY_DOOR )
pev->solid = SOLID_BSP;
7 years ago
else
8 years ago
pev->solid = SOLID_NOT;
7 years ago
8 years ago
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
const char *pszSound = ButtonSound( m_sounds );
8 years ago
PRECACHE_SOUND( pszSound );
pev->noise = MAKE_STRING( pszSound );
7 years ago
m_lastUsed = 0;
}
void CMomentaryRotButton::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "returnspeed" ) )
7 years ago
{
8 years ago
m_returnSpeed = atof( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
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
CBaseToggle::KeyValue( pkvd );
}
void CMomentaryRotButton::PlaySound( void )
{
EMIT_SOUND( ENT( pev ), CHAN_VOICE, STRING( pev->noise ), 1, ATTN_NORM );
7 years ago
}
// BUGBUG: This design causes a latentcy. When the button is retriggered, the first impulse
// will send the target in the wrong direction because the parameter is calculated based on the
// current, not future position.
void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (IsLockedByMaster()) return; //LRC
// the distance between the current angle and the "base" angle.
pev->ideal_yaw = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles, m_start ) / m_flMoveDistance;
UpdateAllButtons( pev->ideal_yaw, 1 );
// TODO: which code to use?
#if 1 // spirit
7 years ago
float f = m_fNextThink - pev->ltime;
f = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles + pev->avelocity*f, m_start ) / m_flMoveDistance;
// ALERT(at_console,"sending update = %f\n", f);
7 years ago
UpdateTarget( f );
#else // hlsdk-xash3d master
// Calculate destination angle and use it to predict value, this prevents sending target in wrong direction on retriggering
Vector dest = pev->angles + pev->avelocity * ( pev->nextthink - pev->ltime );
float value1 = CBaseToggle::AxisDelta( pev->spawnflags, dest, m_start ) / m_flMoveDistance;
UpdateTarget( value1 );
#endif
7 years ago
}
void CMomentaryRotButton::UpdateAllButtons( float value, int start )
{
// Update all rot buttons attached to my target
// (this includes myself)
CBaseEntity *pTarget = NULL;
8 years ago
for( ; ; )
7 years ago
{
pTarget = UTIL_FindEntityByTarget(pTarget, STRING(pev->target));
if (FNullEnt(pTarget))
break;
if ( FClassnameIs( pTarget->pev, "momentary_rot_button" ) )
9 years ago
{
7 years ago
CMomentaryRotButton *pEntity = (CMomentaryRotButton*)pTarget;
8 years ago
if( start )
9 years ago
pEntity->UpdateSelf( value );
else
pEntity->UpdateSelfReturn( value );
}
7 years ago
}
}
void CMomentaryRotButton::UpdateSelf( float value )
{
BOOL fplaysound = FALSE;
8 years ago
if( !m_lastUsed )
7 years ago
{
fplaysound = TRUE;
m_direction = -m_direction;
}
m_lastUsed = 1;
SetNextThink( 0.1 );
//LRC check if we're outside the boundaries
8 years ago
if( m_direction > 0 && value >= 1.0 )
7 years ago
{
pev->avelocity = g_vecZero;
pev->angles = m_end;
return;
}
8 years ago
else if( m_direction < 0 && value <= 0 )
7 years ago
{
pev->avelocity = g_vecZero;
pev->angles = m_start;
return;
}
8 years ago
if( fplaysound )
7 years ago
PlaySound();
// HACKHACK -- If we're going slow, we'll get multiple player packets per frame;
// bump nexthink on each one to avoid stalling
//LRC- that is to say: our avelocity will get us to the target point in 0.1 secs.
// If we're being told to move further than that, wait that much longer.
if ( m_fNextThink < pev->ltime )
SetNextThink( 0.1 );
else
{
AbsoluteNextThink( m_fNextThink + 0.1 );
}
8 years ago
pev->avelocity = m_direction * pev->speed * pev->movedir;
9 years ago
SetThink( &CMomentaryRotButton::Off );
7 years ago
}
void CMomentaryRotButton::UpdateTarget( float value )
{
8 years ago
if( !FStringNull( pev->target ) )
7 years ago
{
CBaseEntity* pTarget = NULL;
8 years ago
for( ; ; )
7 years ago
{
pTarget = UTIL_FindEntityByTargetname(pTarget, STRING(pev->target));
if ( !pTarget )
break;
pTarget->Use( this, this, USE_SET, value );
}
}
}
void CMomentaryRotButton::Off( void )
{
pev->avelocity = g_vecZero;
m_lastUsed = 0;
8 years ago
if( FBitSet( pev->spawnflags, SF_PENDULUM_AUTO_RETURN ) && m_returnSpeed > 0 )
7 years ago
{
9 years ago
SetThink( &CMomentaryRotButton::Return );
7 years ago
SetNextThink( 0.1 );
m_direction = -1;
}
else
SetThink( NULL );
}
void CMomentaryRotButton::Return( void )
{
float value = CBaseToggle::AxisDelta( pev->spawnflags, pev->angles, m_start ) / m_flMoveDistance;
UpdateAllButtons( value, 0 ); // This will end up calling UpdateSelfReturn() n times, but it still works right
8 years ago
if( value > 0 )
7 years ago
UpdateTarget( value );
}
void CMomentaryRotButton::UpdateSelfReturn( float value )
{
8 years ago
if( value <= 0 )
7 years ago
{
pev->avelocity = g_vecZero;
pev->angles = m_start;
DontThink();
SetThink( NULL );
}
else
{
pev->avelocity = -m_returnSpeed * pev->movedir;
SetNextThink( 0.1 );
}
}
//----------------------------------------------------------------
// Spark
//----------------------------------------------------------------
class CEnvSpark : public CBaseEntity
{
public:
8 years ago
void Spawn( void );
void Precache( void );
void EXPORT SparkThink( void );
7 years ago
void EXPORT SparkWait(void);
void EXPORT SparkCyclic(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
8 years ago
void EXPORT SparkStart( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT SparkStop( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
7 years ago
8 years ago
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
7 years ago
8 years ago
float m_flDelay;
7 years ago
STATE m_iState; //LRC
virtual STATE GetState( void ) { return m_iState; };
};
TYPEDESCRIPTION CEnvSpark::m_SaveData[] =
{
DEFINE_FIELD( CEnvSpark, m_flDelay, FIELD_FLOAT),
DEFINE_FIELD( CEnvSpark, m_iState, FIELD_INTEGER), //LRC
};
IMPLEMENT_SAVERESTORE( CEnvSpark, CBaseEntity )
7 years ago
LINK_ENTITY_TO_CLASS( env_spark, CEnvSpark )
LINK_ENTITY_TO_CLASS( env_debris, CEnvSpark )
7 years ago
8 years ago
void CEnvSpark::Spawn( void )
7 years ago
{
SetThink( NULL );
SetUse( NULL );
if (FBitSet(pev->spawnflags, 16))
{
SetUse(&CEnvSpark::SparkCyclic);
}
else if (FBitSet(pev->spawnflags, 32)) // Use for on/off
{
8 years ago
if( FBitSet( pev->spawnflags, 64 ) ) // Start on
7 years ago
{
8 years ago
SetThink( &CEnvSpark::SparkThink ); // start sparking
SetUse( &CEnvSpark::SparkStop ); // set up +USE to stop sparking
7 years ago
}
else
8 years ago
SetUse( &CEnvSpark::SparkStart );
7 years ago
}
else
8 years ago
SetThink( &CEnvSpark::SparkThink );
7 years ago
if (this->m_pfnThink)
{
SetNextThink( 0.1 + RANDOM_FLOAT ( 0, 1.5 ) );
8 years ago
if( m_flDelay <= 0 )
9 years ago
m_flDelay = 1.5;
7 years ago
}
8 years ago
Precache();
7 years ago
}
8 years ago
void CEnvSpark::Precache( void )
7 years ago
{
PRECACHE_SOUND( "buttons/spark1.wav" );
PRECACHE_SOUND( "buttons/spark2.wav" );
PRECACHE_SOUND( "buttons/spark3.wav" );
PRECACHE_SOUND( "buttons/spark4.wav" );
PRECACHE_SOUND( "buttons/spark5.wav" );
PRECACHE_SOUND( "buttons/spark6.wav" );
}
void CEnvSpark::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "MaxDelay" ) )
7 years ago
{
8 years ago
m_flDelay = atof( pkvd->szValue );
7 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "style" ) ||
FStrEq( pkvd->szKeyName, "height" ) ||
FStrEq( pkvd->szKeyName, "killtarget" ) ||
FStrEq( pkvd->szKeyName, "value1" ) ||
FStrEq( pkvd->szKeyName, "value2" ) ||
FStrEq( pkvd->szKeyName, "value3" ) )
7 years ago
pkvd->fHandled = TRUE;
else
CBaseEntity::KeyValue( pkvd );
}
void EXPORT CEnvSpark::SparkCyclic(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (m_pfnThink == NULL)
{
DoSpark( pev, pev->origin );
SetThink(&CEnvSpark:: SparkWait );
SetNextThink( m_flDelay );
}
else
{
SetThink(&CEnvSpark::SparkThink ); // if we're on SparkWait, change to actually spark at the specified time.
}
}
void EXPORT CEnvSpark::SparkWait(void)
{
SetThink( NULL );
}
8 years ago
void EXPORT CEnvSpark::SparkThink( void )
7 years ago
{
DoSpark( pev, pev->origin );
if (pev->spawnflags & 16)
{
SetThink( NULL );
}
else
{
SetNextThink( 0.1 + RANDOM_FLOAT (0, m_flDelay) );
}
}
8 years ago
void EXPORT CEnvSpark::SparkStart( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
8 years ago
SetUse( &CEnvSpark::SparkStop );
SetThink( &CEnvSpark::SparkThink );
7 years ago
m_iState = STATE_ON; //LRC
SetNextThink( 0.1 + RANDOM_FLOAT ( 0, m_flDelay) );
}
8 years ago
void EXPORT CEnvSpark::SparkStop( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
7 years ago
{
9 years ago
SetUse( &CEnvSpark::SparkStart);
SetThink( NULL );
7 years ago
m_iState = STATE_OFF; //LRC
}
#define SF_BTARGET_USE 0x0001
#define SF_BTARGET_ON 0x0002
class CButtonTarget : public CBaseEntity
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
8 years ago
int ObjectCaps( void );
7 years ago
};
LINK_ENTITY_TO_CLASS( button_target, CButtonTarget )
7 years ago
void CButtonTarget::Spawn( void )
{
8 years ago
pev->movetype = MOVETYPE_PUSH;
pev->solid = SOLID_BSP;
SET_MODEL( ENT( pev ), STRING( pev->model ) );
7 years ago
pev->takedamage = DAMAGE_YES;
8 years ago
if( FBitSet( pev->spawnflags, SF_BTARGET_ON ) )
7 years ago
pev->frame = 1;
}
void CButtonTarget::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
8 years ago
if( !ShouldToggle( useType, (int)pev->frame ) )
7 years ago
return;
pev->frame = 1-pev->frame;
8 years ago
if( pev->frame )
7 years ago
SUB_UseTargets( pActivator, USE_ON, 0 );
else
SUB_UseTargets( pActivator, USE_OFF, 0 );
}
8 years ago
int CButtonTarget::ObjectCaps( void )
7 years ago
{
int caps = CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
8 years ago
if( FBitSet( pev->spawnflags, SF_BTARGET_USE ) )
7 years ago
return caps | FCAP_IMPULSE_USE;
else
return caps;
}
8 years ago
int CButtonTarget::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
7 years ago
{
8 years ago
Use( Instance( pevAttacker ), this, USE_TOGGLE, 0 );
7 years ago
return 1;
}