Team Fortress 2 Source Code as on 22/4/2020
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.

1433 lines
41 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements two types of doors: linear and rotating.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "doors.h"
  9. #include "entitylist.h"
  10. #include "physics.h"
  11. #include "ndebugoverlay.h"
  12. #include "engine/IEngineSound.h"
  13. #include "physics_npc_solver.h"
  14. #ifdef HL1_DLL
  15. #include "filters.h"
  16. #endif
  17. #ifdef CSTRIKE_DLL
  18. #include "KeyValues.h"
  19. #endif
  20. #ifdef TF_DLL
  21. #include "tf_gamerules.h"
  22. #endif // TF_DLL
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. #define CLOSE_AREAPORTAL_THINK_CONTEXT "CloseAreaportalThink"
  26. BEGIN_DATADESC( CBaseDoor )
  27. DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ),
  28. DEFINE_FIELD( m_bLockedSentence, FIELD_CHARACTER ),
  29. DEFINE_FIELD( m_bUnlockedSentence, FIELD_CHARACTER ),
  30. DEFINE_KEYFIELD( m_NoiseMoving, FIELD_SOUNDNAME, "noise1" ),
  31. DEFINE_KEYFIELD( m_NoiseArrived, FIELD_SOUNDNAME, "noise2" ),
  32. DEFINE_KEYFIELD( m_NoiseMovingClosed, FIELD_SOUNDNAME, "startclosesound" ),
  33. DEFINE_KEYFIELD( m_NoiseArrivedClosed, FIELD_SOUNDNAME, "closesound" ),
  34. DEFINE_KEYFIELD( m_ChainTarget, FIELD_STRING, "chainstodoor" ),
  35. // DEFINE_FIELD( m_isChaining, FIELD_BOOLEAN ),
  36. // DEFINE_FIELD( m_ls, locksound_t ),
  37. // DEFINE_FIELD( m_isChaining, FIELD_BOOLEAN ),
  38. DEFINE_KEYFIELD( m_ls.sLockedSound, FIELD_SOUNDNAME, "locked_sound" ),
  39. DEFINE_KEYFIELD( m_ls.sUnlockedSound, FIELD_SOUNDNAME, "unlocked_sound" ),
  40. DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ),
  41. DEFINE_KEYFIELD( m_flWaveHeight, FIELD_FLOAT, "WaveHeight" ),
  42. DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "dmg" ),
  43. DEFINE_KEYFIELD( m_eSpawnPosition, FIELD_INTEGER, "spawnpos" ),
  44. DEFINE_KEYFIELD( m_bForceClosed, FIELD_BOOLEAN, "forceclosed" ),
  45. DEFINE_FIELD( m_bDoorGroup, FIELD_BOOLEAN ),
  46. #ifdef HL1_DLL
  47. DEFINE_KEYFIELD( m_iBlockFilterName, FIELD_STRING, "filtername" ),
  48. DEFINE_FIELD( m_hBlockFilter, FIELD_EHANDLE ),
  49. #endif
  50. DEFINE_KEYFIELD( m_bLoopMoveSound, FIELD_BOOLEAN, "loopmovesound" ),
  51. DEFINE_KEYFIELD( m_bIgnoreDebris, FIELD_BOOLEAN, "ignoredebris" ),
  52. DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ),
  53. DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ),
  54. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  55. DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
  56. DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
  57. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ),
  58. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetToggleState", InputSetToggleState ),
  59. DEFINE_OUTPUT( m_OnBlockedOpening, "OnBlockedOpening" ),
  60. DEFINE_OUTPUT( m_OnBlockedClosing, "OnBlockedClosing" ),
  61. DEFINE_OUTPUT( m_OnUnblockedOpening, "OnUnblockedOpening" ),
  62. DEFINE_OUTPUT( m_OnUnblockedClosing, "OnUnblockedClosing" ),
  63. DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ),
  64. DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ),
  65. DEFINE_OUTPUT( m_OnClose, "OnClose" ),
  66. DEFINE_OUTPUT( m_OnOpen, "OnOpen" ),
  67. DEFINE_OUTPUT( m_OnLockedUse, "OnLockedUse" ),
  68. // Function Pointers
  69. DEFINE_FUNCTION( DoorTouch ),
  70. DEFINE_FUNCTION( DoorGoUp ),
  71. DEFINE_FUNCTION( DoorGoDown ),
  72. DEFINE_FUNCTION( DoorHitTop ),
  73. DEFINE_FUNCTION( DoorHitBottom ),
  74. DEFINE_THINKFUNC( MovingSoundThink ),
  75. DEFINE_THINKFUNC( CloseAreaPortalsThink ),
  76. END_DATADESC()
  77. LINK_ENTITY_TO_CLASS( func_door, CBaseDoor );
  78. //
  79. // func_water is implemented as a linear door so we can raise/lower the water level.
  80. //
  81. LINK_ENTITY_TO_CLASS( func_water, CBaseDoor );
  82. // SendTable stuff.
  83. IMPLEMENT_SERVERCLASS_ST(CBaseDoor, DT_BaseDoor)
  84. SendPropFloat (SENDINFO(m_flWaveHeight), 8, SPROP_ROUNDUP, 0.0f, 8.0f),
  85. END_SEND_TABLE()
  86. #define DOOR_SENTENCEWAIT 6
  87. #define DOOR_SOUNDWAIT 1
  88. #define BUTTON_SOUNDWAIT 0.5
  89. //-----------------------------------------------------------------------------
  90. // Purpose: play door or button locked or unlocked sounds.
  91. // NOTE: this routine is shared by doors and buttons
  92. // Input : pEdict -
  93. // pls -
  94. // flocked - if true, play 'door is locked' sound, otherwise play 'door
  95. // is unlocked' sound.
  96. // fbutton -
  97. //-----------------------------------------------------------------------------
  98. void PlayLockSounds(CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton)
  99. {
  100. if ( pEdict->HasSpawnFlags( SF_DOOR_SILENT ) )
  101. {
  102. return;
  103. }
  104. float flsoundwait = ( fbutton ) ? BUTTON_SOUNDWAIT : DOOR_SOUNDWAIT;
  105. if ( flocked )
  106. {
  107. int fplaysound = (pls->sLockedSound != NULL_STRING && gpGlobals->curtime > pls->flwaitSound);
  108. int fplaysentence = (pls->sLockedSentence != NULL_STRING && !pls->bEOFLocked && gpGlobals->curtime > pls->flwaitSentence);
  109. float fvol = ( fplaysound && fplaysentence ) ? 0.25f : 1.0f;
  110. // if there is a locked sound, and we've debounced, play sound
  111. if (fplaysound)
  112. {
  113. // play 'door locked' sound
  114. CPASAttenuationFilter filter( pEdict );
  115. EmitSound_t ep;
  116. ep.m_nChannel = CHAN_ITEM;
  117. ep.m_pSoundName = (char*)STRING(pls->sLockedSound);
  118. ep.m_flVolume = fvol;
  119. ep.m_SoundLevel = SNDLVL_NORM;
  120. CBaseEntity::EmitSound( filter, pEdict->entindex(), ep );
  121. pls->flwaitSound = gpGlobals->curtime + flsoundwait;
  122. }
  123. // if there is a sentence, we've not played all in list, and we've debounced, play sound
  124. if (fplaysentence)
  125. {
  126. // play next 'door locked' sentence in group
  127. int iprev = pls->iLockedSentence;
  128. pls->iLockedSentence = SENTENCEG_PlaySequentialSz( pEdict->edict(),
  129. STRING(pls->sLockedSentence),
  130. 0.85f,
  131. SNDLVL_NORM,
  132. 0,
  133. 100,
  134. pls->iLockedSentence,
  135. FALSE);
  136. pls->iUnlockedSentence = 0;
  137. // make sure we don't keep calling last sentence in list
  138. pls->bEOFLocked = (iprev == pls->iLockedSentence);
  139. pls->flwaitSentence = gpGlobals->curtime + DOOR_SENTENCEWAIT;
  140. }
  141. }
  142. else
  143. {
  144. // UNLOCKED SOUND
  145. int fplaysound = (pls->sUnlockedSound != NULL_STRING && gpGlobals->curtime > pls->flwaitSound);
  146. int fplaysentence = (pls->sUnlockedSentence != NULL_STRING && !pls->bEOFUnlocked && gpGlobals->curtime > pls->flwaitSentence);
  147. float fvol;
  148. // if playing both sentence and sound, lower sound volume so we hear sentence
  149. fvol = ( fplaysound && fplaysentence ) ? 0.25f : 1.0f;
  150. // play 'door unlocked' sound if set
  151. if (fplaysound)
  152. {
  153. CPASAttenuationFilter filter( pEdict );
  154. EmitSound_t ep;
  155. ep.m_nChannel = CHAN_ITEM;
  156. ep.m_pSoundName = (char*)STRING(pls->sUnlockedSound);
  157. ep.m_flVolume = fvol;
  158. ep.m_SoundLevel = SNDLVL_NORM;
  159. CBaseEntity::EmitSound( filter, pEdict->entindex(), ep );
  160. pls->flwaitSound = gpGlobals->curtime + flsoundwait;
  161. }
  162. // play next 'door unlocked' sentence in group
  163. if (fplaysentence)
  164. {
  165. int iprev = pls->iUnlockedSentence;
  166. pls->iUnlockedSentence = SENTENCEG_PlaySequentialSz(pEdict->edict(), STRING(pls->sUnlockedSentence),
  167. 0.85, SNDLVL_NORM, 0, 100, pls->iUnlockedSentence, FALSE);
  168. pls->iLockedSentence = 0;
  169. // make sure we don't keep calling last sentence in list
  170. pls->bEOFUnlocked = (iprev == pls->iUnlockedSentence);
  171. pls->flwaitSentence = gpGlobals->curtime + DOOR_SENTENCEWAIT;
  172. }
  173. }
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose: Cache user-entity-field values until spawn is called.
  177. // Input : szKeyName -
  178. // szValue -
  179. // Output : Returns true.
  180. //-----------------------------------------------------------------------------
  181. bool CBaseDoor::KeyValue( const char *szKeyName, const char *szValue )
  182. {
  183. if (FStrEq(szKeyName, "locked_sentence"))
  184. {
  185. m_bLockedSentence = atof(szValue);
  186. }
  187. else if (FStrEq(szKeyName, "unlocked_sentence"))
  188. {
  189. m_bUnlockedSentence = atof(szValue);
  190. }
  191. else
  192. return BaseClass::KeyValue( szKeyName, szValue );
  193. return true;
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. void CBaseDoor::Spawn()
  199. {
  200. Precache();
  201. #ifdef HL1_DLL
  202. SetSolid( SOLID_BSP );
  203. #else
  204. if ( GetMoveParent() && GetRootMoveParent()->GetSolid() == SOLID_BSP )
  205. {
  206. SetSolid( SOLID_BSP );
  207. }
  208. else
  209. {
  210. SetSolid( SOLID_VPHYSICS );
  211. }
  212. #endif
  213. // Convert movedir from angles to a vector
  214. QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z );
  215. AngleVectors( angMoveDir, &m_vecMoveDir );
  216. SetModel( STRING( GetModelName() ) );
  217. m_vecPosition1 = GetLocalOrigin();
  218. // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
  219. Vector vecOBB = CollisionProp()->OBBSize();
  220. vecOBB -= Vector( 2, 2, 2 );
  221. m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * (DotProductAbs( m_vecMoveDir, vecOBB ) - m_flLip));
  222. if ( !IsRotatingDoor() )
  223. {
  224. if ( ( m_eSpawnPosition == FUNC_DOOR_SPAWN_OPEN ) || HasSpawnFlags( SF_DOOR_START_OPEN_OBSOLETE ) )
  225. { // swap pos1 and pos2, put door at pos2
  226. UTIL_SetOrigin( this, m_vecPosition2);
  227. m_toggle_state = TS_AT_TOP;
  228. }
  229. else
  230. {
  231. m_toggle_state = TS_AT_BOTTOM;
  232. }
  233. }
  234. if (HasSpawnFlags(SF_DOOR_LOCKED))
  235. {
  236. m_bLocked = true;
  237. }
  238. SetMoveType( MOVETYPE_PUSH );
  239. if (m_flSpeed == 0)
  240. {
  241. m_flSpeed = 100;
  242. }
  243. SetTouch( &CBaseDoor::DoorTouch );
  244. if ( !FClassnameIs( this, "func_water" ) )
  245. {
  246. if ( HasSpawnFlags(SF_DOOR_PASSABLE) )
  247. {
  248. //normal door
  249. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  250. AddSolidFlags( FSOLID_NOT_SOLID );
  251. }
  252. if ( HasSpawnFlags( SF_DOOR_NONSOLID_TO_PLAYER ) )
  253. {
  254. SetCollisionGroup( COLLISION_GROUP_PASSABLE_DOOR );
  255. // HACKHACK: Set this hoping that any children of the door that get blocked by the player
  256. // will get fixed up by vphysics
  257. // NOTE: We could decouple this as a separate behavior, but managing player collisions is already complex enough.
  258. // NOTE: This is necessary to prevent the player from blocking the wrecked train car in ep2_outland_01
  259. AddFlag( FL_UNBLOCKABLE_BY_PLAYER );
  260. }
  261. if ( m_bIgnoreDebris )
  262. {
  263. // both of these flags want to set the collision group and
  264. // there isn't a combo group
  265. Assert( !HasSpawnFlags( SF_DOOR_NONSOLID_TO_PLAYER ) );
  266. if ( HasSpawnFlags( SF_DOOR_NONSOLID_TO_PLAYER ) )
  267. {
  268. Warning("Door %s with conflicting collision settings, removing ignoredebris\n", GetDebugName() );
  269. }
  270. else
  271. {
  272. SetCollisionGroup( COLLISION_GROUP_INTERACTIVE );
  273. }
  274. }
  275. }
  276. if ( ( m_eSpawnPosition == FUNC_DOOR_SPAWN_OPEN ) && HasSpawnFlags( SF_DOOR_START_OPEN_OBSOLETE ) )
  277. {
  278. Warning("Door %s using obsolete 'Start Open' spawnflag with 'Spawn Position' set to 'Open'. Reverting to old behavior.\n", GetDebugName() );
  279. }
  280. CreateVPhysics();
  281. #ifdef TF_DLL
  282. if ( TFGameRules() && TFGameRules()->IsMultiplayer() )
  283. {
  284. // Never block doors in TF2 - to prevent various exploits.
  285. m_bIgnoreNonPlayerEntsOnBlock = true;
  286. }
  287. #else
  288. m_bIgnoreNonPlayerEntsOnBlock = false;
  289. #endif // TF_DLL
  290. }
  291. void CBaseDoor::MovingSoundThink( void )
  292. {
  293. CPASAttenuationFilter filter( this );
  294. filter.MakeReliable();
  295. EmitSound_t ep;
  296. ep.m_nChannel = CHAN_STATIC;
  297. if ( m_NoiseMovingClosed == NULL_STRING || m_toggle_state == TS_GOING_DOWN || m_toggle_state == TS_AT_BOTTOM )
  298. {
  299. ep.m_pSoundName = (char*)STRING(m_NoiseMoving);
  300. }
  301. else
  302. {
  303. ep.m_pSoundName = (char*)STRING(m_NoiseMovingClosed);
  304. }
  305. ep.m_flVolume = 1;
  306. ep.m_SoundLevel = SNDLVL_NORM;
  307. EmitSound( filter, entindex(), ep );
  308. //Only loop sounds in HL1 to maintain HL2 behavior
  309. if( ShouldLoopMoveSound() )
  310. {
  311. float duration = enginesound->GetSoundDuration( ep.m_pSoundName );
  312. SetContextThink( &CBaseDoor::MovingSoundThink, gpGlobals->curtime + duration, "MovingSound" );
  313. }
  314. }
  315. void CBaseDoor::StartMovingSound( void )
  316. {
  317. MovingSoundThink();
  318. #ifdef CSTRIKE_DLL // this event is only used by CS:S bots
  319. CBasePlayer *player = ToBasePlayer(m_hActivator);
  320. IGameEvent * event = gameeventmanager->CreateEvent( "door_moving" );
  321. if( event )
  322. {
  323. event->SetInt( "entindex", entindex() );
  324. event->SetInt( "userid", (player)?player->GetUserID():0 );
  325. gameeventmanager->FireEvent( event );
  326. }
  327. #endif
  328. }
  329. void CBaseDoor::StopMovingSound(void)
  330. {
  331. SetContextThink( NULL, gpGlobals->curtime, "MovingSound" );
  332. char *pSoundName;
  333. if ( m_NoiseMovingClosed == NULL_STRING || m_toggle_state == TS_GOING_UP || m_toggle_state == TS_AT_TOP )
  334. {
  335. pSoundName = (char*)STRING(m_NoiseMoving);
  336. }
  337. else
  338. {
  339. pSoundName = (char*)STRING(m_NoiseMovingClosed);
  340. }
  341. StopSound( entindex(), CHAN_STATIC, pSoundName );
  342. }
  343. bool CBaseDoor::ShouldSavePhysics()
  344. {
  345. // don't save physics if you're func_water
  346. return !FClassnameIs( this, "func_water" );
  347. }
  348. //-----------------------------------------------------------------------------
  349. bool CBaseDoor::CreateVPhysics( )
  350. {
  351. if ( !FClassnameIs( this, "func_water" ) )
  352. {
  353. //normal door
  354. // NOTE: Create this even when the door is not solid to support constraints.
  355. VPhysicsInitShadow( false, false );
  356. }
  357. else
  358. {
  359. // special contents
  360. AddSolidFlags( FSOLID_VOLUME_CONTENTS );
  361. SETBITS( m_spawnflags, SF_DOOR_SILENT ); // water is silent for now
  362. IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false );
  363. fluidparams_t fluid;
  364. Assert( CollisionProp()->GetCollisionAngles() == vec3_angle );
  365. fluid.damping = 0.01f;
  366. fluid.surfacePlane[0] = 0;
  367. fluid.surfacePlane[1] = 0;
  368. fluid.surfacePlane[2] = 1;
  369. fluid.surfacePlane[3] = CollisionProp()->GetCollisionOrigin().z + CollisionProp()->OBBMaxs().z - 1;
  370. fluid.currentVelocity.Init(0,0,0);
  371. fluid.torqueFactor = 0.1f;
  372. fluid.viscosityFactor = 0.01f;
  373. fluid.pGameData = static_cast<void *>(this);
  374. //FIXME: Currently there's no way to specify that you want slime
  375. fluid.contents = CONTENTS_WATER;
  376. physenv->CreateFluidController( pPhysics, &fluid );
  377. }
  378. return true;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. //-----------------------------------------------------------------------------
  383. void CBaseDoor::Activate( void )
  384. {
  385. BaseClass::Activate();
  386. CBaseDoor *pDoorList[64];
  387. m_bDoorGroup = true;
  388. // force movement groups to sync!!!
  389. int doorCount = GetDoorMovementGroup( pDoorList, ARRAYSIZE(pDoorList) );
  390. for ( int i = 0; i < doorCount; i++ )
  391. {
  392. if ( pDoorList[i]->m_vecMoveDir == m_vecMoveDir )
  393. {
  394. bool error = false;
  395. if ( pDoorList[i]->IsRotatingDoor() )
  396. {
  397. error = ( pDoorList[i]->GetLocalAngles() != GetLocalAngles() ) ? true : false;
  398. }
  399. else
  400. {
  401. error = ( pDoorList[i]->GetLocalOrigin() != GetLocalOrigin() ) ? true : false;
  402. }
  403. if ( error )
  404. {
  405. // don't do group blocking
  406. m_bDoorGroup = false;
  407. #ifdef HL1_DLL
  408. // UNDONE: This should probably fixup m_vecPosition1 & m_vecPosition2
  409. Warning("Door group %s has misaligned origin!\n", STRING(GetEntityName()) );
  410. #endif
  411. }
  412. }
  413. }
  414. switch ( m_toggle_state )
  415. {
  416. case TS_AT_TOP:
  417. UpdateAreaPortals( true );
  418. break;
  419. case TS_AT_BOTTOM:
  420. UpdateAreaPortals( false );
  421. break;
  422. }
  423. #ifdef HL1_DLL
  424. // Get a handle to my filter entity if there is one
  425. if (m_iBlockFilterName != NULL_STRING)
  426. {
  427. m_hBlockFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iBlockFilterName, NULL ));
  428. }
  429. #endif
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose:
  433. // Input : state -
  434. //-----------------------------------------------------------------------------
  435. // This is ONLY used by the node graph to test movement through a door
  436. void CBaseDoor::InputSetToggleState( inputdata_t &inputdata )
  437. {
  438. SetToggleState( inputdata.value.Int() );
  439. }
  440. void CBaseDoor::SetToggleState( int state )
  441. {
  442. if ( state == TS_AT_TOP )
  443. UTIL_SetOrigin( this, m_vecPosition2 );
  444. else
  445. UTIL_SetOrigin( this, m_vecPosition1 );
  446. }
  447. //-----------------------------------------------------------------------------
  448. // Purpose:
  449. //-----------------------------------------------------------------------------
  450. void CBaseDoor::Precache( void )
  451. {
  452. //Fill in a default value if necessary
  453. if ( IsRotatingDoor() )
  454. {
  455. UTIL_ValidateSoundName( m_NoiseMoving, "RotDoorSound.DefaultMove" );
  456. UTIL_ValidateSoundName( m_NoiseArrived, "RotDoorSound.DefaultArrive" );
  457. UTIL_ValidateSoundName( m_ls.sLockedSound, "RotDoorSound.DefaultLocked" );
  458. UTIL_ValidateSoundName( m_ls.sUnlockedSound,"DoorSound.Null" );
  459. }
  460. else
  461. {
  462. UTIL_ValidateSoundName( m_NoiseMoving, "DoorSound.DefaultMove" );
  463. UTIL_ValidateSoundName( m_NoiseArrived, "DoorSound.DefaultArrive" );
  464. #ifndef HL1_DLL
  465. UTIL_ValidateSoundName( m_ls.sLockedSound, "DoorSound.DefaultLocked" );
  466. #endif
  467. UTIL_ValidateSoundName( m_ls.sUnlockedSound,"DoorSound.Null" );
  468. }
  469. #ifdef HL1_DLL
  470. if( m_ls.sLockedSound != NULL_STRING && strlen((char*)STRING(m_ls.sLockedSound)) < 4 )
  471. {
  472. // Too short to be ANYTHING ".wav", so it must be an old index into a long-lost
  473. // array of sound choices. slam it to a known "deny" sound. We lose the designer's
  474. // original selection, but we don't get unresponsive doors.
  475. m_ls.sLockedSound = AllocPooledString("buttons/button2.wav");
  476. }
  477. #endif//HL1_DLL
  478. //Precache them all
  479. PrecacheScriptSound( (char *) STRING(m_NoiseMoving) );
  480. PrecacheScriptSound( (char *) STRING(m_NoiseArrived) );
  481. PrecacheScriptSound( (char *) STRING(m_NoiseMovingClosed) );
  482. PrecacheScriptSound( (char *) STRING(m_NoiseArrivedClosed) );
  483. PrecacheScriptSound( (char *) STRING(m_ls.sLockedSound) );
  484. PrecacheScriptSound( (char *) STRING(m_ls.sUnlockedSound) );
  485. //Get sentence group names, for doors which are directly 'touched' to open
  486. switch (m_bLockedSentence)
  487. {
  488. case 1: m_ls.sLockedSentence = AllocPooledString("NA"); break; // access denied
  489. case 2: m_ls.sLockedSentence = AllocPooledString("ND"); break; // security lockout
  490. case 3: m_ls.sLockedSentence = AllocPooledString("NF"); break; // blast door
  491. case 4: m_ls.sLockedSentence = AllocPooledString("NFIRE"); break; // fire door
  492. case 5: m_ls.sLockedSentence = AllocPooledString("NCHEM"); break; // chemical door
  493. case 6: m_ls.sLockedSentence = AllocPooledString("NRAD"); break; // radiation door
  494. case 7: m_ls.sLockedSentence = AllocPooledString("NCON"); break; // gen containment
  495. case 8: m_ls.sLockedSentence = AllocPooledString("NH"); break; // maintenance door
  496. case 9: m_ls.sLockedSentence = AllocPooledString("NG"); break; // broken door
  497. default: m_ls.sLockedSentence = NULL_STRING; break;
  498. }
  499. switch (m_bUnlockedSentence)
  500. {
  501. case 1: m_ls.sUnlockedSentence = AllocPooledString("EA"); break; // access granted
  502. case 2: m_ls.sUnlockedSentence = AllocPooledString("ED"); break; // security door
  503. case 3: m_ls.sUnlockedSentence = AllocPooledString("EF"); break; // blast door
  504. case 4: m_ls.sUnlockedSentence = AllocPooledString("EFIRE"); break; // fire door
  505. case 5: m_ls.sUnlockedSentence = AllocPooledString("ECHEM"); break; // chemical door
  506. case 6: m_ls.sUnlockedSentence = AllocPooledString("ERAD"); break; // radiation door
  507. case 7: m_ls.sUnlockedSentence = AllocPooledString("ECON"); break; // gen containment
  508. case 8: m_ls.sUnlockedSentence = AllocPooledString("EH"); break; // maintenance door
  509. default: m_ls.sUnlockedSentence = NULL_STRING; break;
  510. }
  511. }
  512. //-----------------------------------------------------------------------------
  513. // Purpose: Doors not tied to anything (e.g. button, another door) can be touched,
  514. // to make them activate.
  515. // Input : *pOther -
  516. //-----------------------------------------------------------------------------
  517. void CBaseDoor::DoorTouch( CBaseEntity *pOther )
  518. {
  519. if( m_ChainTarget != NULL_STRING )
  520. ChainTouch( pOther );
  521. // Ignore touches by anything but players.
  522. if ( !pOther->IsPlayer() )
  523. {
  524. #ifdef HL1_DLL
  525. if( PassesBlockTouchFilter( pOther ) && m_toggle_state == TS_GOING_DOWN )
  526. {
  527. DoorGoUp();
  528. }
  529. #endif
  530. return;
  531. }
  532. // If door is not opened by touch, do nothing.
  533. if ( !HasSpawnFlags(SF_DOOR_PTOUCH) )
  534. {
  535. #ifdef HL1_DLL
  536. if( m_toggle_state == TS_AT_BOTTOM )
  537. {
  538. PlayLockSounds(this, &m_ls, TRUE, FALSE);
  539. }
  540. #endif//HL1_DLL
  541. return;
  542. }
  543. // If door has master, and it's not ready to trigger,
  544. // play 'locked' sound.
  545. if (m_sMaster != NULL_STRING && !UTIL_IsMasterTriggered(m_sMaster, pOther))
  546. {
  547. PlayLockSounds(this, &m_ls, TRUE, FALSE);
  548. }
  549. if (m_bLocked)
  550. {
  551. m_OnLockedUse.FireOutput( pOther, pOther );
  552. PlayLockSounds(this, &m_ls, TRUE, FALSE);
  553. return;
  554. }
  555. // Remember who activated the door.
  556. m_hActivator = pOther;
  557. if (DoorActivate( ))
  558. {
  559. // Temporarily disable the touch function, until movement is finished.
  560. SetTouch( NULL );
  561. }
  562. }
  563. #ifdef HL1_DLL
  564. bool CBaseDoor::PassesBlockTouchFilter(CBaseEntity *pOther)
  565. {
  566. CBaseFilter* pFilter = (CBaseFilter*)(m_hBlockFilter.Get());
  567. return ( pFilter && pFilter->PassesFilter( this, pOther ) );
  568. }
  569. #endif
  570. //-----------------------------------------------------------------------------
  571. // Purpose: Delays turning off area portals when closing doors to prevent visual artifacts
  572. //-----------------------------------------------------------------------------
  573. void CBaseDoor::CloseAreaPortalsThink( void )
  574. {
  575. UpdateAreaPortals( false );
  576. SetContextThink( NULL, gpGlobals->curtime, CLOSE_AREAPORTAL_THINK_CONTEXT );
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Purpose:
  580. // Input : isOpen -
  581. //-----------------------------------------------------------------------------
  582. void CBaseDoor::UpdateAreaPortals( bool isOpen )
  583. {
  584. // cancel pending close
  585. SetContextThink( NULL, gpGlobals->curtime, CLOSE_AREAPORTAL_THINK_CONTEXT );
  586. if ( IsRotatingDoor() && HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE) ) // logic inverted when using rot doors that start open
  587. isOpen = !isOpen;
  588. string_t name = GetEntityName();
  589. if ( !name )
  590. return;
  591. CBaseEntity *pPortal = NULL;
  592. while ( ( pPortal = gEntList.FindEntityByClassname( pPortal, "func_areaportal" ) ) != NULL )
  593. {
  594. if ( pPortal->HasTarget( name ) )
  595. {
  596. // USE_ON means open the portal, off means close it
  597. pPortal->Use( this, this, isOpen?USE_ON:USE_OFF, 0 );
  598. }
  599. }
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose: Called when the player uses the door.
  603. // Input : pActivator -
  604. // pCaller -
  605. // useType -
  606. // value -
  607. //-----------------------------------------------------------------------------
  608. void CBaseDoor::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  609. {
  610. m_hActivator = pActivator;
  611. if( m_ChainTarget != NULL_STRING )
  612. ChainUse();
  613. // We can't +use this if it can't be +used
  614. if ( m_hActivator != NULL && m_hActivator->IsPlayer() && HasSpawnFlags( SF_DOOR_PUSE ) == false )
  615. {
  616. PlayLockSounds( this, &m_ls, TRUE, FALSE );
  617. return;
  618. }
  619. bool bAllowUse = false;
  620. // if not ready to be used, ignore "use" command.
  621. if( HasSpawnFlags(SF_DOOR_NEW_USE_RULES) )
  622. {
  623. //New behavior:
  624. // If not ready to be used, ignore "use" command.
  625. // Allow use in these cases:
  626. // - when the door is closed/closing
  627. // - when the door is open/opening and can be manually closed
  628. 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 ) ) )
  629. bAllowUse = true;
  630. }
  631. else
  632. {
  633. // Legacy behavior:
  634. if (m_toggle_state == TS_AT_BOTTOM || (HasSpawnFlags(SF_DOOR_NO_AUTO_RETURN) && m_toggle_state == TS_AT_TOP) )
  635. bAllowUse = true;
  636. }
  637. if( bAllowUse )
  638. {
  639. if (m_bLocked)
  640. {
  641. m_OnLockedUse.FireOutput( pActivator, pCaller );
  642. PlayLockSounds(this, &m_ls, TRUE, FALSE);
  643. }
  644. else
  645. {
  646. DoorActivate();
  647. }
  648. }
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Purpose: Passes Use along to certain named doors.
  652. //-----------------------------------------------------------------------------
  653. void CBaseDoor::ChainUse( void )
  654. {
  655. if ( m_isChaining )
  656. return;
  657. CBaseEntity *ent = NULL;
  658. while ( ( ent = gEntList.FindEntityByName( ent, m_ChainTarget, NULL ) ) != NULL )
  659. {
  660. if ( ent == this )
  661. continue;
  662. CBaseDoor *door = dynamic_cast< CBaseDoor * >( ent );
  663. if ( door )
  664. {
  665. door->SetChaining( true );
  666. door->Use( m_hActivator, NULL, USE_TOGGLE, 0.0f ); // only the first param is used
  667. door->SetChaining( false );
  668. }
  669. }
  670. }
  671. //-----------------------------------------------------------------------------
  672. // Purpose: Passes Touch along to certain named doors.
  673. //-----------------------------------------------------------------------------
  674. void CBaseDoor::ChainTouch( CBaseEntity *pOther )
  675. {
  676. if ( m_isChaining )
  677. return;
  678. CBaseEntity *ent = NULL;
  679. while ( ( ent = gEntList.FindEntityByName( ent, m_ChainTarget, NULL ) ) != NULL )
  680. {
  681. if ( ent == this )
  682. continue;
  683. CBaseDoor *door = dynamic_cast< CBaseDoor * >( ent );
  684. if ( door )
  685. {
  686. door->SetChaining( true );
  687. door->Touch( pOther );
  688. door->SetChaining( false );
  689. }
  690. }
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Purpose: Closes the door if it is not already closed.
  694. //-----------------------------------------------------------------------------
  695. void CBaseDoor::InputClose( inputdata_t &inputdata )
  696. {
  697. if ( m_toggle_state != TS_AT_BOTTOM )
  698. {
  699. DoorGoDown();
  700. }
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Purpose: Input handler that locks the door.
  704. //-----------------------------------------------------------------------------
  705. void CBaseDoor::InputLock( inputdata_t &inputdata )
  706. {
  707. Lock();
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose: Opens the door if it is not already open.
  711. //-----------------------------------------------------------------------------
  712. void CBaseDoor::InputOpen( inputdata_t &inputdata )
  713. {
  714. if (m_toggle_state != TS_AT_TOP && m_toggle_state != TS_GOING_UP )
  715. {
  716. // I'm locked, can't open
  717. if (m_bLocked)
  718. return;
  719. // Play door unlock sounds.
  720. PlayLockSounds(this, &m_ls, false, false);
  721. DoorGoUp();
  722. }
  723. }
  724. //-----------------------------------------------------------------------------
  725. // Purpose: Opens the door if it is not already open.
  726. //-----------------------------------------------------------------------------
  727. void CBaseDoor::InputToggle( inputdata_t &inputdata )
  728. {
  729. // I'm locked, can't open
  730. if (m_bLocked)
  731. return;
  732. if (m_toggle_state == TS_AT_BOTTOM)
  733. {
  734. DoorGoUp();
  735. }
  736. else if (m_toggle_state == TS_AT_TOP)
  737. {
  738. DoorGoDown();
  739. }
  740. }
  741. //-----------------------------------------------------------------------------
  742. // Purpose: Input handler that unlocks the door.
  743. //-----------------------------------------------------------------------------
  744. void CBaseDoor::InputUnlock( inputdata_t &inputdata )
  745. {
  746. Unlock();
  747. }
  748. //-----------------------------------------------------------------------------
  749. // Purpose:
  750. //-----------------------------------------------------------------------------
  751. void CBaseDoor::InputSetSpeed( inputdata_t &inputdata )
  752. {
  753. m_flSpeed = inputdata.value.Float();
  754. }
  755. //-----------------------------------------------------------------------------
  756. // Purpose: Locks the door so that it cannot be opened.
  757. //-----------------------------------------------------------------------------
  758. void CBaseDoor::Lock( void )
  759. {
  760. m_bLocked = true;
  761. }
  762. //-----------------------------------------------------------------------------
  763. // Purpose: Unlocks the door so that it can be opened.
  764. //-----------------------------------------------------------------------------
  765. void CBaseDoor::Unlock( void )
  766. {
  767. m_bLocked = false;
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Purpose: Causes the door to "do its thing", i.e. start moving, and cascade activation.
  771. // Output : int
  772. //-----------------------------------------------------------------------------
  773. int CBaseDoor::DoorActivate( )
  774. {
  775. if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator))
  776. return 0;
  777. if (HasSpawnFlags(SF_DOOR_NO_AUTO_RETURN) && m_toggle_state == TS_AT_TOP)
  778. {
  779. // door should close
  780. DoorGoDown();
  781. }
  782. else
  783. {
  784. // door should open
  785. // play door unlock sounds
  786. PlayLockSounds(this, &m_ls, FALSE, FALSE);
  787. if ( m_toggle_state != TS_AT_TOP && m_toggle_state != TS_GOING_UP )
  788. {
  789. DoorGoUp();
  790. }
  791. }
  792. return 1;
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Purpose: Starts the door going to its "up" position (simply ToggleData->vecPosition2).
  796. //-----------------------------------------------------------------------------
  797. void CBaseDoor::DoorGoUp( void )
  798. {
  799. edict_t *pevActivator;
  800. UpdateAreaPortals( true );
  801. // It could be going-down, if blocked.
  802. ASSERT(m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN);
  803. // emit door moving and stop sounds on CHAN_STATIC so that the multicast doesn't
  804. // filter them out and leave a client stuck with looping door sounds!
  805. if ( !HasSpawnFlags(SF_DOOR_SILENT ) )
  806. {
  807. // If we're not moving already, start the moving noise
  808. if ( m_toggle_state != TS_GOING_UP && m_toggle_state != TS_GOING_DOWN )
  809. {
  810. StartMovingSound();
  811. }
  812. }
  813. m_toggle_state = TS_GOING_UP;
  814. SetMoveDone( &CBaseDoor::DoorHitTop );
  815. if ( IsRotatingDoor() ) // !!! BUGBUG Triggered doors don't work with this yet
  816. {
  817. float sign = 1.0;
  818. if ( m_hActivator != NULL )
  819. {
  820. pevActivator = m_hActivator->edict();
  821. if ( !HasSpawnFlags( SF_DOOR_ONEWAY ) && m_vecMoveAng.y ) // Y axis rotation, move away from the player
  822. {
  823. // Positive is CCW, negative is CW, so make 'sign' 1 or -1 based on which way we want to open.
  824. // Important note: All doors face East at all times, and twist their local angle to open.
  825. // So you can't look at the door's facing to determine which way to open.
  826. Vector nearestPoint;
  827. CollisionProp()->CalcNearestPoint( m_hActivator->GetAbsOrigin(), &nearestPoint );
  828. Vector activatorToNearestPoint = nearestPoint - m_hActivator->GetAbsOrigin();
  829. activatorToNearestPoint.z = 0;
  830. Vector activatorToOrigin = GetAbsOrigin() - m_hActivator->GetAbsOrigin();
  831. activatorToOrigin.z = 0;
  832. // Point right hand at door hinge, curl hand towards closest spot on door, if thumb
  833. // is up, open door CW. -- Department of Basic Cross Product Understanding for Noobs
  834. Vector cross = activatorToOrigin.Cross( activatorToNearestPoint );
  835. if( cross.z > 0.0f )
  836. {
  837. sign = -1.0f;
  838. }
  839. }
  840. }
  841. AngularMove(m_vecAngle2*sign, m_flSpeed);
  842. }
  843. else
  844. {
  845. LinearMove(m_vecPosition2, m_flSpeed);
  846. }
  847. //Fire our open ouput
  848. m_OnOpen.FireOutput( this, this );
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Purpose: The door has reached the "up" position. Either go back down, or
  852. // wait for another activation.
  853. //-----------------------------------------------------------------------------
  854. void CBaseDoor::DoorHitTop( void )
  855. {
  856. if ( !HasSpawnFlags( SF_DOOR_SILENT ) )
  857. {
  858. CPASAttenuationFilter filter( this );
  859. filter.MakeReliable();
  860. StopMovingSound();
  861. EmitSound_t ep;
  862. ep.m_nChannel = CHAN_STATIC;
  863. ep.m_pSoundName = (char*)STRING(m_NoiseArrived);
  864. ep.m_flVolume = 1;
  865. ep.m_SoundLevel = SNDLVL_NORM;
  866. EmitSound( filter, entindex(), ep );
  867. }
  868. ASSERT(m_toggle_state == TS_GOING_UP);
  869. m_toggle_state = TS_AT_TOP;
  870. // toggle-doors don't come down automatically, they wait for refire.
  871. if (HasSpawnFlags( SF_DOOR_NO_AUTO_RETURN))
  872. {
  873. // Re-instate touch method, movement is complete
  874. SetTouch( &CBaseDoor::DoorTouch );
  875. }
  876. else
  877. {
  878. // In flWait seconds, DoorGoDown will fire, unless wait is -1, then door stays open
  879. SetMoveDoneTime( m_flWait );
  880. SetMoveDone( &CBaseDoor::DoorGoDown );
  881. if ( m_flWait == -1 )
  882. {
  883. SetNextThink( TICK_NEVER_THINK );
  884. }
  885. }
  886. if (HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE) )
  887. {
  888. m_OnFullyClosed.FireOutput(this, this);
  889. }
  890. else
  891. {
  892. m_OnFullyOpen.FireOutput(this, this);
  893. }
  894. }
  895. //-----------------------------------------------------------------------------
  896. // Purpose: Starts the door going to its "down" position (simply ToggleData->vecPosition1).
  897. //-----------------------------------------------------------------------------
  898. void CBaseDoor::DoorGoDown( void )
  899. {
  900. if ( !HasSpawnFlags( SF_DOOR_SILENT ) )
  901. {
  902. // If we're not moving already, start the moving noise
  903. if ( m_toggle_state != TS_GOING_UP && m_toggle_state != TS_GOING_DOWN )
  904. {
  905. StartMovingSound();
  906. }
  907. }
  908. #ifdef DOOR_ASSERT
  909. ASSERT(m_toggle_state == TS_AT_TOP);
  910. #endif // DOOR_ASSERT
  911. m_toggle_state = TS_GOING_DOWN;
  912. SetMoveDone( &CBaseDoor::DoorHitBottom );
  913. if ( IsRotatingDoor() )//rotating door
  914. AngularMove( m_vecAngle1, m_flSpeed);
  915. else
  916. LinearMove( m_vecPosition1, m_flSpeed);
  917. //Fire our closed output
  918. m_OnClose.FireOutput( this, this );
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose: The door has reached the "down" position. Back to quiescence.
  922. //-----------------------------------------------------------------------------
  923. void CBaseDoor::DoorHitBottom( void )
  924. {
  925. if ( !HasSpawnFlags( SF_DOOR_SILENT ) )
  926. {
  927. CPASAttenuationFilter filter( this );
  928. filter.MakeReliable();
  929. StopMovingSound();
  930. EmitSound_t ep;
  931. ep.m_nChannel = CHAN_STATIC;
  932. if ( m_NoiseArrivedClosed == NULL_STRING )
  933. ep.m_pSoundName = (char*)STRING(m_NoiseArrived);
  934. else
  935. ep.m_pSoundName = (char*)STRING(m_NoiseArrivedClosed);
  936. ep.m_flVolume = 1;
  937. ep.m_SoundLevel = SNDLVL_NORM;
  938. EmitSound( filter, entindex(), ep );
  939. }
  940. ASSERT(m_toggle_state == TS_GOING_DOWN);
  941. m_toggle_state = TS_AT_BOTTOM;
  942. // Re-instate touch method, cycle is complete
  943. SetTouch( &CBaseDoor::DoorTouch );
  944. if (HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE))
  945. {
  946. m_OnFullyOpen.FireOutput(m_hActivator, this);
  947. }
  948. else
  949. {
  950. m_OnFullyClosed.FireOutput(m_hActivator, this);
  951. }
  952. // Close the area portals just after the door closes, to prevent visual artifacts in multiplayer games
  953. SetContextThink( &CBaseDoor::CloseAreaPortalsThink, gpGlobals->curtime + 0.5f, CLOSE_AREAPORTAL_THINK_CONTEXT );
  954. }
  955. // Lists all doors in the same movement group as this one
  956. int CBaseDoor::GetDoorMovementGroup( CBaseDoor *pDoorList[], int listMax )
  957. {
  958. int count = 0;
  959. CBaseEntity *pTarget = NULL;
  960. // Block all door pieces with the same targetname here.
  961. if ( GetEntityName() != NULL_STRING )
  962. {
  963. for (;;)
  964. {
  965. pTarget = gEntList.FindEntityByName( pTarget, GetEntityName(), NULL );
  966. if ( pTarget != this )
  967. {
  968. if ( !pTarget )
  969. break;
  970. CBaseDoor *pDoor = dynamic_cast<CBaseDoor *>(pTarget);
  971. if ( pDoor && count < listMax )
  972. {
  973. pDoorList[count] = pDoor;
  974. count++;
  975. }
  976. }
  977. }
  978. }
  979. return count;
  980. }
  981. //-----------------------------------------------------------------------------
  982. // Purpose: Called the first frame that the door is blocked while opening or closing.
  983. // Input : pOther - The blocking entity.
  984. //-----------------------------------------------------------------------------
  985. void CBaseDoor::StartBlocked( CBaseEntity *pOther )
  986. {
  987. //
  988. // Fire whatever events we need to due to our blocked state.
  989. //
  990. if (m_toggle_state == TS_GOING_DOWN)
  991. {
  992. m_OnBlockedClosing.FireOutput(pOther, this);
  993. }
  994. else
  995. {
  996. m_OnBlockedOpening.FireOutput(pOther, this);
  997. }
  998. }
  999. //-----------------------------------------------------------------------------
  1000. // Purpose: Called every frame when the door is blocked while opening or closing.
  1001. // Input : pOther - The blocking entity.
  1002. //-----------------------------------------------------------------------------
  1003. void CBaseDoor::Blocked( CBaseEntity *pOther )
  1004. {
  1005. // Hurt the blocker a little.
  1006. if ( m_flBlockDamage )
  1007. {
  1008. // if the door is marked "force closed" or it has a negative wait, then there's nothing to do but
  1009. // push/damage the object.
  1010. // If block damage is set, but this object is a physics prop that can't be damaged, just
  1011. // give up and disable collisions
  1012. if ( (m_bForceClosed || m_flWait < 0) && pOther->GetMoveType() == MOVETYPE_VPHYSICS &&
  1013. (pOther->m_takedamage == DAMAGE_NO || pOther->m_takedamage == DAMAGE_EVENTS_ONLY) )
  1014. {
  1015. EntityPhysics_CreateSolver( this, pOther, true, 4.0f );
  1016. }
  1017. else
  1018. {
  1019. pOther->TakeDamage( CTakeDamageInfo( this, this, m_flBlockDamage, DMG_CRUSH ) );
  1020. }
  1021. }
  1022. // If set, ignore non-player ents that block us. Mainly of use in multiplayer to prevent exploits.
  1023. else if ( pOther && !pOther->IsPlayer() && m_bIgnoreNonPlayerEntsOnBlock )
  1024. {
  1025. return;
  1026. }
  1027. // If we're set to force ourselves closed, keep going
  1028. if ( m_bForceClosed )
  1029. return;
  1030. // if a door has a negative wait, it would never come back if blocked,
  1031. // so let it just squash the object to death real fast
  1032. if (m_flWait >= 0)
  1033. {
  1034. if (m_toggle_state == TS_GOING_DOWN)
  1035. {
  1036. DoorGoUp();
  1037. }
  1038. else
  1039. {
  1040. DoorGoDown();
  1041. }
  1042. }
  1043. // Block all door pieces with the same targetname here.
  1044. if ( GetEntityName() != NULL_STRING )
  1045. {
  1046. CBaseDoor *pDoorList[64];
  1047. int doorCount = GetDoorMovementGroup( pDoorList, ARRAYSIZE(pDoorList) );
  1048. for ( int i = 0; i < doorCount; i++ )
  1049. {
  1050. CBaseDoor *pDoor = pDoorList[i];
  1051. if ( pDoor->m_flWait >= 0)
  1052. {
  1053. if (m_bDoorGroup && pDoor->m_vecMoveDir == m_vecMoveDir && pDoor->GetAbsVelocity() == GetAbsVelocity() && pDoor->GetLocalAngularVelocity() == GetLocalAngularVelocity())
  1054. {
  1055. pDoor->m_nSimulationTick = m_nSimulationTick; // don't run simulation this frame if you haven't run yet
  1056. // this is the most hacked, evil, bastardized thing I've ever seen. kjb
  1057. if ( !pDoor->IsRotatingDoor() )
  1058. {// set origin to realign normal doors
  1059. pDoor->SetLocalOrigin( GetLocalOrigin() );
  1060. pDoor->SetAbsVelocity( vec3_origin );// stop!
  1061. }
  1062. else
  1063. {// set angles to realign rotating doors
  1064. pDoor->SetLocalAngles( GetLocalAngles() );
  1065. pDoor->SetLocalAngularVelocity( vec3_angle );
  1066. }
  1067. }
  1068. if ( pDoor->m_toggle_state == TS_GOING_DOWN)
  1069. pDoor->DoorGoUp();
  1070. else
  1071. pDoor->DoorGoDown();
  1072. }
  1073. }
  1074. }
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. // Purpose: Called the first frame that the door is unblocked while opening or closing.
  1078. //-----------------------------------------------------------------------------
  1079. void CBaseDoor::EndBlocked( void )
  1080. {
  1081. //
  1082. // Fire whatever events we need to due to our unblocked state.
  1083. //
  1084. if (m_toggle_state == TS_GOING_DOWN)
  1085. {
  1086. m_OnUnblockedClosing.FireOutput(this, this);
  1087. }
  1088. else
  1089. {
  1090. m_OnUnblockedOpening.FireOutput(this, this);
  1091. }
  1092. }
  1093. /*func_door_rotating
  1094. TOGGLE causes the door to wait in both the start and end states for
  1095. a trigger event.
  1096. START_OPEN causes the door to move to its destination when spawned,
  1097. and operate in reverse. It is used to temporarily or permanently
  1098. close off an area when triggered (not usefull for touch or
  1099. takedamage doors).
  1100. You need to have an origin brush as part of this entity. The
  1101. center of that brush will be
  1102. the point around which it is rotated. It will rotate around the Z
  1103. axis by default. You can
  1104. check either the X_AXIS or Y_AXIS box to change that.
  1105. "distance" is how many degrees the door will be rotated.
  1106. "speed" determines how fast the door moves; default value is 100.
  1107. REVERSE will cause the door to rotate in the opposite direction.
  1108. "angle" determines the opening direction
  1109. "targetname" if set, no touch field will be spawned and a remote
  1110. button or trigger field activates the door.
  1111. "health" if set, door must be shot open
  1112. "speed" movement speed (100 default)
  1113. "wait" wait before returning (3 default, -1 = never return)
  1114. "dmg" damage to inflict when blocked (2 default)
  1115. */
  1116. //==================================================
  1117. // CRotDoor
  1118. //==================================================
  1119. class CRotDoor : public CBaseDoor
  1120. {
  1121. public:
  1122. DECLARE_CLASS( CRotDoor, CBaseDoor );
  1123. void Spawn( void );
  1124. bool CreateVPhysics();
  1125. // This is ONLY used by the node graph to test movement through a door
  1126. virtual void SetToggleState( int state );
  1127. virtual bool IsRotatingDoor() { return true; }
  1128. bool m_bSolidBsp;
  1129. DECLARE_DATADESC();
  1130. };
  1131. LINK_ENTITY_TO_CLASS( func_door_rotating, CRotDoor );
  1132. BEGIN_DATADESC( CRotDoor )
  1133. DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ),
  1134. END_DATADESC()
  1135. //-----------------------------------------------------------------------------
  1136. // Purpose:
  1137. //-----------------------------------------------------------------------------
  1138. void CRotDoor::Spawn( void )
  1139. {
  1140. BaseClass::Spawn();
  1141. // set the axis of rotation
  1142. CBaseToggle::AxisDir();
  1143. // check for clockwise rotation
  1144. if ( HasSpawnFlags(SF_DOOR_ROTATE_BACKWARDS) )
  1145. m_vecMoveAng = m_vecMoveAng * -1;
  1146. //m_flWait = 2; who the hell did this? (sjb)
  1147. m_vecAngle1 = GetLocalAngles();
  1148. m_vecAngle2 = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance;
  1149. ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating door start/end positions are equal\n");
  1150. // Starting open allows a func_door to be lighted in the closed position but
  1151. // spawn in the open position
  1152. //
  1153. // SF_DOOR_START_OPEN_OBSOLETE is an old broken way of spawning open that has
  1154. // been deprecated.
  1155. if ( HasSpawnFlags(SF_DOOR_START_OPEN_OBSOLETE) )
  1156. {
  1157. // swap pos1 and pos2, put door at pos2, invert movement direction
  1158. QAngle vecNewAngles = m_vecAngle2;
  1159. m_vecAngle2 = m_vecAngle1;
  1160. m_vecAngle1 = vecNewAngles;
  1161. m_vecMoveAng = -m_vecMoveAng;
  1162. // We've already had our physics setup in BaseClass::Spawn, so teleport to our
  1163. // current position. If we don't do this, our vphysics shadow will not update.
  1164. Teleport( NULL, &m_vecAngle1, NULL );
  1165. m_toggle_state = TS_AT_BOTTOM;
  1166. }
  1167. else if ( m_eSpawnPosition == FUNC_DOOR_SPAWN_OPEN )
  1168. {
  1169. // We've already had our physics setup in BaseClass::Spawn, so teleport to our
  1170. // current position. If we don't do this, our vphysics shadow will not update.
  1171. Teleport( NULL, &m_vecAngle2, NULL );
  1172. m_toggle_state = TS_AT_TOP;
  1173. }
  1174. else
  1175. {
  1176. m_toggle_state = TS_AT_BOTTOM;
  1177. }
  1178. #ifdef HL1_DLL
  1179. SetSolid( SOLID_VPHYSICS );
  1180. #endif
  1181. // Slam the object back to solid - if we really want it to be solid.
  1182. if ( m_bSolidBsp )
  1183. {
  1184. SetSolid( SOLID_BSP );
  1185. }
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. bool CRotDoor::CreateVPhysics()
  1189. {
  1190. if ( !IsSolidFlagSet( FSOLID_NOT_SOLID ) )
  1191. {
  1192. VPhysicsInitShadow( false, false );
  1193. }
  1194. return true;
  1195. }
  1196. //-----------------------------------------------------------------------------
  1197. // Purpose:
  1198. // Input : state -
  1199. //-----------------------------------------------------------------------------
  1200. // This is ONLY used by the node graph to test movement through a door
  1201. void CRotDoor::SetToggleState( int state )
  1202. {
  1203. if ( state == TS_AT_TOP )
  1204. SetLocalAngles( m_vecAngle2 );
  1205. else
  1206. SetLocalAngles( m_vecAngle1 );
  1207. }