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.

1586 lines
43 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements buttons.
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "doors.h"
  8. #include "ndebugoverlay.h"
  9. #include "spark.h"
  10. #include "vstdlib/random.h"
  11. #include "engine/IEngineSound.h"
  12. #include "tier1/strtools.h"
  13. #include "buttons.h"
  14. #include "eventqueue.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. void PlayLockSounds( CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton );
  18. string_t MakeButtonSound( int sound ); // get string of button sound number
  19. #define SF_BUTTON_DONTMOVE 1
  20. #define SF_ROTBUTTON_NOTSOLID 1
  21. #define SF_BUTTON_TOGGLE 32 // button stays pushed until reactivated
  22. #define SF_BUTTON_TOUCH_ACTIVATES 256 // Button fires when touched.
  23. #define SF_BUTTON_DAMAGE_ACTIVATES 512 // Button fires when damaged.
  24. #define SF_BUTTON_USE_ACTIVATES 1024 // Button fires when used.
  25. #define SF_BUTTON_LOCKED 2048 // Whether the button is initially locked.
  26. #define SF_BUTTON_SPARK_IF_OFF 4096 // button sparks in OFF state
  27. #define SF_BUTTON_JIGGLE_ON_USE_LOCKED 8192 // whether to jiggle if someone uses us when we're locked
  28. BEGIN_DATADESC( CBaseButton )
  29. DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ),
  30. DEFINE_FIELD( m_fStayPushed, FIELD_BOOLEAN ),
  31. DEFINE_FIELD( m_fRotating, FIELD_BOOLEAN ),
  32. DEFINE_FIELD( m_bLockedSound, FIELD_CHARACTER ),
  33. DEFINE_FIELD( m_bLockedSentence, FIELD_CHARACTER ),
  34. DEFINE_FIELD( m_bUnlockedSound, FIELD_CHARACTER ),
  35. DEFINE_FIELD( m_bUnlockedSentence, FIELD_CHARACTER ),
  36. DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ),
  37. DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ),
  38. DEFINE_FIELD( m_flUseLockedTime, FIELD_TIME ),
  39. DEFINE_FIELD( m_bSolidBsp, FIELD_BOOLEAN ),
  40. DEFINE_KEYFIELD( m_sounds, FIELD_INTEGER, "sounds" ),
  41. // DEFINE_FIELD( m_ls, FIELD_SOUNDNAME ), // This is restored in Precache()
  42. // DEFINE_FIELD( m_nState, FIELD_INTEGER ),
  43. // Function Pointers
  44. DEFINE_FUNCTION( ButtonTouch ),
  45. DEFINE_FUNCTION( ButtonSpark ),
  46. DEFINE_FUNCTION( TriggerAndWait ),
  47. DEFINE_FUNCTION( ButtonReturn ),
  48. DEFINE_FUNCTION( ButtonBackHome ),
  49. DEFINE_FUNCTION( ButtonUse ),
  50. // Inputs
  51. DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
  52. DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
  53. DEFINE_INPUTFUNC( FIELD_VOID, "Press", InputPress ),
  54. DEFINE_INPUTFUNC( FIELD_VOID, "PressIn", InputPressIn ),
  55. DEFINE_INPUTFUNC( FIELD_VOID, "PressOut", InputPressOut ),
  56. // Outputs
  57. DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ),
  58. DEFINE_OUTPUT( m_OnPressed, "OnPressed" ),
  59. DEFINE_OUTPUT( m_OnUseLocked, "OnUseLocked" ),
  60. DEFINE_OUTPUT( m_OnIn, "OnIn" ),
  61. DEFINE_OUTPUT( m_OnOut, "OnOut" ),
  62. END_DATADESC()
  63. LINK_ENTITY_TO_CLASS( func_button, CBaseButton );
  64. void CBaseButton::Precache( void )
  65. {
  66. // get door button sounds, for doors which require buttons to open
  67. if (m_bLockedSound)
  68. {
  69. m_ls.sLockedSound = MakeButtonSound( (int)m_bLockedSound );
  70. PrecacheScriptSound(m_ls.sLockedSound.ToCStr());
  71. }
  72. if (m_bUnlockedSound)
  73. {
  74. m_ls.sUnlockedSound = MakeButtonSound( (int)m_bUnlockedSound );
  75. PrecacheScriptSound(m_ls.sUnlockedSound.ToCStr());
  76. }
  77. // get sentence group names, for doors which are directly 'touched' to open
  78. switch (m_bLockedSentence)
  79. {
  80. case 1: m_ls.sLockedSentence = MAKE_STRING("NA"); break; // access denied
  81. case 2: m_ls.sLockedSentence = MAKE_STRING("ND"); break; // security lockout
  82. case 3: m_ls.sLockedSentence = MAKE_STRING("NF"); break; // blast door
  83. case 4: m_ls.sLockedSentence = MAKE_STRING("NFIRE"); break; // fire door
  84. case 5: m_ls.sLockedSentence = MAKE_STRING("NCHEM"); break; // chemical door
  85. case 6: m_ls.sLockedSentence = MAKE_STRING("NRAD"); break; // radiation door
  86. case 7: m_ls.sLockedSentence = MAKE_STRING("NCON"); break; // gen containment
  87. case 8: m_ls.sLockedSentence = MAKE_STRING("NH"); break; // maintenance door
  88. case 9: m_ls.sLockedSentence = MAKE_STRING("NG"); break; // broken door
  89. default: m_ls.sLockedSentence = NULL_STRING; break;
  90. }
  91. switch (m_bUnlockedSentence)
  92. {
  93. case 1: m_ls.sUnlockedSentence = MAKE_STRING("EA"); break; // access granted
  94. case 2: m_ls.sUnlockedSentence = MAKE_STRING("ED"); break; // security door
  95. case 3: m_ls.sUnlockedSentence = MAKE_STRING("EF"); break; // blast door
  96. case 4: m_ls.sUnlockedSentence = MAKE_STRING("EFIRE"); break; // fire door
  97. case 5: m_ls.sUnlockedSentence = MAKE_STRING("ECHEM"); break; // chemical door
  98. case 6: m_ls.sUnlockedSentence = MAKE_STRING("ERAD"); break; // radiation door
  99. case 7: m_ls.sUnlockedSentence = MAKE_STRING("ECON"); break; // gen containment
  100. case 8: m_ls.sUnlockedSentence = MAKE_STRING("EH"); break; // maintenance door
  101. default: m_ls.sUnlockedSentence = NULL_STRING; break;
  102. }
  103. if ( m_sNoise != NULL_STRING )
  104. {
  105. PrecacheScriptSound( STRING( m_sNoise ) );
  106. }
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: Cache user-entity-field values until spawn is called.
  110. // Input : szKeyName -
  111. // szValue -
  112. // Output : Returns true if handled, false if not.
  113. //-----------------------------------------------------------------------------
  114. bool CBaseButton::KeyValue( const char *szKeyName, const char *szValue )
  115. {
  116. if (FStrEq(szKeyName, "locked_sound"))
  117. {
  118. m_bLockedSound = atof(szValue);
  119. }
  120. else if (FStrEq(szKeyName, "locked_sentence"))
  121. {
  122. m_bLockedSentence = atof(szValue);
  123. }
  124. else if (FStrEq(szKeyName, "unlocked_sound"))
  125. {
  126. m_bUnlockedSound = atof(szValue);
  127. }
  128. else if (FStrEq(szKeyName, "unlocked_sentence"))
  129. {
  130. m_bUnlockedSentence = atof(szValue);
  131. }
  132. else
  133. {
  134. return BaseClass::KeyValue( szKeyName, szValue );
  135. }
  136. return true;
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose: Locks the button. If locked, the button will play the locked sound
  140. // when the player tries to use it.
  141. //-----------------------------------------------------------------------------
  142. void CBaseButton::Lock()
  143. {
  144. m_bLocked = true;
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose: Unlocks the button, making it able to be pressed again.
  148. //-----------------------------------------------------------------------------
  149. void CBaseButton::Unlock()
  150. {
  151. m_bLocked = false;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose: Locks the button. If locked, the button will play the locked sound
  155. // when the player tries to use it.
  156. //-----------------------------------------------------------------------------
  157. void CBaseButton::InputLock( inputdata_t &inputdata )
  158. {
  159. Lock();
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose: Unlocks the button, making it able to be pressed again.
  163. //-----------------------------------------------------------------------------
  164. void CBaseButton::InputUnlock( inputdata_t &inputdata )
  165. {
  166. Unlock();
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Presses or unpresses the button.
  170. //-----------------------------------------------------------------------------
  171. void CBaseButton::Press( CBaseEntity *pActivator, BUTTON_CODE eCode )
  172. {
  173. if ( ( eCode == BUTTON_PRESS ) && ( m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) )
  174. {
  175. return;
  176. }
  177. if ( ( eCode == BUTTON_ACTIVATE ) && ( m_toggle_state == TS_GOING_UP || m_toggle_state == TS_AT_TOP ) )
  178. {
  179. return;
  180. }
  181. if ( ( eCode == BUTTON_RETURN ) && ( m_toggle_state == TS_GOING_DOWN || m_toggle_state == TS_AT_BOTTOM ) )
  182. {
  183. return;
  184. }
  185. // FIXME: consolidate all the button press code into one place!
  186. if (m_bLocked)
  187. {
  188. // play button locked sound
  189. PlayLockSounds(this, &m_ls, TRUE, TRUE);
  190. return;
  191. }
  192. // Temporarily disable the touch function, until movement is finished.
  193. SetTouch( NULL );
  194. if ( ( ( eCode == BUTTON_PRESS ) && ( m_toggle_state == TS_AT_TOP ) ) ||
  195. ( ( eCode == BUTTON_RETURN ) && ( m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP ) ) )
  196. {
  197. if ( m_sNoise != NULL_STRING )
  198. {
  199. CPASAttenuationFilter filter( this );
  200. EmitSound_t ep;
  201. ep.m_nChannel = CHAN_VOICE;
  202. ep.m_pSoundName = (char*)STRING(m_sNoise);
  203. ep.m_flVolume = 1;
  204. ep.m_SoundLevel = SNDLVL_NORM;
  205. EmitSound( filter, entindex(), ep );
  206. }
  207. m_OnPressed.FireOutput(pActivator, this);
  208. ButtonReturn();
  209. }
  210. else if ( ( eCode == BUTTON_PRESS ) ||
  211. ( ( eCode == BUTTON_ACTIVATE ) && ( m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN ) ) )
  212. {
  213. m_OnPressed.FireOutput(pActivator, this);
  214. ButtonActivate();
  215. }
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Presses the button.
  219. //-----------------------------------------------------------------------------
  220. void CBaseButton::InputPress( inputdata_t &inputdata )
  221. {
  222. Press( inputdata.pActivator, BUTTON_PRESS );
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Presses the button, sending it to the top/pressed position.
  226. //-----------------------------------------------------------------------------
  227. void CBaseButton::InputPressIn( inputdata_t &inputdata )
  228. {
  229. Press( inputdata.pActivator, BUTTON_ACTIVATE );
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Unpresses the button, sending it to the unpressed/bottom position.
  233. //-----------------------------------------------------------------------------
  234. void CBaseButton::InputPressOut( inputdata_t &inputdata )
  235. {
  236. Press( inputdata.pActivator, BUTTON_RETURN );
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose: We have been damaged. Possibly activate, depending on our flags.
  240. // Input : pInflictor -
  241. // pAttacker -
  242. // flDamage -
  243. // bitsDamageType -
  244. // Output :
  245. //-----------------------------------------------------------------------------
  246. int CBaseButton::OnTakeDamage( const CTakeDamageInfo &info )
  247. {
  248. m_OnDamaged.FireOutput(m_hActivator, this);
  249. // dvsents2: remove obselete health keyvalue from func_button
  250. if (!HasSpawnFlags(SF_BUTTON_DAMAGE_ACTIVATES) && (m_iHealth == 0))
  251. {
  252. return(0);
  253. }
  254. BUTTON_CODE code = ButtonResponseToTouch();
  255. if ( code == BUTTON_NOTHING )
  256. return 0;
  257. m_hActivator = info.GetAttacker();
  258. // dvsents2: why would activator be NULL here?
  259. if ( m_hActivator == NULL )
  260. return 0;
  261. if (m_bLocked)
  262. {
  263. return(0);
  264. }
  265. // Temporarily disable the touch function, until movement is finished.
  266. SetTouch( NULL );
  267. if ( code == BUTTON_RETURN )
  268. {
  269. if ( m_sNoise != NULL_STRING )
  270. {
  271. CPASAttenuationFilter filter( this );
  272. EmitSound_t ep;
  273. ep.m_nChannel = CHAN_VOICE;
  274. ep.m_pSoundName = (char*)STRING(m_sNoise);
  275. ep.m_flVolume = 1;
  276. ep.m_SoundLevel = SNDLVL_NORM;
  277. EmitSound( filter, entindex(), ep );
  278. }
  279. m_OnPressed.FireOutput(m_hActivator, this);
  280. ButtonReturn();
  281. }
  282. else
  283. {
  284. // code == BUTTON_ACTIVATE
  285. m_OnPressed.FireOutput(m_hActivator, this);
  286. ButtonActivate( );
  287. }
  288. return 0;
  289. }
  290. void CBaseButton::Spawn( )
  291. {
  292. //----------------------------------------------------
  293. //determine sounds for buttons
  294. //a sound of 0 should not make a sound
  295. //----------------------------------------------------
  296. if ( m_sounds )
  297. {
  298. m_sNoise = MakeButtonSound( m_sounds );
  299. PrecacheScriptSound(m_sNoise.ToCStr());
  300. }
  301. else
  302. {
  303. m_sNoise = NULL_STRING;
  304. }
  305. Precache();
  306. if ( HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state
  307. {
  308. SetThink ( &CBaseButton::ButtonSpark );
  309. SetNextThink( gpGlobals->curtime + 0.5f );// no hurry, make sure everything else spawns
  310. }
  311. // Convert movedir from angles to a vector
  312. QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z );
  313. AngleVectors( angMoveDir, &m_vecMoveDir );
  314. SetMoveType( MOVETYPE_PUSH );
  315. SetSolid( SOLID_BSP );
  316. SetModel( STRING( GetModelName() ) );
  317. if (m_flSpeed == 0)
  318. {
  319. m_flSpeed = 40;
  320. }
  321. m_takedamage = DAMAGE_YES;
  322. if (m_flWait == 0)
  323. {
  324. m_flWait = 1;
  325. }
  326. if (m_flLip == 0)
  327. {
  328. m_flLip = 4;
  329. }
  330. m_toggle_state = TS_AT_BOTTOM;
  331. m_vecPosition1 = GetLocalOrigin();
  332. // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
  333. Vector vecButtonOBB = CollisionProp()->OBBSize();
  334. vecButtonOBB -= Vector( 2, 2, 2 );
  335. m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * (DotProductAbs( m_vecMoveDir, vecButtonOBB ) - m_flLip));
  336. // Is this a non-moving button?
  337. if ( ((m_vecPosition2 - m_vecPosition1).Length() < 1) || HasSpawnFlags(SF_BUTTON_DONTMOVE) )
  338. {
  339. m_vecPosition2 = m_vecPosition1;
  340. }
  341. m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE);
  342. m_fRotating = FALSE;
  343. if (HasSpawnFlags(SF_BUTTON_LOCKED))
  344. {
  345. m_bLocked = true;
  346. }
  347. //
  348. // If using activates the button, set its use function.
  349. //
  350. if (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES))
  351. {
  352. SetUse(&CBaseButton::ButtonUse);
  353. }
  354. else
  355. {
  356. SetUse(NULL);
  357. }
  358. //
  359. // If touching activates the button, set its touch function.
  360. //
  361. if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES))
  362. {
  363. SetTouch( &CBaseButton::ButtonTouch );
  364. }
  365. else
  366. {
  367. SetTouch ( NULL );
  368. }
  369. CreateVPhysics();
  370. }
  371. //-----------------------------------------------------------------------------
  372. bool CBaseButton::CreateVPhysics()
  373. {
  374. VPhysicsInitShadow( false, false );
  375. return true;
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Purpose: Button sound table.
  379. // Also used by CBaseDoor to get 'touched' door lock/unlock sounds
  380. // Input : sound - index of sound to look up.
  381. // Output : Returns a pointer to the corresponding sound file.
  382. //-----------------------------------------------------------------------------
  383. string_t MakeButtonSound( int sound )
  384. {
  385. char tmp[1024];
  386. Q_snprintf( tmp, sizeof(tmp), "Buttons.snd%d", sound );
  387. return AllocPooledString(tmp);
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: Think function that emits sparks at random intervals.
  391. //-----------------------------------------------------------------------------
  392. void CBaseButton::ButtonSpark ( void )
  393. {
  394. SetThink ( &CBaseButton::ButtonSpark );
  395. SetNextThink( gpGlobals->curtime + 0.1 + random->RandomFloat ( 0, 1.5 ) );// spark again at random interval
  396. DoSpark( this, WorldSpaceCenter(), 1, 1, true, vec3_origin );
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose: Called when someone uses us whilst we are locked.
  400. //-----------------------------------------------------------------------------
  401. bool CBaseButton::OnUseLocked( CBaseEntity *pActivator )
  402. {
  403. PlayLockSounds(this, &m_ls, TRUE, TRUE);
  404. if ( gpGlobals->curtime > m_flUseLockedTime )
  405. {
  406. m_OnUseLocked.FireOutput( pActivator, this );
  407. m_flUseLockedTime = gpGlobals->curtime + 0.5;
  408. return true;
  409. }
  410. return false;
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose: Use function that starts the button moving.
  414. // Input : pActivator -
  415. // pCaller -
  416. // useType -
  417. // value -
  418. //-----------------------------------------------------------------------------
  419. void CBaseButton::ButtonUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  420. {
  421. // Ignore touches if button is moving, or pushed-in and waiting to auto-come-out.
  422. // UNDONE: Should this use ButtonResponseToTouch() too?
  423. if (m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN )
  424. return;
  425. if (m_bLocked)
  426. {
  427. OnUseLocked( pActivator );
  428. return;
  429. }
  430. m_hActivator = pActivator;
  431. if ( m_toggle_state == TS_AT_TOP)
  432. {
  433. //
  434. // If it's a toggle button it can return now. Otherwise, it will either
  435. // return on its own or will stay pressed indefinitely.
  436. //
  437. if ( HasSpawnFlags(SF_BUTTON_TOGGLE))
  438. {
  439. if ( m_sNoise != NULL_STRING )
  440. {
  441. CPASAttenuationFilter filter( this );
  442. EmitSound_t ep;
  443. ep.m_nChannel = CHAN_VOICE;
  444. ep.m_pSoundName = (char*)STRING(m_sNoise);
  445. ep.m_flVolume = 1;
  446. ep.m_SoundLevel = SNDLVL_NORM;
  447. EmitSound( filter, entindex(), ep );
  448. }
  449. m_OnPressed.FireOutput(m_hActivator, this);
  450. ButtonReturn();
  451. }
  452. }
  453. else
  454. {
  455. m_OnPressed.FireOutput(m_hActivator, this);
  456. ButtonActivate( );
  457. }
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose: Returns a code indicating how the button should respond to being touched.
  461. // Output : Returns one of the following:
  462. // BUTTON_NOTHING - do nothing
  463. // BUTTON_RETURN -
  464. // BUTTON_ACTIVATE - act as if pressed
  465. //-----------------------------------------------------------------------------
  466. CBaseButton::BUTTON_CODE CBaseButton::ButtonResponseToTouch( void )
  467. {
  468. // Ignore touches if button is moving, or pushed-in and waiting to auto-come-out.
  469. if (m_toggle_state == TS_GOING_UP ||
  470. m_toggle_state == TS_GOING_DOWN ||
  471. (m_toggle_state == TS_AT_TOP && !m_fStayPushed && !HasSpawnFlags(SF_BUTTON_TOGGLE) ) )
  472. return BUTTON_NOTHING;
  473. if (m_toggle_state == TS_AT_TOP)
  474. {
  475. if ( HasSpawnFlags(SF_BUTTON_TOGGLE) && !m_fStayPushed)
  476. {
  477. return BUTTON_RETURN;
  478. }
  479. }
  480. else
  481. return BUTTON_ACTIVATE;
  482. return BUTTON_NOTHING;
  483. }
  484. //-----------------------------------------------------------------------------
  485. // Purpose: Touch function that activates the button if it responds to touch.
  486. // Input : pOther - The entity that touched us.
  487. //-----------------------------------------------------------------------------
  488. void CBaseButton::ButtonTouch( CBaseEntity *pOther )
  489. {
  490. // Ignore touches by anything but players
  491. if ( !pOther->IsPlayer() )
  492. return;
  493. m_hActivator = pOther;
  494. BUTTON_CODE code = ButtonResponseToTouch();
  495. if ( code == BUTTON_NOTHING )
  496. return;
  497. if (!UTIL_IsMasterTriggered(m_sMaster, pOther) || m_bLocked)
  498. {
  499. // play button locked sound
  500. PlayLockSounds(this, &m_ls, TRUE, TRUE);
  501. return;
  502. }
  503. // Temporarily disable the touch function, until movement is finished.
  504. SetTouch( NULL );
  505. if ( code == BUTTON_RETURN )
  506. {
  507. if ( m_sNoise != NULL_STRING )
  508. {
  509. CPASAttenuationFilter filter( this );
  510. EmitSound_t ep;
  511. ep.m_nChannel = CHAN_VOICE;
  512. ep.m_pSoundName = (char*)STRING(m_sNoise);
  513. ep.m_flVolume = 1;
  514. ep.m_SoundLevel = SNDLVL_NORM;
  515. EmitSound( filter, entindex(), ep );
  516. }
  517. m_OnPressed.FireOutput(m_hActivator, this);
  518. ButtonReturn();
  519. }
  520. else
  521. {
  522. // code == BUTTON_ACTIVATE
  523. m_OnPressed.FireOutput(m_hActivator, this);
  524. ButtonActivate( );
  525. }
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose: Starts the button moving "in/up".
  529. // Input : *pOther -
  530. //-----------------------------------------------------------------------------
  531. void CBaseButton::ButtonActivate( void )
  532. {
  533. if ( m_sNoise != NULL_STRING )
  534. {
  535. CPASAttenuationFilter filter( this );
  536. EmitSound_t ep;
  537. ep.m_nChannel = CHAN_VOICE;
  538. ep.m_pSoundName = (char*)STRING(m_sNoise);
  539. ep.m_flVolume = 1;
  540. ep.m_SoundLevel = SNDLVL_NORM;
  541. EmitSound( filter, entindex(), ep );
  542. }
  543. if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked)
  544. {
  545. // button is locked, play locked sound
  546. PlayLockSounds(this, &m_ls, TRUE, TRUE);
  547. return;
  548. }
  549. else
  550. {
  551. // button is unlocked, play unlocked sound
  552. PlayLockSounds(this, &m_ls, FALSE, TRUE);
  553. }
  554. ASSERT(m_toggle_state == TS_AT_BOTTOM);
  555. m_toggle_state = TS_GOING_UP;
  556. SetMoveDone( &CBaseButton::TriggerAndWait );
  557. if (!m_fRotating)
  558. LinearMove( m_vecPosition2, m_flSpeed);
  559. else
  560. AngularMove( m_vecAngle2, m_flSpeed);
  561. }
  562. //-----------------------------------------------------------------------------
  563. // Purpose: Enables or disables the use capability based on our spawnflags.
  564. //-----------------------------------------------------------------------------
  565. int CBaseButton::ObjectCaps(void)
  566. {
  567. return((BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) |
  568. (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES) ? (FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS) : 0));
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Purpose: Button has reached the "pressed/top" position. Fire its OnIn output,
  572. // and pause before returning to "unpressed/bottom".
  573. //-----------------------------------------------------------------------------
  574. void CBaseButton::TriggerAndWait( void )
  575. {
  576. ASSERT(m_toggle_state == TS_GOING_UP);
  577. if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked)
  578. {
  579. return;
  580. }
  581. m_toggle_state = TS_AT_TOP;
  582. //
  583. // Re-instate touches if the button is of the toggle variety.
  584. //
  585. if (m_fStayPushed || HasSpawnFlags(SF_BUTTON_TOGGLE ) )
  586. {
  587. if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES))
  588. {
  589. SetTouch(&CBaseButton::ButtonTouch);
  590. }
  591. else
  592. {
  593. // BUGBUG: ALL buttons no longer respond to touch
  594. SetTouch (NULL);
  595. }
  596. }
  597. //
  598. // If button automatically comes back out, start it moving out.
  599. //
  600. else
  601. {
  602. SetNextThink( gpGlobals->curtime + m_flWait );
  603. SetThink( &CBaseButton::ButtonReturn );
  604. }
  605. m_nState = 1; // use alternate textures
  606. m_OnIn.FireOutput(m_hActivator, this);
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: Starts the button moving "out/down".
  610. //-----------------------------------------------------------------------------
  611. void CBaseButton::ButtonReturn( void )
  612. {
  613. ASSERT(m_toggle_state == TS_AT_TOP);
  614. m_toggle_state = TS_GOING_DOWN;
  615. SetMoveDone( &CBaseButton::ButtonBackHome );
  616. if (!m_fRotating)
  617. LinearMove( m_vecPosition1, m_flSpeed);
  618. else
  619. AngularMove( m_vecAngle1, m_flSpeed);
  620. m_nState = 0; // use normal textures
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Purpose: Button has returned to the "unpressed/bottom" position. Fire its
  624. // OnOut output and stop moving.
  625. //-----------------------------------------------------------------------------
  626. void CBaseButton::ButtonBackHome( void )
  627. {
  628. ASSERT(m_toggle_state == TS_GOING_DOWN);
  629. m_toggle_state = TS_AT_BOTTOM;
  630. m_OnOut.FireOutput(m_hActivator, this);
  631. //
  632. // Re-instate touch method, movement cycle is complete.
  633. //
  634. if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES))
  635. {
  636. SetTouch( &CBaseButton::ButtonTouch );
  637. }
  638. else
  639. {
  640. // BUGBUG: ALL buttons no longer respond to touch
  641. SetTouch ( NULL );
  642. }
  643. // reset think for a sparking button
  644. if (HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) )
  645. {
  646. SetThink ( &CBaseButton::ButtonSpark );
  647. SetNextThink( gpGlobals->curtime + 0.5f );// no hurry
  648. }
  649. }
  650. //-----------------------------------------------------------------------------
  651. //-----------------------------------------------------------------------------
  652. int CBaseButton::DrawDebugTextOverlays()
  653. {
  654. int text_offset = BaseClass::DrawDebugTextOverlays();
  655. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  656. {
  657. static const char *pszStates[] =
  658. {
  659. "Pressed",
  660. "Unpressed",
  661. "Pressing...",
  662. "Unpressing...",
  663. "<UNKNOWN STATE>",
  664. };
  665. char tempstr[255];
  666. int nState = m_toggle_state;
  667. if ( ( nState < 0 ) || ( nState > 3 ) )
  668. {
  669. nState = 4;
  670. }
  671. Q_snprintf( tempstr, sizeof(tempstr), "State: %s", pszStates[nState] );
  672. EntityText( text_offset, tempstr, 0 );
  673. text_offset++;
  674. Q_snprintf( tempstr, sizeof(tempstr), "%s", m_bLocked ? "Locked" : "Unlocked" );
  675. EntityText( text_offset, tempstr, 0 );
  676. text_offset++;
  677. }
  678. return text_offset;
  679. }
  680. //
  681. // Rotating button (aka "lever")
  682. //
  683. LINK_ENTITY_TO_CLASS( func_rot_button, CRotButton );
  684. void CRotButton::Spawn( void )
  685. {
  686. //----------------------------------------------------
  687. //determine sounds for buttons
  688. //a sound of 0 should not make a sound
  689. //----------------------------------------------------
  690. if ( m_sounds )
  691. {
  692. m_sNoise = MakeButtonSound( m_sounds );
  693. PrecacheScriptSound(m_sNoise.ToCStr());
  694. }
  695. else
  696. {
  697. m_sNoise = NULL_STRING;
  698. }
  699. // set the axis of rotation
  700. CBaseToggle::AxisDir();
  701. // check for clockwise rotation
  702. if ( HasSpawnFlags( SF_DOOR_ROTATE_BACKWARDS) )
  703. {
  704. m_vecMoveAng = m_vecMoveAng * -1;
  705. }
  706. SetMoveType( MOVETYPE_PUSH );
  707. #ifdef HL1_DLL
  708. SetSolid( SOLID_BSP );
  709. #else
  710. SetSolid( SOLID_VPHYSICS );
  711. #endif
  712. if ( HasSpawnFlags( SF_ROTBUTTON_NOTSOLID ) )
  713. {
  714. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  715. AddSolidFlags( FSOLID_NOT_SOLID );
  716. }
  717. SetModel( STRING( GetModelName() ) );
  718. if (m_flSpeed == 0)
  719. m_flSpeed = 40;
  720. if (m_flWait == 0)
  721. m_flWait = 1;
  722. if (m_iHealth > 0)
  723. {
  724. m_takedamage = DAMAGE_YES;
  725. }
  726. m_toggle_state = TS_AT_BOTTOM;
  727. m_vecAngle1 = GetLocalAngles();
  728. m_vecAngle2 = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance;
  729. ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating button start/end positions are equal\n");
  730. m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE);
  731. m_fRotating = TRUE;
  732. SetUse(&CRotButton::ButtonUse);
  733. //
  734. // If touching activates the button, set its touch function.
  735. //
  736. if (!HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES))
  737. {
  738. SetTouch ( NULL );
  739. }
  740. else
  741. {
  742. SetTouch( &CRotButton::ButtonTouch );
  743. }
  744. CreateVPhysics();
  745. }
  746. bool CRotButton::CreateVPhysics( void )
  747. {
  748. VPhysicsInitShadow( false, false );
  749. return true;
  750. }
  751. //-----------------------------------------------------------------------------
  752. // CMomentaryRotButton spawnflags
  753. //-----------------------------------------------------------------------------
  754. #define SF_MOMENTARY_DOOR 1
  755. #define SF_MOMENTARY_NOT_USABLE 2
  756. #define SF_MOMENTARY_AUTO_RETURN 16
  757. BEGIN_DATADESC( CMomentaryRotButton )
  758. DEFINE_FIELD( m_lastUsed, FIELD_INTEGER ),
  759. DEFINE_FIELD( m_start, FIELD_VECTOR ),
  760. DEFINE_FIELD( m_end, FIELD_VECTOR ),
  761. DEFINE_FIELD( m_IdealYaw, FIELD_FLOAT ),
  762. DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ),
  763. DEFINE_FIELD( m_bUpdateTarget, FIELD_BOOLEAN ),
  764. DEFINE_KEYFIELD( m_direction, FIELD_INTEGER, "StartDirection" ),
  765. DEFINE_KEYFIELD( m_returnSpeed, FIELD_FLOAT, "returnspeed" ),
  766. DEFINE_KEYFIELD( m_flStartPosition, FIELD_FLOAT, "StartPosition"),
  767. DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ),
  768. // Function Pointers
  769. DEFINE_FUNCTION( UseMoveDone ),
  770. DEFINE_FUNCTION( ReturnMoveDone ),
  771. DEFINE_FUNCTION( SetPositionMoveDone ),
  772. DEFINE_FUNCTION( UpdateThink ),
  773. // Inputs
  774. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPosition", InputSetPosition ),
  775. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPositionImmediately", InputSetPositionImmediately ),
  776. DEFINE_INPUTFUNC( FIELD_VOID, "_DisableUpdateTarget", InputDisableUpdateTarget ),
  777. DEFINE_INPUTFUNC( FIELD_VOID, "_EnableUpdateTarget", InputEnableUpdateTarget ),
  778. // Outputs
  779. DEFINE_OUTPUT( m_Position, "Position" ),
  780. DEFINE_OUTPUT( m_OnUnpressed, "OnUnpressed" ),
  781. DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ),
  782. DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ),
  783. DEFINE_OUTPUT( m_OnReachedPosition, "OnReachedPosition" ),
  784. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  785. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  786. DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN )
  787. END_DATADESC()
  788. LINK_ENTITY_TO_CLASS( momentary_rot_button, CMomentaryRotButton );
  789. //-----------------------------------------------------------------------------
  790. // Purpose: Called when spawning, after keyvalues have been handled.
  791. //-----------------------------------------------------------------------------
  792. void CMomentaryRotButton::Spawn( void )
  793. {
  794. CBaseToggle::AxisDir();
  795. m_bUpdateTarget = true;
  796. if ( m_flSpeed == 0 )
  797. {
  798. m_flSpeed = 100;
  799. }
  800. // Clamp start position and issue bounds warning
  801. if (m_flStartPosition < 0.0f || m_flStartPosition > 1.0f)
  802. {
  803. Warning("WARNING: Momentary door (%s) start position not between 0 and 1. Clamping.\n",GetDebugName());
  804. m_flStartPosition = clamp(m_IdealYaw, 0.f, 1.f);
  805. }
  806. // Check direction fields (for backward compatibility)
  807. if (m_direction != 1 && m_direction != -1)
  808. {
  809. m_direction = 1;
  810. }
  811. if (m_flMoveDistance < 0)
  812. {
  813. m_vecMoveAng = m_vecMoveAng * -1;
  814. m_flMoveDistance = -m_flMoveDistance;
  815. }
  816. m_start = GetLocalAngles() - m_vecMoveAng * m_flMoveDistance * m_flStartPosition;
  817. m_end = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance * (1-m_flStartPosition);
  818. m_IdealYaw = m_flStartPosition;
  819. // Force start direction at end points
  820. if (m_flStartPosition == 0.0)
  821. {
  822. m_direction = -1;
  823. }
  824. else if (m_flStartPosition == 1.0)
  825. {
  826. m_direction = 1;
  827. }
  828. if (HasSpawnFlags(SF_BUTTON_LOCKED))
  829. {
  830. m_bLocked = true;
  831. }
  832. if ( HasSpawnFlags( SF_BUTTON_USE_ACTIVATES ) )
  833. {
  834. if ( m_sounds )
  835. {
  836. m_sNoise = MakeButtonSound( m_sounds );
  837. PrecacheScriptSound(m_sNoise.ToCStr());
  838. }
  839. else
  840. {
  841. m_sNoise = NULL_STRING;
  842. }
  843. m_lastUsed = 0;
  844. UpdateTarget(0,this);
  845. }
  846. #ifdef HL1_DLL
  847. SetSolid( SOLID_BSP );
  848. #else
  849. SetSolid( SOLID_VPHYSICS );
  850. #endif
  851. if (HasSpawnFlags(SF_ROTBUTTON_NOTSOLID))
  852. {
  853. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  854. AddSolidFlags( FSOLID_NOT_SOLID );
  855. }
  856. SetMoveType( MOVETYPE_PUSH );
  857. SetModel( STRING( GetModelName() ) );
  858. CreateVPhysics();
  859. // Slam the object back to solid - if we really want it to be solid.
  860. if ( m_bSolidBsp )
  861. {
  862. SetSolid( SOLID_BSP );
  863. }
  864. m_bDisabled = false;
  865. }
  866. int CMomentaryRotButton::ObjectCaps( void )
  867. {
  868. int flags = BaseClass::ObjectCaps();
  869. if (!HasSpawnFlags(SF_BUTTON_USE_ACTIVATES))
  870. {
  871. return flags;
  872. }
  873. else
  874. {
  875. return (flags | FCAP_CONTINUOUS_USE | FCAP_USE_IN_RADIUS);
  876. }
  877. }
  878. //-----------------------------------------------------------------------------
  879. // Purpose:
  880. //-----------------------------------------------------------------------------
  881. bool CMomentaryRotButton::CreateVPhysics( void )
  882. {
  883. VPhysicsInitShadow( false, false );
  884. return true;
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Purpose:
  888. //-----------------------------------------------------------------------------
  889. void CMomentaryRotButton::PlaySound( void )
  890. {
  891. if ( m_sNoise == NULL_STRING )
  892. return;
  893. CPASAttenuationFilter filter( this );
  894. EmitSound_t ep;
  895. ep.m_nChannel = CHAN_VOICE;
  896. ep.m_pSoundName = (char*)STRING(m_sNoise);
  897. ep.m_flVolume = 1;
  898. ep.m_SoundLevel = SNDLVL_NORM;
  899. EmitSound( filter, entindex(), ep );
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Purpose: Returns a given angular position as a value along our motion from 0 to 1.
  903. // Input : vecAngles -
  904. //-----------------------------------------------------------------------------
  905. float CMomentaryRotButton::GetPos( const QAngle &vecAngles )
  906. {
  907. float flScale = 1;
  908. if (( m_vecMoveAng[0] < 0 ) || ( m_vecMoveAng[1] < 0 ) || ( m_vecMoveAng[2] < 0 ))
  909. {
  910. flScale = -1;
  911. }
  912. float flPos = flScale * CBaseToggle::AxisDelta( m_spawnflags, vecAngles, m_start ) / m_flMoveDistance;
  913. return( clamp( flPos, 0.f, 1.f ));
  914. }
  915. //------------------------------------------------------------------------------
  916. // Purpose :
  917. // Input : flPosition
  918. //------------------------------------------------------------------------------
  919. void CMomentaryRotButton::InputSetPosition( inputdata_t &inputdata )
  920. {
  921. m_IdealYaw = clamp( inputdata.value.Float(), 0.f, 1.f );
  922. float flCurPos = GetPos( GetLocalAngles() );
  923. if ( flCurPos < m_IdealYaw )
  924. {
  925. // Moving forward (from start to end).
  926. SetLocalAngularVelocity( m_flSpeed * m_vecMoveAng );
  927. m_direction = 1;
  928. }
  929. else if ( flCurPos > m_IdealYaw )
  930. {
  931. // Moving backward (from end to start).
  932. SetLocalAngularVelocity( -m_flSpeed * m_vecMoveAng );
  933. m_direction = -1;
  934. }
  935. else
  936. {
  937. // We're there already; nothing to do.
  938. SetLocalAngularVelocity( vec3_angle );
  939. return;
  940. }
  941. SetMoveDone( &CMomentaryRotButton::SetPositionMoveDone );
  942. SetThink( &CMomentaryRotButton::UpdateThink );
  943. SetNextThink( gpGlobals->curtime );
  944. //
  945. // Think again in 0.1 seconds or the time that it will take us to reach our movement goal,
  946. // whichever is the shorter interval. This prevents us from overshooting and stuttering when we
  947. // are told to change position in very small increments.
  948. //
  949. QAngle vecNewAngles = m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance );
  950. float flAngleDelta = fabs( AxisDelta( m_spawnflags, vecNewAngles, GetLocalAngles() ));
  951. float dt = flAngleDelta / m_flSpeed;
  952. if ( dt < TICK_INTERVAL )
  953. {
  954. dt = TICK_INTERVAL;
  955. float speed = flAngleDelta / TICK_INTERVAL;
  956. SetLocalAngularVelocity( speed * m_vecMoveAng * m_direction );
  957. }
  958. dt = clamp( dt, TICK_INTERVAL, TICK_INTERVAL * 6);
  959. SetMoveDoneTime( dt );
  960. }
  961. //------------------------------------------------------------------------------
  962. // Purpose :
  963. // Input : flPosition
  964. //------------------------------------------------------------------------------
  965. void CMomentaryRotButton::InputSetPositionImmediately( inputdata_t &inputdata )
  966. {
  967. m_IdealYaw = clamp( inputdata.value.Float(), 0.f, 1.f );
  968. SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) );
  969. }
  970. //------------------------------------------------------------------------------
  971. // Purpose: Turns off target updates so that we can change the wheel's position
  972. // without changing the target's position. Used for jiggling when locked.
  973. //------------------------------------------------------------------------------
  974. void CMomentaryRotButton::InputDisableUpdateTarget( inputdata_t &inputdata )
  975. {
  976. m_bUpdateTarget = false;
  977. }
  978. //------------------------------------------------------------------------------
  979. // Purpose: Turns target updates back on (after jiggling).
  980. //------------------------------------------------------------------------------
  981. void CMomentaryRotButton::InputEnableUpdateTarget( inputdata_t &inputdata )
  982. {
  983. m_bUpdateTarget = true;
  984. }
  985. //-----------------------------------------------------------------------------
  986. // Purpose: Locks the button. If locked, the button will play the locked sound
  987. // when the player tries to use it.
  988. //-----------------------------------------------------------------------------
  989. void CMomentaryRotButton::Lock()
  990. {
  991. BaseClass::Lock();
  992. SetLocalAngularVelocity( vec3_angle );
  993. SetMoveDoneTime( -1 );
  994. SetMoveDone( NULL );
  995. SetNextThink( TICK_NEVER_THINK );
  996. SetThink( NULL );
  997. }
  998. //-----------------------------------------------------------------------------
  999. // Purpose: Unlocks the button, making it able to be pressed again.
  1000. //-----------------------------------------------------------------------------
  1001. void CMomentaryRotButton::Unlock()
  1002. {
  1003. BaseClass::Unlock();
  1004. SetMoveDone( &CMomentaryRotButton::ReturnMoveDone );
  1005. // Delay before autoreturn.
  1006. SetMoveDoneTime( 0.1f );
  1007. }
  1008. //-----------------------------------------------------------------------------
  1009. // Purpose: Fires the appropriate outputs at the extremes of motion.
  1010. //-----------------------------------------------------------------------------
  1011. void CMomentaryRotButton::OutputMovementComplete( void )
  1012. {
  1013. if (m_IdealYaw == 1.0)
  1014. {
  1015. m_OnFullyClosed.FireOutput(this, this);
  1016. }
  1017. else if (m_IdealYaw == 0.0)
  1018. {
  1019. m_OnFullyOpen.FireOutput(this, this);
  1020. }
  1021. m_OnReachedPosition.FireOutput( this, this );
  1022. }
  1023. //------------------------------------------------------------------------------
  1024. // Purpose: MoveDone function for the SetPosition input handler. Tracks our
  1025. // progress toward a movement goal and updates our outputs.
  1026. //------------------------------------------------------------------------------
  1027. void CMomentaryRotButton::SetPositionMoveDone(void)
  1028. {
  1029. float flCurPos = GetPos( GetLocalAngles() );
  1030. if ((( flCurPos >= m_IdealYaw ) && ( m_direction == 1 )) ||
  1031. (( flCurPos <= m_IdealYaw ) && ( m_direction == -1 )))
  1032. {
  1033. //
  1034. // We reached or surpassed our movement goal.
  1035. //
  1036. SetLocalAngularVelocity( vec3_angle );
  1037. // BUGBUG: Won't this get the player stuck?
  1038. SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) );
  1039. SetNextThink( TICK_NEVER_THINK );
  1040. SetMoveDoneTime( -1 );
  1041. UpdateTarget( m_IdealYaw, this );
  1042. OutputMovementComplete();
  1043. return;
  1044. }
  1045. // TODO: change this to use a Think function like ReturnThink.
  1046. QAngle vecNewAngles = m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance );
  1047. float flAngleDelta = fabs( AxisDelta( m_spawnflags, vecNewAngles, GetLocalAngles() ));
  1048. float dt = flAngleDelta / m_flSpeed;
  1049. if ( dt < TICK_INTERVAL )
  1050. {
  1051. dt = TICK_INTERVAL;
  1052. float speed = flAngleDelta / TICK_INTERVAL;
  1053. SetLocalAngularVelocity( speed * m_vecMoveAng * m_direction );
  1054. }
  1055. dt = clamp( dt, TICK_INTERVAL, TICK_INTERVAL * 6);
  1056. SetMoveDoneTime( dt );
  1057. }
  1058. //-----------------------------------------------------------------------------
  1059. // Purpose:
  1060. // Input : pActivator -
  1061. // pCaller -
  1062. // useType -
  1063. // value -
  1064. //-----------------------------------------------------------------------------
  1065. void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1066. {
  1067. if ( m_bDisabled == true )
  1068. return;
  1069. if (m_bLocked)
  1070. {
  1071. if ( OnUseLocked( pActivator ) && HasSpawnFlags( SF_BUTTON_JIGGLE_ON_USE_LOCKED ) )
  1072. {
  1073. // Jiggle two degrees.
  1074. float flDist = 2.0 / m_flMoveDistance;
  1075. // Must be first!
  1076. g_EventQueue.AddEvent( this, "_DisableUpdateTarget", 0, this, this );
  1077. variant_t value;
  1078. value.SetFloat( flDist );
  1079. g_EventQueue.AddEvent( this, "SetPosition", value, 0.01, this, this );
  1080. value.SetFloat( 0.0 );
  1081. g_EventQueue.AddEvent( this, "SetPosition", value, 0.1, this, this );
  1082. value.SetFloat( 0.5 * flDist );
  1083. g_EventQueue.AddEvent( this, "SetPosition", value, 0.2, this, this );
  1084. value.SetFloat( 0.0 );
  1085. g_EventQueue.AddEvent( this, "SetPosition", value, 0.3, this, this );
  1086. // Must be last! And must be late enough to cover the settling time.
  1087. g_EventQueue.AddEvent( this, "_EnableUpdateTarget", 0.5, this, this );
  1088. }
  1089. return;
  1090. }
  1091. //
  1092. // Reverse our direction and play movement sound every time the player
  1093. // pauses between uses.
  1094. //
  1095. bool bPlaySound = false;
  1096. if ( !m_lastUsed )
  1097. {
  1098. bPlaySound = true;
  1099. m_direction = -m_direction;
  1100. //Alert that we've been pressed
  1101. m_OnPressed.FireOutput( m_hActivator, this );
  1102. }
  1103. m_lastUsed = 1;
  1104. float flPos = GetPos( GetLocalAngles() );
  1105. UpdateSelf( flPos, bPlaySound );
  1106. //
  1107. // Think every frame while we are moving.
  1108. // HACK: Don't reset the think time if we already have a pending think.
  1109. // This works around an issue with host_thread_mode > 0 when the player's
  1110. // clock runs ahead of the server.
  1111. //
  1112. if ( !m_pfnThink )
  1113. {
  1114. SetThink( &CMomentaryRotButton::UpdateThink );
  1115. SetNextThink( gpGlobals->curtime );
  1116. }
  1117. }
  1118. //-----------------------------------------------------------------------------
  1119. // Purpose: Handles changing direction at the extremes of our range of motion
  1120. // and updating our avelocity while being used by the player.
  1121. // Input : value - Number from 0 to 1 indicating our desired position within
  1122. // our range of motion, 0 = start, 1 = end.
  1123. //-----------------------------------------------------------------------------
  1124. void CMomentaryRotButton::UpdateSelf( float value, bool bPlaySound )
  1125. {
  1126. //
  1127. // Set our move clock to 0.1 seconds in the future so we stop spinning unless we are
  1128. // used again before then.
  1129. //
  1130. SetMoveDoneTime( 0.1 );
  1131. //
  1132. // If we hit the end, zero our avelocity and snap to the end angles.
  1133. //
  1134. if ( m_direction > 0 && value >= 1.0 )
  1135. {
  1136. SetLocalAngularVelocity( vec3_angle );
  1137. SetLocalAngles( m_end );
  1138. m_OnFullyClosed.FireOutput(this, this);
  1139. return;
  1140. }
  1141. //
  1142. // If we returned to the start, zero our avelocity and snap to the start angles.
  1143. //
  1144. else if ( m_direction < 0 && value <= 0 )
  1145. {
  1146. SetLocalAngularVelocity( vec3_angle );
  1147. SetLocalAngles( m_start );
  1148. m_OnFullyOpen.FireOutput(this, this);
  1149. return;
  1150. }
  1151. if ( bPlaySound )
  1152. {
  1153. PlaySound();
  1154. }
  1155. SetLocalAngularVelocity( ( m_direction * m_flSpeed ) * m_vecMoveAng );
  1156. SetMoveDone( &CMomentaryRotButton::UseMoveDone );
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. // Purpose: Updates the value of our position, firing any targets.
  1160. // Input : value - New position, from 0 - 1.
  1161. //-----------------------------------------------------------------------------
  1162. void CMomentaryRotButton::UpdateTarget( float value, CBaseEntity *pActivator )
  1163. {
  1164. if ( !m_bUpdateTarget )
  1165. return;
  1166. if (m_Position.Get() != value)
  1167. {
  1168. m_Position.Set(value, pActivator, this);
  1169. }
  1170. }
  1171. //-----------------------------------------------------------------------------
  1172. // Purpose: Handles the end of motion caused by player use.
  1173. //-----------------------------------------------------------------------------
  1174. void CMomentaryRotButton::UseMoveDone( void )
  1175. {
  1176. SetLocalAngularVelocity( vec3_angle );
  1177. // Make sure our targets stop where we stopped.
  1178. float flPos = GetPos( GetLocalAngles() );
  1179. UpdateTarget( flPos, this );
  1180. // Alert that we've been unpressed
  1181. m_OnUnpressed.FireOutput( m_hActivator, this );
  1182. m_lastUsed = 0;
  1183. if ( !HasSpawnFlags( SF_BUTTON_TOGGLE ) && m_returnSpeed > 0 )
  1184. {
  1185. SetMoveDone( &CMomentaryRotButton::ReturnMoveDone );
  1186. m_direction = -1;
  1187. // Delay before autoreturn.
  1188. SetMoveDoneTime( 0.1f );
  1189. }
  1190. else
  1191. {
  1192. SetThink( NULL );
  1193. SetMoveDone( NULL );
  1194. }
  1195. }
  1196. //-----------------------------------------------------------------------------
  1197. // Purpose: MoveDone function for rotating back to the start position.
  1198. //-----------------------------------------------------------------------------
  1199. void CMomentaryRotButton::ReturnMoveDone( void )
  1200. {
  1201. float value = GetPos( GetLocalAngles() );
  1202. if ( value <= 0 )
  1203. {
  1204. //
  1205. // Got back to the start, stop spinning.
  1206. //
  1207. SetLocalAngularVelocity( vec3_angle );
  1208. SetLocalAngles( m_start );
  1209. UpdateTarget( 0, NULL );
  1210. SetMoveDoneTime( -1 );
  1211. SetMoveDone( NULL );
  1212. SetNextThink( TICK_NEVER_THINK );
  1213. SetThink( NULL );
  1214. }
  1215. else
  1216. {
  1217. SetLocalAngularVelocity( -m_returnSpeed * m_vecMoveAng );
  1218. SetMoveDoneTime( 0.1f );
  1219. SetThink( &CMomentaryRotButton::UpdateThink );
  1220. SetNextThink( gpGlobals->curtime + 0.01f );
  1221. }
  1222. }
  1223. //-----------------------------------------------------------------------------
  1224. // Purpose: Think function for updating target as we move.
  1225. //-----------------------------------------------------------------------------
  1226. void CMomentaryRotButton::UpdateThink( void )
  1227. {
  1228. float value = GetPos( GetLocalAngles() );
  1229. UpdateTarget( value, NULL );
  1230. SetNextThink( gpGlobals->curtime );
  1231. }
  1232. //-----------------------------------------------------------------------------
  1233. // Purpose: Draw any debug text overlays
  1234. // Output : Current text offset from the top
  1235. //-----------------------------------------------------------------------------
  1236. int CMomentaryRotButton::DrawDebugTextOverlays(void)
  1237. {
  1238. int text_offset = BaseClass::DrawDebugTextOverlays();
  1239. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1240. {
  1241. char tempstr[255];
  1242. Q_snprintf(tempstr,sizeof(tempstr),"QAngle: %.2f %.2f %.2f", GetLocalAngles()[0], GetLocalAngles()[1], GetLocalAngles()[2]);
  1243. EntityText(text_offset,tempstr,0);
  1244. text_offset++;
  1245. Q_snprintf(tempstr,sizeof(tempstr),"AVelocity: %.2f %.2f %.2f", GetLocalAngularVelocity()[0], GetLocalAngularVelocity()[1], GetLocalAngularVelocity()[2]);
  1246. EntityText(text_offset,tempstr,0);
  1247. text_offset++;
  1248. Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",m_IdealYaw);
  1249. EntityText(text_offset,tempstr,0);
  1250. text_offset++;
  1251. float flCurPos = GetPos(GetLocalAngles());
  1252. Q_snprintf(tempstr,sizeof(tempstr),"Current Pos: %3.3f",flCurPos);
  1253. EntityText(text_offset,tempstr,0);
  1254. text_offset++;
  1255. Q_snprintf(tempstr,sizeof(tempstr),"Direction: %s",(m_direction == 1) ? "Forward" : "Backward");
  1256. EntityText(text_offset,tempstr,0);
  1257. text_offset++;
  1258. }
  1259. return text_offset;
  1260. }
  1261. //-----------------------------------------------------------------------------
  1262. // Purpose: Input hander that starts the spawner
  1263. //-----------------------------------------------------------------------------
  1264. void CMomentaryRotButton::InputEnable( inputdata_t &inputdata )
  1265. {
  1266. Enable();
  1267. }
  1268. //-----------------------------------------------------------------------------
  1269. // Purpose: Input hander that stops the spawner
  1270. //-----------------------------------------------------------------------------
  1271. void CMomentaryRotButton::InputDisable( inputdata_t &inputdata )
  1272. {
  1273. Disable();
  1274. }
  1275. //-----------------------------------------------------------------------------
  1276. // Purpose: Start the spawner
  1277. //-----------------------------------------------------------------------------
  1278. void CMomentaryRotButton::Enable( void )
  1279. {
  1280. m_bDisabled = false;
  1281. }
  1282. //-----------------------------------------------------------------------------
  1283. // Purpose: Stop the spawner
  1284. //-----------------------------------------------------------------------------
  1285. void CMomentaryRotButton::Disable( void )
  1286. {
  1287. m_bDisabled = true;
  1288. }