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.

1598 lines
43 KiB

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