aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/doors.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/doors.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/doors.cpp')
-rw-r--r--mp/src/game/server/doors.cpp1414
1 files changed, 1414 insertions, 0 deletions
diff --git a/mp/src/game/server/doors.cpp b/mp/src/game/server/doors.cpp
new file mode 100644
index 00000000..d44d6632
--- /dev/null
+++ b/mp/src/game/server/doors.cpp
@@ -0,0 +1,1414 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implements two types of doors: linear and rotating.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "doors.h"
+#include "entitylist.h"
+#include "physics.h"
+#include "ndebugoverlay.h"
+#include "engine/IEngineSound.h"
+#include "physics_npc_solver.h"
+
+#ifdef HL1_DLL
+#include "filters.h"
+#endif
+
+#ifdef CSTRIKE_DLL
+#include "KeyValues.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define CLOSE_AREAPORTAL_THINK_CONTEXT "CloseAreaportalThink"
+
+BEGIN_DATADESC( CBaseDoor )
+
+ DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ),
+
+ DEFINE_FIELD( m_bLockedSentence, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_bUnlockedSentence, FIELD_CHARACTER ),
+ DEFINE_KEYFIELD( m_NoiseMoving, FIELD_SOUNDNAME, "noise1" ),
+ DEFINE_KEYFIELD( m_NoiseArrived, FIELD_SOUNDNAME, "noise2" ),
+ DEFINE_KEYFIELD( m_NoiseMovingClosed, FIELD_SOUNDNAME, "startclosesound" ),
+ DEFINE_KEYFIELD( m_NoiseArrivedClosed, FIELD_SOUNDNAME, "closesound" ),
+ DEFINE_KEYFIELD( m_ChainTarget, FIELD_STRING, "chainstodoor" ),
+ // DEFINE_FIELD( m_isChaining, FIELD_BOOLEAN ),
+ // DEFINE_FIELD( m_ls, locksound_t ),
+// DEFINE_FIELD( m_isChaining, FIELD_BOOLEAN ),
+ DEFINE_KEYFIELD( m_ls.sLockedSound, FIELD_SOUNDNAME, "locked_sound" ),
+ DEFINE_KEYFIELD( m_ls.sUnlockedSound, FIELD_SOUNDNAME, "unlocked_sound" ),
+ DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ),
+ DEFINE_KEYFIELD( m_flWaveHeight, FIELD_FLOAT, "WaveHeight" ),
+ DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "dmg" ),
+ DEFINE_KEYFIELD( m_eSpawnPosition, FIELD_INTEGER, "spawnpos" ),
+
+ DEFINE_KEYFIELD( m_bForceClosed, FIELD_BOOLEAN, "forceclosed" ),
+ DEFINE_FIELD( m_bDoorGroup, FIELD_BOOLEAN ),
+
+#ifdef HL1_DLL
+ DEFINE_KEYFIELD( m_iBlockFilterName, FIELD_STRING, "filtername" ),
+ DEFINE_FIELD( m_hBlockFilter, FIELD_EHANDLE ),
+#endif
+
+ DEFINE_KEYFIELD( m_bLoopMoveSound, FIELD_BOOLEAN, "loopmovesound" ),
+ DEFINE_KEYFIELD( m_bIgnoreDebris, FIELD_BOOLEAN, "ignoredebris" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "SetToggleState", InputSetToggleState ),
+
+ DEFINE_OUTPUT( m_OnBlockedOpening, "OnBlockedOpening" ),
+ DEFINE_OUTPUT( m_OnBlockedClosing, "OnBlockedClosing" ),
+ DEFINE_OUTPUT( m_OnUnblockedOpening, "OnUnblockedOpening" ),
+ DEFINE_OUTPUT( m_OnUnblockedClosing, "OnUnblockedClosing" ),
+ DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ),
+ DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ),
+ DEFINE_OUTPUT( m_OnClose, "OnClose" ),
+ DEFINE_OUTPUT( m_OnOpen, "OnOpen" ),
+ DEFINE_OUTPUT( m_OnLockedUse, "OnLockedUse" ),
+
+ // Function Pointers
+ DEFINE_FUNCTION( DoorTouch ),
+ DEFINE_FUNCTION( DoorGoUp ),
+ DEFINE_FUNCTION( DoorGoDown ),
+ DEFINE_FUNCTION( DoorHitTop ),
+ DEFINE_FUNCTION( DoorHitBottom ),
+ DEFINE_THINKFUNC( MovingSoundThink ),
+ DEFINE_THINKFUNC( CloseAreaPortalsThink ),
+
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( func_door, CBaseDoor );
+
+//
+// func_water is implemented as a linear door so we can raise/lower the water level.
+//
+LINK_ENTITY_TO_CLASS( func_water, CBaseDoor );
+
+
+// SendTable stuff.
+IMPLEMENT_SERVERCLASS_ST(CBaseDoor, DT_BaseDoor)
+ SendPropFloat (SENDINFO(m_flWaveHeight), 8, SPROP_ROUNDUP, 0.0f, 8.0f),
+END_SEND_TABLE()
+
+#define DOOR_SENTENCEWAIT 6
+#define DOOR_SOUNDWAIT 1
+#define BUTTON_SOUNDWAIT 0.5
+
+
+//-----------------------------------------------------------------------------
+// Purpose: play door or button locked or unlocked sounds.
+// NOTE: this routine is shared by doors and buttons
+// Input : pEdict -
+// pls -
+// flocked - if true, play 'door is locked' sound, otherwise play 'door
+// is unlocked' sound.
+// fbutton -
+//-----------------------------------------------------------------------------
+void PlayLockSounds(CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton)
+{
+ if ( pEdict->HasSpawnFlags( SF_DOOR_SILENT ) )
+ {
+ return;
+ }
+ float flsoundwait = ( fbutton ) ? BUTTON_SOUNDWAIT : DOOR_SOUNDWAIT;
+
+ if ( flocked )
+ {
+ int fplaysound = (pls->sLockedSound != NULL_STRING && gpGlobals->curtime > pls->flwaitSound);
+ int fplaysentence = (pls->sLockedSentence != NULL_STRING && !pls->bEOFLocked && gpGlobals->curtime > pls->flwaitSentence);
+ float fvol = ( fplaysound && fplaysentence ) ? 0.25f : 1.0f;
+
+ // if there is a locked sound, and we've debounced, play sound
+ if (fplaysound)
+ {
+ // play 'door locked' sound
+ CPASAttenuationFilter filter( pEdict );
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_ITEM;
+ ep.m_pSoundName = (char*)STRING(pls->sLockedSound);
+ ep.m_flVolume = fvol;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ CBaseEntity::EmitSound( filter, pEdict->entindex(), ep );
+ pls->flwaitSound = gpGlobals->curtime + flsoundwait;
+ }
+
+ // if there is a sentence, we've not played all in list, and we've debounced, play sound
+ if (fplaysentence)
+ {
+ // play next 'door locked' sentence in group
+ int iprev = pls->iLockedSentence;
+
+ pls->iLockedSentence = SENTENCEG_PlaySequentialSz( pEdict->edict(),
+ STRING(pls->sLockedSentence),
+ 0.85f,
+ SNDLVL_NORM,
+ 0,
+ 100,
+ pls->iLockedSentence,
+ FALSE);
+ pls->iUnlockedSentence = 0;
+
+ // make sure we don't keep calling last sentence in list
+ pls->bEOFLocked = (iprev == pls->iLockedSentence);
+
+ pls->flwaitSentence = gpGlobals->curtime + DOOR_SENTENCEWAIT;
+ }
+ }
+ else
+ {
+ // UNLOCKED SOUND
+
+ int fplaysound = (pls->sUnlockedSound != NULL_STRING && gpGlobals->curtime > pls->flwaitSound);
+ int fplaysentence = (pls->sUnlockedSentence != NULL_STRING && !pls->bEOFUnlocked && gpGlobals->curtime > pls->flwaitSentence);
+ float fvol;
+
+ // if playing both sentence and sound, lower sound volume so we hear sentence
+ fvol = ( fplaysound && fplaysentence ) ? 0.25f : 1.0f;
+
+ // play 'door unlocked' sound if set
+ if (fplaysound)
+ {
+ CPASAttenuationFilter filter( pEdict );
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_ITEM;
+ ep.m_pSoundName = (char*)STRING(pls->sUnlockedSound);
+ ep.m_flVolume = fvol;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ CBaseEntity::EmitSound( filter, pEdict->entindex(), ep );
+ pls->flwaitSound = gpGlobals->curtime + flsoundwait;
+ }
+
+ // play next 'door unlocked' sentence in group
+ if (fplaysentence)
+ {
+ int iprev = pls->iUnlockedSentence;
+
+ pls->iUnlockedSentence = SENTENCEG_PlaySequentialSz(pEdict->edict(), STRING(pls->sUnlockedSentence),
+ 0.85, SNDLVL_NORM, 0, 100, pls->iUnlockedSentence, FALSE);
+ pls->iLockedSentence = 0;
+
+ // make sure we don't keep calling last sentence in list
+ pls->bEOFUnlocked = (iprev == pls->iUnlockedSentence);
+ pls->flwaitSentence = gpGlobals->curtime + DOOR_SENTENCEWAIT;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Cache user-entity-field values until spawn is called.
+// Input : szKeyName -
+// szValue -
+// Output : Returns true.
+//-----------------------------------------------------------------------------
+bool CBaseDoor::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "locked_sentence"))
+ {
+ m_bLockedSentence = atof(szValue);
+ }
+ else if (FStrEq(szKeyName, "unlocked_sentence"))
+ {
+ m_bUnlockedSentence = atof(szValue);
+ }
+ else
+ return BaseClass::KeyValue( szKeyName, szValue );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDoor::Spawn()
+{
+ Precache();
+
+#ifdef HL1_DLL
+ SetSolid( SOLID_BSP );
+#else
+ if ( GetMoveParent() && GetRootMoveParent()->GetSolid() == SOLID_BSP )
+ {
+ SetSolid( SOLID_BSP );
+ }
+ else
+ {
+ SetSolid( SOLID_VPHYSICS );
+ }
+#endif
+
+ // Convert movedir from angles to a vector
+ QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z );
+ AngleVectors( angMoveDir, &m_vecMoveDir );
+
+ SetModel( STRING( GetModelName() ) );
+ m_vecPosition1 = GetLocalOrigin();
+
+ // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
+ Vector vecOBB = CollisionProp()->OBBSize();
+ vecOBB -= Vector( 2, 2, 2 );
+ m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * (DotProductAbs( m_vecMoveDir, vecOBB ) - m_flLip));
+
+ if ( !IsRotatingDoor() )
+ {
+ if ( ( m_eSpawnPosition == FUNC_DOOR_SPAWN_OPEN ) || HasSpawnFlags( SF_DOOR_START_OPEN_OBSOLETE ) )
+ { // swap pos1 and pos2, put door at pos2
+ UTIL_SetOrigin( this, m_vecPosition2);
+ m_toggle_state = TS_AT_TOP;
+ }
+ else
+ {
+ m_toggle_state = TS_AT_BOTTOM;
+ }
+ }
+
+ if (HasSpawnFlags(SF_DOOR_LOCKED))
+ {
+ m_bLocked = true;
+ }
+
+ SetMoveType( MOVETYPE_PUSH );
+
+ if (m_flSpeed == 0)
+ {
+ m_flSpeed = 100;
+ }
+
+ SetTouch( &CBaseDoor::DoorTouch );
+
+ if ( !FClassnameIs( this, "func_water" ) )
+ {
+ if ( HasSpawnFlags(SF_DOOR_PASSABLE) )
+ {
+ //normal door
+ AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ }
+
+ if ( HasSpawnFlags( SF_DOOR_NONSOLID_TO_PLAYER ) )
+ {
+ SetCollisionGroup( COLLISION_GROUP_PASSABLE_DOOR );
+ // HACKHACK: Set this hoping that any children of the door that get blocked by the player
+ // will get fixed up by vphysics
+ // NOTE: We could decouple this as a separate behavior, but managing player collisions is already complex enough.
+ // NOTE: This is necessary to prevent the player from blocking the wrecked train car in ep2_outland_01
+ AddFlag( FL_UNBLOCKABLE_BY_PLAYER );
+ }
+ if ( m_bIgnoreDebris )
+ {
+ // both of these flags want to set the collision group and
+ // there isn't a combo group
+ Assert( !HasSpawnFlags( SF_DOOR_NONSOLID_TO_PLAYER ) );
+ if ( HasSpawnFlags( SF_DOOR_NONSOLID_TO_PLAYER ) )
+ {
+ Warning("Door %s with conflicting collision settings, removing ignoredebris\n", GetDebugName() );
+ }
+ else
+ {
+ SetCollisionGroup( COLLISION_GROUP_INTERACTIVE );
+ }
+ }
+ }
+
+ if ( ( m_eSpawnPosition == FUNC_DOOR_SPAWN_OPEN ) && HasSpawnFlags( SF_DOOR_START_OPEN_OBSOLETE ) )
+ {
+ Warning("Door %s using obsolete 'Start Open' spawnflag with 'Spawn Position' set to 'Open'. Reverting to old behavior.\n", GetDebugName() );
+ }
+
+ CreateVPhysics();
+}
+
+void CBaseDoor::MovingSoundThink( void )
+{
+ CPASAttenuationFilter filter( this );
+ filter.MakeReliable();
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_STATIC;
+ if ( m_NoiseMovingClosed == NULL_STRING || m_toggle_state == TS_GOING_DOWN || m_toggle_state == TS_AT_BOTTOM )
+ {
+ ep.m_pSoundName = (char*)STRING(m_NoiseMoving);
+ }
+ else
+ {
+ ep.m_pSoundName = (char*)STRING(m_NoiseMovingClosed);
+ }
+ ep.m_flVolume = 1;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ EmitSound( filter, entindex(), ep );
+
+ //Only loop sounds in HL1 to maintain HL2 behavior
+ if( ShouldLoopMoveSound() )
+ {
+ float duration = enginesound->GetSoundDuration( ep.m_pSoundName );
+ SetContextThink( &CBaseDoor::MovingSoundThink, gpGlobals->curtime + duration, "MovingSound" );
+ }
+}
+
+void CBaseDoor::StartMovingSound( void )
+{
+ MovingSoundThink();
+
+#ifdef CSTRIKE_DLL // this event is only used by CS:S bots
+
+ CBasePlayer *player = ToBasePlayer(m_hActivator);
+ IGameEvent * event = gameeventmanager->CreateEvent( "door_moving" );
+ if( event )
+ {
+ event->SetInt( "entindex", entindex() );
+ event->SetInt( "userid", (player)?player->GetUserID():0 );
+ gameeventmanager->FireEvent( event );
+ }
+
+#endif
+}
+
+void CBaseDoor::StopMovingSound(void)
+{
+ SetContextThink( NULL, gpGlobals->curtime, "MovingSound" );
+ char *pSoundName;
+ if ( m_NoiseMovingClosed == NULL_STRING || m_toggle_state == TS_GOING_UP || m_toggle_state == TS_AT_TOP )
+ {
+ pSoundName = (char*)STRING(m_NoiseMoving);
+ }
+ else
+ {
+ pSoundName = (char*)STRING(m_NoiseMovingClosed);
+ }
+ StopSound( entindex(), CHAN_STATIC, pSoundName );
+}
+
+
+bool CBaseDoor::ShouldSavePhysics()
+{
+ // don't save physics if you're func_water
+ return !FClassnameIs( this, "func_water" );
+}
+
+//-----------------------------------------------------------------------------
+bool CBaseDoor::CreateVPhysics( )
+{
+ if ( !FClassnameIs( this, "func_water" ) )
+ {
+ //normal door
+ // NOTE: Create this even when the door is not solid to support constraints.
+ VPhysicsInitShadow( false, false );
+ }
+ else
+ {
+ // special contents
+ AddSolidFlags( FSOLID_VOLUME_CONTENTS );
+ SETBITS( m_spawnflags, SF_DOOR_SILENT ); // water is silent for now
+
+ IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false );
+ fluidparams_t fluid;
+
+ Assert( CollisionProp()->GetCollisionAngles() == vec3_angle );
+ fluid.damping = 0.01f;
+ fluid.surfacePlane[0] = 0;
+ fluid.surfacePlane[1] = 0;
+ fluid.surfacePlane[2] = 1;
+ fluid.surfacePlane[3] = CollisionProp()->GetCollisionOrigin().z + CollisionProp()->OBBMaxs().z - 1;
+ fluid.currentVelocity.Init(0,0,0);
+ fluid.torqueFactor = 0.1f;
+ fluid.viscosityFactor = 0.01f;
+ fluid.pGameData = static_cast<void *>(this);
+
+ //FIXME: Currently there's no way to specify that you want slime
+ fluid.contents = CONTENTS_WATER;
+
+ physenv->CreateFluidController( pPhysics, &fluid );
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDoor::Activate( void )
+{
+ BaseClass::Activate();
+
+ CBaseDoor *pDoorList[64];
+ m_bDoorGroup = true;
+
+ // force movement groups to sync!!!
+ int doorCount = GetDoorMovementGroup( pDoorList, ARRAYSIZE(pDoorList) );
+ for ( int i = 0; i < doorCount; i++ )
+ {
+ if ( pDoorList[i]->m_vecMoveDir == m_vecMoveDir )
+ {
+ bool error = false;
+ if ( pDoorList[i]->IsRotatingDoor() )
+ {
+ error = ( pDoorList[i]->GetLocalAngles() != GetLocalAngles() ) ? true : false;
+ }
+ else
+ {
+ error = ( pDoorList[i]->GetLocalOrigin() != GetLocalOrigin() ) ? true : false;
+ }
+ if ( error )
+ {
+ // don't do group blocking
+ m_bDoorGroup = false;
+#ifdef HL1_DLL
+ // UNDONE: This should probably fixup m_vecPosition1 & m_vecPosition2
+ Warning("Door group %s has misaligned origin!\n", STRING(GetEntityName()) );
+#endif
+ }
+ }
+ }
+
+ switch ( m_toggle_state )
+ {
+ case TS_AT_TOP:
+ UpdateAreaPortals( true );
+ break;
+ case TS_AT_BOTTOM:
+ UpdateAreaPortals( false );
+ break;
+ }
+
+#ifdef HL1_DLL
+ // Get a handle to my filter entity if there is one
+ if (m_iBlockFilterName != NULL_STRING)
+ {
+ m_hBlockFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iBlockFilterName, NULL ));
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+// This is ONLY used by the node graph to test movement through a door
+void CBaseDoor::InputSetToggleState( inputdata_t &inputdata )
+{
+ SetToggleState( inputdata.value.Int() );
+}
+
+void CBaseDoor::SetToggleState( int state )
+{
+ if ( state == TS_AT_TOP )
+ UTIL_SetOrigin( this, m_vecPosition2 );
+ else
+ UTIL_SetOrigin( this, m_vecPosition1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDoor::Precache( void )
+{
+ //Fill in a default value if necessary
+ if ( IsRotatingDoor() )
+ {
+ UTIL_ValidateSoundName( m_NoiseMoving, "RotDoorSound.DefaultMove" );
+ UTIL_ValidateSoundName( m_NoiseArrived, "RotDoorSound.DefaultArrive" );
+ UTIL_ValidateSoundName( m_ls.sLockedSound, "RotDoorSound.DefaultLocked" );
+ UTIL_ValidateSoundName( m_ls.sUnlockedSound,"DoorSound.Null" );
+ }
+ else
+ {
+ UTIL_ValidateSoundName( m_NoiseMoving, "DoorSound.DefaultMove" );
+ UTIL_ValidateSoundName( m_NoiseArrived, "DoorSound.DefaultArrive" );
+#ifndef HL1_DLL
+ UTIL_ValidateSoundName( m_ls.sLockedSound, "DoorSound.DefaultLocked" );
+#endif
+ UTIL_ValidateSoundName( m_ls.sUnlockedSound,"DoorSound.Null" );
+ }
+
+#ifdef HL1_DLL
+ if( m_ls.sLockedSound != NULL_STRING && strlen((char*)STRING(m_ls.sLockedSound)) < 4 )
+ {
+ // Too short to be ANYTHING ".wav", so it must be an old index into a long-lost
+ // array of sound choices. slam it to a known "deny" sound. We lose the designer's
+ // original selection, but we don't get unresponsive doors.
+ m_ls.sLockedSound = AllocPooledString("buttons/button2.wav");
+ }
+#endif//HL1_DLL
+
+ //Precache them all
+ PrecacheScriptSound( (char *) STRING(m_NoiseMoving) );
+ PrecacheScriptSound( (char *) STRING(m_NoiseArrived) );
+ PrecacheScriptSound( (char *) STRING(m_NoiseMovingClosed) );
+ PrecacheScriptSound( (char *) STRING(m_NoiseArrivedClosed) );
+ PrecacheScriptSound( (char *) STRING(m_ls.sLockedSound) );
+ PrecacheScriptSound( (char *) STRING(m_ls.sUnlockedSound) );
+
+ //Get sentence group names, for doors which are directly 'touched' to open
+ switch (m_bLockedSentence)
+ {
+ case 1: m_ls.sLockedSentence = AllocPooledString("NA"); break; // access denied
+ case 2: m_ls.sLockedSentence = AllocPooledString("ND"); break; // security lockout
+ case 3: m_ls.sLockedSentence = AllocPooledString("NF"); break; // blast door
+ case 4: m_ls.sLockedSentence = AllocPooledString("NFIRE"); break; // fire door
+ case 5: m_ls.sLockedSentence = AllocPooledString("NCHEM"); break; // chemical door
+ case 6: m_ls.sLockedSentence = AllocPooledString("NRAD"); break; // radiation door
+ case 7: m_ls.sLockedSentence = AllocPooledString("NCON"); break; // gen containment
+ case 8: m_ls.sLockedSentence = AllocPooledString("NH"); break; // maintenance door
+ case 9: m_ls.sLockedSentence = AllocPooledString("NG"); break; // broken door
+
+ default: m_ls.sLockedSentence = NULL_STRING; break;
+ }
+
+ switch (m_bUnlockedSentence)
+ {
+ case 1: m_ls.sUnlockedSentence = AllocPooledString("EA"); break; // access granted
+ case 2: m_ls.sUnlockedSentence = AllocPooledString("ED"); break; // security door
+ case 3: m_ls.sUnlockedSentence = AllocPooledString("EF"); break; // blast door
+ case 4: m_ls.sUnlockedSentence = AllocPooledString("EFIRE"); break; // fire door
+ case 5: m_ls.sUnlockedSentence = AllocPooledString("ECHEM"); break; // chemical door
+ case 6: m_ls.sUnlockedSentence = AllocPooledString("ERAD"); break; // radiation door
+ case 7: m_ls.sUnlockedSentence = AllocPooledString("ECON"); break; // gen containment
+ case 8: m_ls.sUnlockedSentence = AllocPooledString("EH"); break; // maintenance door
+
+ default: m_ls.sUnlockedSentence = NULL_STRING; break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Doors not tied to anything (e.g. button, another door) can be touched,
+// to make them activate.
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CBaseDoor::DoorTouch( CBaseEntity *pOther )
+{
+ if( m_ChainTarget != NULL_STRING )
+ ChainTouch( pOther );
+
+ // Ignore touches by anything but players.
+ if ( !pOther->IsPlayer() )
+ {
+#ifdef HL1_DLL
+ if( PassesBlockTouchFilter( pOther ) && m_toggle_state == TS_GOING_DOWN )
+ {
+ DoorGoUp();
+ }
+#endif
+ return;
+ }
+
+ // If door is not opened by touch, do nothing.
+ if ( !HasSpawnFlags(SF_DOOR_PTOUCH) )
+ {
+#ifdef HL1_DLL
+ if( m_toggle_state == TS_AT_BOTTOM )
+ {
+ PlayLockSounds(this, &m_ls, TRUE, FALSE);
+ }
+#endif//HL1_DLL
+
+ return;
+ }
+
+ // If door has master, and it's not ready to trigger,
+ // play 'locked' sound.
+ if (m_sMaster != NULL_STRING && !UTIL_IsMasterTriggered(m_sMaster, pOther))
+ {
+ PlayLockSounds(this, &m_ls, TRUE, FALSE);
+ }
+
+ if (m_bLocked)
+ {
+ m_OnLockedUse.FireOutput( pOther, pOther );
+ PlayLockSounds(this, &m_ls, TRUE, FALSE);
+ return;
+ }
+
+ // Remember who activated the door.
+ m_hActivator = pOther;
+
+ if (DoorActivate( ))
+ {
+ // Temporarily disable the touch function, until movement is finished.
+ SetTouch( NULL );
+ }
+}
+
+#ifdef HL1_DLL
+bool CBaseDoor::PassesBlockTouchFilter(CBaseEntity *pOther)
+{
+ CBaseFilter* pFilter = (CBaseFilter*)(m_hBlockFilter.Get());
+ return ( pFilter && pFilter->PassesFilter( this, pOther ) );
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Delays turning off area portals when closing doors to prevent visual artifacts
+//-----------------------------------------------------------------------------
+void CBaseDoor::CloseAreaPortalsThink( void )
+{
+ UpdateAreaPortals( false );
+ SetContextThink( NULL, gpGlobals->curtime, CLOSE_AREAPORTAL_THINK_CONTEXT );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : isOpen -
+//-----------------------------------------------------------------------------
+void CBaseDoor::UpdateAreaPortals( bool isOpen )
+{
+ // cancel pending close
+ SetContextThink( NULL, gpGlobals->curtime, CLOSE_AREAPORTAL_THINK_CONTEXT );
+
+ if ( IsRotatingDoor() && HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE) ) // logic inverted when using rot doors that start open
+ isOpen = !isOpen;
+
+ string_t name = GetEntityName();
+ if ( !name )
+ return;
+
+ CBaseEntity *pPortal = NULL;
+ while ( ( pPortal = gEntList.FindEntityByClassname( pPortal, "func_areaportal" ) ) != NULL )
+ {
+ if ( pPortal->HasTarget( name ) )
+ {
+ // USE_ON means open the portal, off means close it
+ pPortal->Use( this, this, isOpen?USE_ON:USE_OFF, 0 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the player uses the door.
+// Input : pActivator -
+// pCaller -
+// useType -
+// value -
+//-----------------------------------------------------------------------------
+void CBaseDoor::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ m_hActivator = pActivator;
+
+ if( m_ChainTarget != NULL_STRING )
+ ChainUse();
+
+ // We can't +use this if it can't be +used
+ if ( m_hActivator != NULL && m_hActivator->IsPlayer() && HasSpawnFlags( SF_DOOR_PUSE ) == false )
+ {
+ PlayLockSounds( this, &m_ls, TRUE, FALSE );
+ return;
+ }
+
+ bool bAllowUse = false;
+
+ // if not ready to be used, ignore "use" command.
+ if( HasSpawnFlags(SF_DOOR_NEW_USE_RULES) )
+ {
+ //New behavior:
+ // If not ready to be used, ignore "use" command.
+ // Allow use in these cases:
+ // - when the door is closed/closing
+ // - when the door is open/opening and can be manually closed
+ if ( ( m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN ) || ( HasSpawnFlags(SF_DOOR_NO_AUTO_RETURN) && ( m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP ) ) )
+ bAllowUse = true;
+ }
+ else
+ {
+ // Legacy behavior:
+ if (m_toggle_state == TS_AT_BOTTOM || (HasSpawnFlags(SF_DOOR_NO_AUTO_RETURN) && m_toggle_state == TS_AT_TOP) )
+ bAllowUse = true;
+ }
+
+ if( bAllowUse )
+ {
+ if (m_bLocked)
+ {
+ m_OnLockedUse.FireOutput( pActivator, pCaller );
+ PlayLockSounds(this, &m_ls, TRUE, FALSE);
+ }
+ else
+ {
+ DoorActivate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Passes Use along to certain named doors.
+//-----------------------------------------------------------------------------
+void CBaseDoor::ChainUse( void )
+{
+ if ( m_isChaining )
+ return;
+
+ CBaseEntity *ent = NULL;
+ while ( ( ent = gEntList.FindEntityByName( ent, m_ChainTarget, NULL ) ) != NULL )
+ {
+ if ( ent == this )
+ continue;
+
+ CBaseDoor *door = dynamic_cast< CBaseDoor * >( ent );
+ if ( door )
+ {
+ door->SetChaining( true );
+ door->Use( m_hActivator, NULL, USE_TOGGLE, 0.0f ); // only the first param is used
+ door->SetChaining( false );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Passes Touch along to certain named doors.
+//-----------------------------------------------------------------------------
+void CBaseDoor::ChainTouch( CBaseEntity *pOther )
+{
+ if ( m_isChaining )
+ return;
+
+ CBaseEntity *ent = NULL;
+ while ( ( ent = gEntList.FindEntityByName( ent, m_ChainTarget, NULL ) ) != NULL )
+ {
+ if ( ent == this )
+ continue;
+
+ CBaseDoor *door = dynamic_cast< CBaseDoor * >( ent );
+ if ( door )
+ {
+ door->SetChaining( true );
+ door->Touch( pOther );
+ door->SetChaining( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Closes the door if it is not already closed.
+//-----------------------------------------------------------------------------
+void CBaseDoor::InputClose( inputdata_t &inputdata )
+{
+ if ( m_toggle_state != TS_AT_BOTTOM )
+ {
+ DoorGoDown();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler that locks the door.
+//-----------------------------------------------------------------------------
+void CBaseDoor::InputLock( inputdata_t &inputdata )
+{
+ Lock();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Opens the door if it is not already open.
+//-----------------------------------------------------------------------------
+void CBaseDoor::InputOpen( inputdata_t &inputdata )
+{
+ if (m_toggle_state != TS_AT_TOP && m_toggle_state != TS_GOING_UP )
+ {
+ // I'm locked, can't open
+ if (m_bLocked)
+ return;
+
+ // Play door unlock sounds.
+ PlayLockSounds(this, &m_ls, false, false);
+ DoorGoUp();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Opens the door if it is not already open.
+//-----------------------------------------------------------------------------
+void CBaseDoor::InputToggle( inputdata_t &inputdata )
+{
+ // I'm locked, can't open
+ if (m_bLocked)
+ return;
+
+ if (m_toggle_state == TS_AT_BOTTOM)
+ {
+ DoorGoUp();
+ }
+ else if (m_toggle_state == TS_AT_TOP)
+ {
+ DoorGoDown();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handler that unlocks the door.
+//-----------------------------------------------------------------------------
+void CBaseDoor::InputUnlock( inputdata_t &inputdata )
+{
+ Unlock();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseDoor::InputSetSpeed( inputdata_t &inputdata )
+{
+ m_flSpeed = inputdata.value.Float();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Locks the door so that it cannot be opened.
+//-----------------------------------------------------------------------------
+void CBaseDoor::Lock( void )
+{
+ m_bLocked = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Unlocks the door so that it can be opened.
+//-----------------------------------------------------------------------------
+void CBaseDoor::Unlock( void )
+{
+ m_bLocked = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Causes the door to "do its thing", i.e. start moving, and cascade activation.
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseDoor::DoorActivate( )
+{
+ if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator))
+ return 0;
+
+ if (HasSpawnFlags(SF_DOOR_NO_AUTO_RETURN) && m_toggle_state == TS_AT_TOP)
+ {
+ // door should close
+ DoorGoDown();
+ }
+ else
+ {
+ // door should open
+ // play door unlock sounds
+ PlayLockSounds(this, &m_ls, FALSE, FALSE);
+
+ if ( m_toggle_state != TS_AT_TOP && m_toggle_state != TS_GOING_UP )
+ {
+ DoorGoUp();
+ }
+ }
+
+ return 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the door going to its "up" position (simply ToggleData->vecPosition2).
+//-----------------------------------------------------------------------------
+void CBaseDoor::DoorGoUp( void )
+{
+ edict_t *pevActivator;
+
+ UpdateAreaPortals( true );
+ // It could be going-down, if blocked.
+ ASSERT(m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN);
+
+ // emit door moving and stop sounds on CHAN_STATIC so that the multicast doesn't
+ // filter them out and leave a client stuck with looping door sounds!
+ if ( !HasSpawnFlags(SF_DOOR_SILENT ) )
+ {
+ // If we're not moving already, start the moving noise
+ if ( m_toggle_state != TS_GOING_UP && m_toggle_state != TS_GOING_DOWN )
+ {
+ StartMovingSound();
+ }
+ }
+
+ m_toggle_state = TS_GOING_UP;
+
+ SetMoveDone( &CBaseDoor::DoorHitTop );
+ if ( IsRotatingDoor() ) // !!! BUGBUG Triggered doors don't work with this yet
+ {
+ float sign = 1.0;
+
+ if ( m_hActivator != NULL )
+ {
+ pevActivator = m_hActivator->edict();
+
+ if ( !HasSpawnFlags( SF_DOOR_ONEWAY ) && m_vecMoveAng.y ) // Y axis rotation, move away from the player
+ {
+ // Positive is CCW, negative is CW, so make 'sign' 1 or -1 based on which way we want to open.
+ // Important note: All doors face East at all times, and twist their local angle to open.
+ // So you can't look at the door's facing to determine which way to open.
+
+ Vector nearestPoint;
+ CollisionProp()->CalcNearestPoint( m_hActivator->GetAbsOrigin(), &nearestPoint );
+ Vector activatorToNearestPoint = nearestPoint - m_hActivator->GetAbsOrigin();
+ activatorToNearestPoint.z = 0;
+
+ Vector activatorToOrigin = GetAbsOrigin() - m_hActivator->GetAbsOrigin();
+ activatorToOrigin.z = 0;
+
+ // Point right hand at door hinge, curl hand towards closest spot on door, if thumb
+ // is up, open door CW. -- Department of Basic Cross Product Understanding for Noobs
+ Vector cross = activatorToOrigin.Cross( activatorToNearestPoint );
+
+ if( cross.z > 0.0f )
+ {
+ sign = -1.0f;
+ }
+ }
+ }
+ AngularMove(m_vecAngle2*sign, m_flSpeed);
+ }
+ else
+ {
+ LinearMove(m_vecPosition2, m_flSpeed);
+ }
+
+ //Fire our open ouput
+ m_OnOpen.FireOutput( this, this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: The door has reached the "up" position. Either go back down, or
+// wait for another activation.
+//-----------------------------------------------------------------------------
+void CBaseDoor::DoorHitTop( void )
+{
+ if ( !HasSpawnFlags( SF_DOOR_SILENT ) )
+ {
+ CPASAttenuationFilter filter( this );
+ filter.MakeReliable();
+ StopMovingSound();
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_STATIC;
+ ep.m_pSoundName = (char*)STRING(m_NoiseArrived);
+ ep.m_flVolume = 1;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ EmitSound( filter, entindex(), ep );
+ }
+
+ ASSERT(m_toggle_state == TS_GOING_UP);
+ m_toggle_state = TS_AT_TOP;
+
+ // toggle-doors don't come down automatically, they wait for refire.
+ if (HasSpawnFlags( SF_DOOR_NO_AUTO_RETURN))
+ {
+ // Re-instate touch method, movement is complete
+ SetTouch( &CBaseDoor::DoorTouch );
+ }
+ else
+ {
+ // In flWait seconds, DoorGoDown will fire, unless wait is -1, then door stays open
+ SetMoveDoneTime( m_flWait );
+ SetMoveDone( &CBaseDoor::DoorGoDown );
+
+ if ( m_flWait == -1 )
+ {
+ SetNextThink( TICK_NEVER_THINK );
+ }
+ }
+
+ if (HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE) )
+ {
+ m_OnFullyClosed.FireOutput(this, this);
+ }
+ else
+ {
+ m_OnFullyOpen.FireOutput(this, this);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the door going to its "down" position (simply ToggleData->vecPosition1).
+//-----------------------------------------------------------------------------
+void CBaseDoor::DoorGoDown( void )
+{
+ if ( !HasSpawnFlags( SF_DOOR_SILENT ) )
+ {
+ // If we're not moving already, start the moving noise
+ if ( m_toggle_state != TS_GOING_UP && m_toggle_state != TS_GOING_DOWN )
+ {
+ StartMovingSound();
+ }
+ }
+
+#ifdef DOOR_ASSERT
+ ASSERT(m_toggle_state == TS_AT_TOP);
+#endif // DOOR_ASSERT
+ m_toggle_state = TS_GOING_DOWN;
+
+ SetMoveDone( &CBaseDoor::DoorHitBottom );
+ if ( IsRotatingDoor() )//rotating door
+ AngularMove( m_vecAngle1, m_flSpeed);
+ else
+ LinearMove( m_vecPosition1, m_flSpeed);
+
+ //Fire our closed output
+ m_OnClose.FireOutput( this, this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: The door has reached the "down" position. Back to quiescence.
+//-----------------------------------------------------------------------------
+void CBaseDoor::DoorHitBottom( void )
+{
+ if ( !HasSpawnFlags( SF_DOOR_SILENT ) )
+ {
+ CPASAttenuationFilter filter( this );
+ filter.MakeReliable();
+
+ StopMovingSound();
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_STATIC;
+ if ( m_NoiseArrivedClosed == NULL_STRING )
+ ep.m_pSoundName = (char*)STRING(m_NoiseArrived);
+ else
+ ep.m_pSoundName = (char*)STRING(m_NoiseArrivedClosed);
+ ep.m_flVolume = 1;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ EmitSound( filter, entindex(), ep );
+ }
+
+ ASSERT(m_toggle_state == TS_GOING_DOWN);
+ m_toggle_state = TS_AT_BOTTOM;
+
+ // Re-instate touch method, cycle is complete
+ SetTouch( &CBaseDoor::DoorTouch );
+
+ if (HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE))
+ {
+ m_OnFullyOpen.FireOutput(m_hActivator, this);
+ }
+ else
+ {
+ m_OnFullyClosed.FireOutput(m_hActivator, this);
+ }
+
+ // Close the area portals just after the door closes, to prevent visual artifacts in multiplayer games
+ SetContextThink( &CBaseDoor::CloseAreaPortalsThink, gpGlobals->curtime + 0.5f, CLOSE_AREAPORTAL_THINK_CONTEXT );
+}
+
+
+// Lists all doors in the same movement group as this one
+int CBaseDoor::GetDoorMovementGroup( CBaseDoor *pDoorList[], int listMax )
+{
+ int count = 0;
+ CBaseEntity *pTarget = NULL;
+
+ // Block all door pieces with the same targetname here.
+ if ( GetEntityName() != NULL_STRING )
+ {
+ for (;;)
+ {
+ pTarget = gEntList.FindEntityByName( pTarget, GetEntityName(), NULL );
+
+ if ( pTarget != this )
+ {
+ if ( !pTarget )
+ break;
+
+ CBaseDoor *pDoor = dynamic_cast<CBaseDoor *>(pTarget);
+
+ if ( pDoor && count < listMax )
+ {
+ pDoorList[count] = pDoor;
+ count++;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called the first frame that the door is blocked while opening or closing.
+// Input : pOther - The blocking entity.
+//-----------------------------------------------------------------------------
+void CBaseDoor::StartBlocked( CBaseEntity *pOther )
+{
+ //
+ // Fire whatever events we need to due to our blocked state.
+ //
+ if (m_toggle_state == TS_GOING_DOWN)
+ {
+ m_OnBlockedClosing.FireOutput(pOther, this);
+ }
+ else
+ {
+ m_OnBlockedOpening.FireOutput(pOther, this);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame when the door is blocked while opening or closing.
+// Input : pOther - The blocking entity.
+//-----------------------------------------------------------------------------
+void CBaseDoor::Blocked( CBaseEntity *pOther )
+{
+ // Hurt the blocker a little.
+ if ( m_flBlockDamage )
+ {
+ // if the door is marked "force closed" or it has a negative wait, then there's nothing to do but
+ // push/damage the object.
+ // If block damage is set, but this object is a physics prop that can't be damaged, just
+ // give up and disable collisions
+ if ( (m_bForceClosed || m_flWait < 0) && pOther->GetMoveType() == MOVETYPE_VPHYSICS &&
+ (pOther->m_takedamage == DAMAGE_NO || pOther->m_takedamage == DAMAGE_EVENTS_ONLY) )
+ {
+ EntityPhysics_CreateSolver( this, pOther, true, 4.0f );
+ }
+ else
+ {
+ pOther->TakeDamage( CTakeDamageInfo( this, this, m_flBlockDamage, DMG_CRUSH ) );
+ }
+ }
+
+ // If we're set to force ourselves closed, keep going
+ if ( m_bForceClosed )
+ return;
+
+ // if a door has a negative wait, it would never come back if blocked,
+ // so let it just squash the object to death real fast
+ if (m_flWait >= 0)
+ {
+ if (m_toggle_state == TS_GOING_DOWN)
+ {
+ DoorGoUp();
+ }
+ else
+ {
+ DoorGoDown();
+ }
+ }
+
+ // Block all door pieces with the same targetname here.
+ if ( GetEntityName() != NULL_STRING )
+ {
+ CBaseDoor *pDoorList[64];
+ int doorCount = GetDoorMovementGroup( pDoorList, ARRAYSIZE(pDoorList) );
+
+ for ( int i = 0; i < doorCount; i++ )
+ {
+ CBaseDoor *pDoor = pDoorList[i];
+
+ if ( pDoor->m_flWait >= 0)
+ {
+ if (m_bDoorGroup && pDoor->m_vecMoveDir == m_vecMoveDir && pDoor->GetAbsVelocity() == GetAbsVelocity() && pDoor->GetLocalAngularVelocity() == GetLocalAngularVelocity())
+ {
+ pDoor->m_nSimulationTick = m_nSimulationTick; // don't run simulation this frame if you haven't run yet
+
+ // this is the most hacked, evil, bastardized thing I've ever seen. kjb
+ if ( !pDoor->IsRotatingDoor() )
+ {// set origin to realign normal doors
+ pDoor->SetLocalOrigin( GetLocalOrigin() );
+ pDoor->SetAbsVelocity( vec3_origin );// stop!
+
+ }
+ else
+ {// set angles to realign rotating doors
+ pDoor->SetLocalAngles( GetLocalAngles() );
+ pDoor->SetLocalAngularVelocity( vec3_angle );
+ }
+ }
+
+ if ( pDoor->m_toggle_state == TS_GOING_DOWN)
+ pDoor->DoorGoUp();
+ else
+ pDoor->DoorGoDown();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called the first frame that the door is unblocked while opening or closing.
+//-----------------------------------------------------------------------------
+void CBaseDoor::EndBlocked( void )
+{
+ //
+ // Fire whatever events we need to due to our unblocked state.
+ //
+ if (m_toggle_state == TS_GOING_DOWN)
+ {
+ m_OnUnblockedClosing.FireOutput(this, this);
+ }
+ else
+ {
+ m_OnUnblockedOpening.FireOutput(this, this);
+ }
+}
+
+
+/*func_door_rotating
+
+TOGGLE causes the door to wait in both the start and end states for
+a trigger event.
+
+START_OPEN causes the door to move to its destination when spawned,
+and operate in reverse. It is used to temporarily or permanently
+close off an area when triggered (not usefull for touch or
+takedamage doors).
+
+You need to have an origin brush as part of this entity. The
+center of that brush will be
+the point around which it is rotated. It will rotate around the Z
+axis by default. You can
+check either the X_AXIS or Y_AXIS box to change that.
+
+"distance" is how many degrees the door will be rotated.
+"speed" determines how fast the door moves; default value is 100.
+
+REVERSE will cause the door to rotate in the opposite direction.
+
+"angle" determines the opening direction
+"targetname" if set, no touch field will be spawned and a remote
+button or trigger field activates the door.
+"health" if set, door must be shot open
+"speed" movement speed (100 default)
+"wait" wait before returning (3 default, -1 = never return)
+"dmg" damage to inflict when blocked (2 default)
+*/
+
+//==================================================
+// CRotDoor
+//==================================================
+
+class CRotDoor : public CBaseDoor
+{
+public:
+ DECLARE_CLASS( CRotDoor, CBaseDoor );
+
+ void Spawn( void );
+ bool CreateVPhysics();
+ // This is ONLY used by the node graph to test movement through a door
+ virtual void SetToggleState( int state );
+ virtual bool IsRotatingDoor() { return true; }
+
+ bool m_bSolidBsp;
+
+ DECLARE_DATADESC();
+};
+
+LINK_ENTITY_TO_CLASS( func_door_rotating, CRotDoor );
+
+BEGIN_DATADESC( CRotDoor )
+ DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CRotDoor::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ // set the axis of rotation
+ CBaseToggle::AxisDir();
+
+ // check for clockwise rotation
+ if ( HasSpawnFlags(SF_DOOR_ROTATE_BACKWARDS) )
+ m_vecMoveAng = m_vecMoveAng * -1;
+
+ //m_flWait = 2; who the hell did this? (sjb)
+ m_vecAngle1 = GetLocalAngles();
+ m_vecAngle2 = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance;
+
+ ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating door start/end positions are equal\n");
+
+ // Starting open allows a func_door to be lighted in the closed position but
+ // spawn in the open position
+ //
+ // SF_DOOR_START_OPEN_OBSOLETE is an old broken way of spawning open that has
+ // been deprecated.
+ if ( HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE) )
+ {
+ // swap pos1 and pos2, put door at pos2, invert movement direction
+ QAngle vecNewAngles = m_vecAngle2;
+ m_vecAngle2 = m_vecAngle1;
+ m_vecAngle1 = vecNewAngles;
+ m_vecMoveAng = -m_vecMoveAng;
+
+ // We've already had our physics setup in BaseClass::Spawn, so teleport to our
+ // current position. If we don't do this, our vphysics shadow will not update.
+ Teleport( NULL, &m_vecAngle1, NULL );
+
+ m_toggle_state = TS_AT_BOTTOM;
+ }
+ else if ( m_eSpawnPosition == FUNC_DOOR_SPAWN_OPEN )
+ {
+ // We've already had our physics setup in BaseClass::Spawn, so teleport to our
+ // current position. If we don't do this, our vphysics shadow will not update.
+ Teleport( NULL, &m_vecAngle2, NULL );
+ m_toggle_state = TS_AT_TOP;
+ }
+ else
+ {
+ m_toggle_state = TS_AT_BOTTOM;
+ }
+
+#ifdef HL1_DLL
+ SetSolid( SOLID_VPHYSICS );
+#endif
+
+ // Slam the object back to solid - if we really want it to be solid.
+ if ( m_bSolidBsp )
+ {
+ SetSolid( SOLID_BSP );
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+bool CRotDoor::CreateVPhysics()
+{
+ if ( !IsSolidFlagSet( FSOLID_NOT_SOLID ) )
+ {
+ VPhysicsInitShadow( false, false );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+// This is ONLY used by the node graph to test movement through a door
+void CRotDoor::SetToggleState( int state )
+{
+ if ( state == TS_AT_TOP )
+ SetLocalAngles( m_vecAngle2 );
+ else
+ SetLocalAngles( m_vecAngle1 );
+}