Counter Strike : Global Offensive Source Code
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.

1395 lines
41 KiB

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