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.

2748 lines
78 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements many of the entities that control logic flow within a map.
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "entityinput.h"
  8. #include "entityoutput.h"
  9. #include "eventqueue.h"
  10. #include "mathlib/mathlib.h"
  11. #include "globalstate.h"
  12. #include "ndebugoverlay.h"
  13. #include "saverestore_utlvector.h"
  14. #include "vstdlib/random.h"
  15. #include "gameinterface.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. extern CServerGameDLL g_ServerGameDLL;
  19. //-----------------------------------------------------------------------------
  20. // Purpose: Compares a set of integer inputs to the one main input
  21. // Outputs true if they are all equivalant, false otherwise
  22. //-----------------------------------------------------------------------------
  23. class CLogicCompareInteger : public CLogicalEntity
  24. {
  25. public:
  26. DECLARE_CLASS( CLogicCompareInteger, CLogicalEntity );
  27. // outputs
  28. COutputEvent m_OnEqual;
  29. COutputEvent m_OnNotEqual;
  30. // data
  31. int m_iIntegerValue;
  32. int m_iShouldCompareToValue;
  33. DECLARE_DATADESC();
  34. CMultiInputVar m_AllIntCompares;
  35. // Input handlers
  36. void InputValue( inputdata_t &inputdata );
  37. void InputCompareValues( inputdata_t &inputdata );
  38. };
  39. LINK_ENTITY_TO_CLASS( logic_multicompare, CLogicCompareInteger );
  40. BEGIN_DATADESC( CLogicCompareInteger )
  41. DEFINE_OUTPUT( m_OnEqual, "OnEqual" ),
  42. DEFINE_OUTPUT( m_OnNotEqual, "OnNotEqual" ),
  43. DEFINE_KEYFIELD( m_iIntegerValue, FIELD_INTEGER, "IntegerValue" ),
  44. DEFINE_KEYFIELD( m_iShouldCompareToValue, FIELD_INTEGER, "ShouldComparetoValue" ),
  45. DEFINE_FIELD( m_AllIntCompares, FIELD_INPUT ),
  46. DEFINE_INPUTFUNC( FIELD_INPUT, "InputValue", InputValue ),
  47. DEFINE_INPUTFUNC( FIELD_INPUT, "CompareValues", InputCompareValues ),
  48. END_DATADESC()
  49. //-----------------------------------------------------------------------------
  50. // Purpose: Adds to the list of compared values
  51. //-----------------------------------------------------------------------------
  52. void CLogicCompareInteger::InputValue( inputdata_t &inputdata )
  53. {
  54. // make sure it's an int, if it can't be converted just throw it away
  55. if ( !inputdata.value.Convert(FIELD_INTEGER) )
  56. return;
  57. // update the value list with the new value
  58. m_AllIntCompares.AddValue( inputdata.value, inputdata.nOutputID );
  59. // if we haven't already this frame, send a message to ourself to update and fire
  60. if ( !m_AllIntCompares.m_bUpdatedThisFrame )
  61. {
  62. // TODO: need to add this event with a lower priority, so it gets called after all inputs have arrived
  63. g_EventQueue.AddEvent( this, "CompareValues", 0, inputdata.pActivator, this, inputdata.nOutputID );
  64. m_AllIntCompares.m_bUpdatedThisFrame = TRUE;
  65. }
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose: Forces a recompare
  69. //-----------------------------------------------------------------------------
  70. void CLogicCompareInteger::InputCompareValues( inputdata_t &inputdata )
  71. {
  72. m_AllIntCompares.m_bUpdatedThisFrame = FALSE;
  73. // loop through all the values comparing them
  74. int value = m_iIntegerValue;
  75. CMultiInputVar::inputitem_t *input = m_AllIntCompares.m_InputList;
  76. if ( !m_iShouldCompareToValue && input )
  77. {
  78. value = input->value.Int();
  79. }
  80. while ( input )
  81. {
  82. if ( input->value.Int() != value )
  83. {
  84. // false
  85. m_OnNotEqual.FireOutput( inputdata.pActivator, this );
  86. return;
  87. }
  88. input = input->next;
  89. }
  90. // true! all values equal
  91. m_OnEqual.FireOutput( inputdata.pActivator, this );
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose: Timer entity. Fires an output at regular or random intervals.
  95. //-----------------------------------------------------------------------------
  96. //
  97. // Spawnflags and others constants.
  98. //
  99. const int SF_TIMER_UPDOWN = 1;
  100. const float LOGIC_TIMER_MIN_INTERVAL = 0.01;
  101. class CTimerEntity : public CLogicalEntity
  102. {
  103. public:
  104. DECLARE_CLASS( CTimerEntity, CLogicalEntity );
  105. void Spawn( void );
  106. void Think( void );
  107. void Toggle( void );
  108. void Enable( void );
  109. void Disable( void );
  110. void FireTimer( void );
  111. int DrawDebugTextOverlays(void);
  112. // outputs
  113. COutputEvent m_OnTimer;
  114. COutputEvent m_OnTimerHigh;
  115. COutputEvent m_OnTimerLow;
  116. // inputs
  117. void InputToggle( inputdata_t &inputdata );
  118. void InputEnable( inputdata_t &inputdata );
  119. void InputDisable( inputdata_t &inputdata );
  120. void InputFireTimer( inputdata_t &inputdata );
  121. void InputRefireTime( inputdata_t &inputdata );
  122. void InputResetTimer( inputdata_t &inputdata );
  123. void InputAddToTimer( inputdata_t &inputdata );
  124. void InputSubtractFromTimer( inputdata_t &inputdata );
  125. int m_iDisabled;
  126. float m_flRefireTime;
  127. bool m_bUpDownState;
  128. int m_iUseRandomTime;
  129. float m_flLowerRandomBound;
  130. float m_flUpperRandomBound;
  131. // methods
  132. void ResetTimer( void );
  133. DECLARE_DATADESC();
  134. };
  135. LINK_ENTITY_TO_CLASS( logic_timer, CTimerEntity );
  136. BEGIN_DATADESC( CTimerEntity )
  137. // Keys
  138. DEFINE_KEYFIELD( m_iDisabled, FIELD_INTEGER, "StartDisabled" ),
  139. DEFINE_KEYFIELD( m_flRefireTime, FIELD_FLOAT, "RefireTime" ),
  140. DEFINE_FIELD( m_bUpDownState, FIELD_BOOLEAN ),
  141. // Inputs
  142. DEFINE_INPUTFUNC( FIELD_FLOAT, "RefireTime", InputRefireTime ),
  143. DEFINE_INPUTFUNC( FIELD_VOID, "FireTimer", InputFireTimer ),
  144. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  145. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  146. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  147. DEFINE_INPUTFUNC( FIELD_FLOAT, "AddToTimer", InputAddToTimer ),
  148. DEFINE_INPUTFUNC( FIELD_VOID, "ResetTimer", InputResetTimer ),
  149. DEFINE_INPUTFUNC( FIELD_FLOAT, "SubtractFromTimer", InputSubtractFromTimer ),
  150. DEFINE_INPUT( m_iUseRandomTime, FIELD_INTEGER, "UseRandomTime" ),
  151. DEFINE_INPUT( m_flLowerRandomBound, FIELD_FLOAT, "LowerRandomBound" ),
  152. DEFINE_INPUT( m_flUpperRandomBound, FIELD_FLOAT, "UpperRandomBound" ),
  153. // Outputs
  154. DEFINE_OUTPUT( m_OnTimer, "OnTimer" ),
  155. DEFINE_OUTPUT( m_OnTimerHigh, "OnTimerHigh" ),
  156. DEFINE_OUTPUT( m_OnTimerLow, "OnTimerLow" ),
  157. END_DATADESC()
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. //-----------------------------------------------------------------------------
  161. void CTimerEntity::Spawn( void )
  162. {
  163. if (!m_iUseRandomTime && (m_flRefireTime < LOGIC_TIMER_MIN_INTERVAL))
  164. {
  165. m_flRefireTime = LOGIC_TIMER_MIN_INTERVAL;
  166. }
  167. if ( !m_iDisabled && (m_flRefireTime > 0 || m_iUseRandomTime) )
  168. {
  169. Enable();
  170. }
  171. else
  172. {
  173. Disable();
  174. }
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose:
  178. //-----------------------------------------------------------------------------
  179. void CTimerEntity::Think( void )
  180. {
  181. FireTimer();
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Purpose: Sets the time the timerentity will next fire
  185. //-----------------------------------------------------------------------------
  186. void CTimerEntity::ResetTimer( void )
  187. {
  188. if ( m_iDisabled )
  189. return;
  190. if ( m_iUseRandomTime )
  191. {
  192. m_flRefireTime = random->RandomFloat( m_flLowerRandomBound, m_flUpperRandomBound );
  193. }
  194. SetNextThink( gpGlobals->curtime + m_flRefireTime );
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose:
  198. //-----------------------------------------------------------------------------
  199. void CTimerEntity::Enable( void )
  200. {
  201. m_iDisabled = FALSE;
  202. ResetTimer();
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. void CTimerEntity::Disable( void )
  208. {
  209. m_iDisabled = TRUE;
  210. SetNextThink( TICK_NEVER_THINK );
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CTimerEntity::Toggle( void )
  216. {
  217. if ( m_iDisabled )
  218. {
  219. Enable();
  220. }
  221. else
  222. {
  223. Disable();
  224. }
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose:
  228. //-----------------------------------------------------------------------------
  229. void CTimerEntity::FireTimer( void )
  230. {
  231. if ( !m_iDisabled )
  232. {
  233. //
  234. // Up/down timers alternate between two outputs.
  235. //
  236. if (m_spawnflags & SF_TIMER_UPDOWN)
  237. {
  238. if (m_bUpDownState)
  239. {
  240. m_OnTimerHigh.FireOutput( this, this );
  241. }
  242. else
  243. {
  244. m_OnTimerLow.FireOutput( this, this );
  245. }
  246. m_bUpDownState = !m_bUpDownState;
  247. }
  248. //
  249. // Regular timers only fire a single output.
  250. //
  251. else
  252. {
  253. m_OnTimer.FireOutput( this, this );
  254. }
  255. ResetTimer();
  256. }
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose:
  260. //-----------------------------------------------------------------------------
  261. void CTimerEntity::InputEnable( inputdata_t &inputdata )
  262. {
  263. Enable();
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose:
  267. //-----------------------------------------------------------------------------
  268. void CTimerEntity::InputDisable( inputdata_t &inputdata )
  269. {
  270. Disable();
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose:
  274. //-----------------------------------------------------------------------------
  275. void CTimerEntity::InputToggle( inputdata_t &inputdata )
  276. {
  277. Toggle();
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose:
  281. //-----------------------------------------------------------------------------
  282. void CTimerEntity::InputFireTimer( inputdata_t &inputdata )
  283. {
  284. FireTimer();
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Changes the time interval between timer fires
  288. // Resets the next firing to be time + newRefireTime
  289. // Input : Float refire frequency in seconds.
  290. //-----------------------------------------------------------------------------
  291. void CTimerEntity::InputRefireTime( inputdata_t &inputdata )
  292. {
  293. float flRefireInterval = inputdata.value.Float();
  294. if ( flRefireInterval < LOGIC_TIMER_MIN_INTERVAL)
  295. {
  296. flRefireInterval = LOGIC_TIMER_MIN_INTERVAL;
  297. }
  298. if (m_flRefireTime != flRefireInterval )
  299. {
  300. m_flRefireTime = flRefireInterval;
  301. ResetTimer();
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. //-----------------------------------------------------------------------------
  306. void CTimerEntity::InputResetTimer( inputdata_t &inputdata )
  307. {
  308. // don't reset the timer if it isn't enabled
  309. if ( m_iDisabled )
  310. return;
  311. ResetTimer();
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose: Adds to the time interval if the timer is enabled
  315. // Input : Float time to add in seconds
  316. //-----------------------------------------------------------------------------
  317. void CTimerEntity::InputAddToTimer( inputdata_t &inputdata )
  318. {
  319. // don't add time if the timer isn't enabled
  320. if ( m_iDisabled )
  321. return;
  322. // Add time to timer
  323. float flNextThink = GetNextThink();
  324. SetNextThink( flNextThink += inputdata.value.Float() );
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Purpose: Subtract from the time interval if the timer is enabled
  328. // Input : Float time to subtract in seconds
  329. //-----------------------------------------------------------------------------
  330. void CTimerEntity::InputSubtractFromTimer( inputdata_t &inputdata )
  331. {
  332. // don't add time if the timer isn't enabled
  333. if ( m_iDisabled )
  334. return;
  335. // Subtract time from the timer but don't let the timer go negative
  336. float flNextThink = GetNextThink();
  337. if ( ( flNextThink - gpGlobals->curtime ) <= inputdata.value.Float() )
  338. {
  339. SetNextThink( gpGlobals->curtime );
  340. }
  341. else
  342. {
  343. SetNextThink( flNextThink -= inputdata.value.Float() );
  344. }
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: Draw any debug text overlays
  348. // Output : Current text offset from the top
  349. //-----------------------------------------------------------------------------
  350. int CTimerEntity::DrawDebugTextOverlays( void )
  351. {
  352. int text_offset = BaseClass::DrawDebugTextOverlays();
  353. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  354. {
  355. char tempstr[512];
  356. // print refire time
  357. Q_snprintf(tempstr,sizeof(tempstr),"refire interval: %.2f sec", m_flRefireTime);
  358. EntityText(text_offset,tempstr,0);
  359. text_offset++;
  360. // print seconds to next fire
  361. if ( !m_iDisabled )
  362. {
  363. float flNextThink = GetNextThink();
  364. Q_snprintf( tempstr, sizeof( tempstr ), " firing in: %.2f sec", flNextThink - gpGlobals->curtime );
  365. EntityText( text_offset, tempstr, 0);
  366. text_offset++;
  367. }
  368. }
  369. return text_offset;
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Computes a line between two entities
  373. //-----------------------------------------------------------------------------
  374. class CLogicLineToEntity : public CLogicalEntity
  375. {
  376. public:
  377. DECLARE_CLASS( CLogicLineToEntity, CLogicalEntity );
  378. void Activate(void);
  379. void Spawn( void );
  380. void Think( void );
  381. // outputs
  382. COutputVector m_Line;
  383. DECLARE_DATADESC();
  384. private:
  385. string_t m_SourceName;
  386. EHANDLE m_StartEntity;
  387. EHANDLE m_EndEntity;
  388. };
  389. LINK_ENTITY_TO_CLASS( logic_lineto, CLogicLineToEntity );
  390. BEGIN_DATADESC( CLogicLineToEntity )
  391. // Keys
  392. // target is handled in the base class, stored in field m_target
  393. DEFINE_KEYFIELD( m_SourceName, FIELD_STRING, "source" ),
  394. DEFINE_FIELD( m_StartEntity, FIELD_EHANDLE ),
  395. DEFINE_FIELD( m_EndEntity, FIELD_EHANDLE ),
  396. // Outputs
  397. DEFINE_OUTPUT( m_Line, "Line" ),
  398. END_DATADESC()
  399. //-----------------------------------------------------------------------------
  400. // Find the entities
  401. //-----------------------------------------------------------------------------
  402. void CLogicLineToEntity::Activate(void)
  403. {
  404. BaseClass::Activate();
  405. if (m_target != NULL_STRING)
  406. {
  407. m_EndEntity = gEntList.FindEntityByName( NULL, m_target );
  408. //
  409. // If we were given a bad measure target, just measure sound where we are.
  410. //
  411. if ((m_EndEntity == NULL) || (m_EndEntity->edict() == NULL))
  412. {
  413. Warning( "logic_lineto - Target not found or target with no origin!\n");
  414. m_EndEntity = this;
  415. }
  416. }
  417. else
  418. {
  419. m_EndEntity = this;
  420. }
  421. if (m_SourceName != NULL_STRING)
  422. {
  423. m_StartEntity = gEntList.FindEntityByName( NULL, m_SourceName );
  424. //
  425. // If we were given a bad measure target, just measure sound where we are.
  426. //
  427. if ((m_StartEntity == NULL) || (m_StartEntity->edict() == NULL))
  428. {
  429. Warning( "logic_lineto - Source not found or source with no origin!\n");
  430. m_StartEntity = this;
  431. }
  432. }
  433. else
  434. {
  435. m_StartEntity = this;
  436. }
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Find the entities
  440. //-----------------------------------------------------------------------------
  441. void CLogicLineToEntity::Spawn(void)
  442. {
  443. SetNextThink( gpGlobals->curtime + 0.01f );
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Find the entities
  447. //-----------------------------------------------------------------------------
  448. void CLogicLineToEntity::Think(void)
  449. {
  450. CBaseEntity* pDest = m_EndEntity.Get();
  451. CBaseEntity* pSrc = m_StartEntity.Get();
  452. if (!pDest || !pSrc || !pDest->edict() || !pSrc->edict())
  453. {
  454. // Can sleep for a long time, no more lines.
  455. m_Line.Set( vec3_origin, this, this );
  456. SetNextThink( gpGlobals->curtime + 10 );
  457. return;
  458. }
  459. Vector delta;
  460. VectorSubtract( pDest->GetAbsOrigin(), pSrc->GetAbsOrigin(), delta );
  461. m_Line.Set(delta, this, this);
  462. SetNextThink( gpGlobals->curtime + 0.01f );
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose: Remaps a given input range to an output range.
  466. //-----------------------------------------------------------------------------
  467. const int SF_MATH_REMAP_IGNORE_OUT_OF_RANGE = 1;
  468. const int SF_MATH_REMAP_CLAMP_OUTPUT_TO_RANGE = 2;
  469. class CMathRemap : public CLogicalEntity
  470. {
  471. public:
  472. DECLARE_CLASS( CMathRemap, CLogicalEntity );
  473. void Spawn(void);
  474. // Keys
  475. float m_flInMin;
  476. float m_flInMax;
  477. float m_flOut1; // Output value when input is m_fInMin
  478. float m_flOut2; // Output value when input is m_fInMax
  479. bool m_bEnabled;
  480. // Inputs
  481. void InputValue( inputdata_t &inputdata );
  482. void InputEnable( inputdata_t &inputdata );
  483. void InputDisable( inputdata_t &inputdata );
  484. // Outputs
  485. COutputFloat m_OutValue;
  486. DECLARE_DATADESC();
  487. };
  488. LINK_ENTITY_TO_CLASS(math_remap, CMathRemap);
  489. BEGIN_DATADESC( CMathRemap )
  490. DEFINE_INPUTFUNC(FIELD_FLOAT, "InValue", InputValue ),
  491. DEFINE_OUTPUT(m_OutValue, "OutValue"),
  492. DEFINE_KEYFIELD(m_flInMin, FIELD_FLOAT, "in1"),
  493. DEFINE_KEYFIELD(m_flInMax, FIELD_FLOAT, "in2"),
  494. DEFINE_KEYFIELD(m_flOut1, FIELD_FLOAT, "out1"),
  495. DEFINE_KEYFIELD(m_flOut2, FIELD_FLOAT, "out2"),
  496. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  497. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  498. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  499. END_DATADESC()
  500. //-----------------------------------------------------------------------------
  501. // Purpose:
  502. //-----------------------------------------------------------------------------
  503. void CMathRemap::Spawn(void)
  504. {
  505. //
  506. // Avoid a divide by zero in ValueChanged.
  507. //
  508. if (m_flInMin == m_flInMax)
  509. {
  510. m_flInMin = 0;
  511. m_flInMax = 1;
  512. }
  513. //
  514. // Make sure min and max are set properly relative to one another.
  515. //
  516. if (m_flInMin > m_flInMax)
  517. {
  518. float flTemp = m_flInMin;
  519. m_flInMin = m_flInMax;
  520. m_flInMax = flTemp;
  521. }
  522. m_bEnabled = true;
  523. }
  524. //-----------------------------------------------------------------------------
  525. // Purpose:
  526. //-----------------------------------------------------------------------------
  527. void CMathRemap::InputEnable( inputdata_t &inputdata )
  528. {
  529. m_bEnabled = true;
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Purpose:
  533. //-----------------------------------------------------------------------------
  534. void CMathRemap::InputDisable( inputdata_t &inputdata )
  535. {
  536. m_bEnabled = false;
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Purpose: Input handler that is called when the input value changes.
  540. //-----------------------------------------------------------------------------
  541. void CMathRemap::InputValue( inputdata_t &inputdata )
  542. {
  543. float flValue = inputdata.value.Float();
  544. //
  545. // Disallow out-of-range input values to avoid out-of-range output values.
  546. //
  547. float flClampValue = clamp(flValue, m_flInMin, m_flInMax);
  548. if ((flClampValue == flValue) || !FBitSet(m_spawnflags, SF_MATH_REMAP_IGNORE_OUT_OF_RANGE))
  549. {
  550. //
  551. // Remap the input value to the desired output range and update the output.
  552. //
  553. float flRemappedValue = m_flOut1 + (((flValue - m_flInMin) * (m_flOut2 - m_flOut1)) / (m_flInMax - m_flInMin));
  554. if ( FBitSet( m_spawnflags, SF_MATH_REMAP_CLAMP_OUTPUT_TO_RANGE ) )
  555. {
  556. flRemappedValue = clamp( flRemappedValue, m_flOut1, m_flOut2 );
  557. }
  558. if ( m_bEnabled == true )
  559. {
  560. m_OutValue.Set(flRemappedValue, inputdata.pActivator, this);
  561. }
  562. }
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Purpose: Remaps a given input range to an output range.
  566. //-----------------------------------------------------------------------------
  567. const int SF_COLOR_BLEND_IGNORE_OUT_OF_RANGE = 1;
  568. class CMathColorBlend : public CLogicalEntity
  569. {
  570. public:
  571. DECLARE_CLASS( CMathColorBlend, CLogicalEntity );
  572. void Spawn(void);
  573. // Keys
  574. float m_flInMin;
  575. float m_flInMax;
  576. color32 m_OutColor1; // Output color when input is m_fInMin
  577. color32 m_OutColor2; // Output color when input is m_fInMax
  578. // Inputs
  579. void InputValue( inputdata_t &inputdata );
  580. // Outputs
  581. COutputColor32 m_OutValue;
  582. DECLARE_DATADESC();
  583. };
  584. LINK_ENTITY_TO_CLASS(math_colorblend, CMathColorBlend);
  585. BEGIN_DATADESC( CMathColorBlend )
  586. DEFINE_INPUTFUNC(FIELD_FLOAT, "InValue", InputValue ),
  587. DEFINE_OUTPUT(m_OutValue, "OutColor"),
  588. DEFINE_KEYFIELD(m_flInMin, FIELD_FLOAT, "inmin"),
  589. DEFINE_KEYFIELD(m_flInMax, FIELD_FLOAT, "inmax"),
  590. DEFINE_KEYFIELD(m_OutColor1, FIELD_COLOR32, "colormin"),
  591. DEFINE_KEYFIELD(m_OutColor2, FIELD_COLOR32, "colormax"),
  592. END_DATADESC()
  593. //-----------------------------------------------------------------------------
  594. // Purpose:
  595. //-----------------------------------------------------------------------------
  596. void CMathColorBlend::Spawn(void)
  597. {
  598. //
  599. // Avoid a divide by zero in ValueChanged.
  600. //
  601. if (m_flInMin == m_flInMax)
  602. {
  603. m_flInMin = 0;
  604. m_flInMax = 1;
  605. }
  606. //
  607. // Make sure min and max are set properly relative to one another.
  608. //
  609. if (m_flInMin > m_flInMax)
  610. {
  611. float flTemp = m_flInMin;
  612. m_flInMin = m_flInMax;
  613. m_flInMax = flTemp;
  614. }
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Purpose: Input handler that is called when the input value changes.
  618. //-----------------------------------------------------------------------------
  619. void CMathColorBlend::InputValue( inputdata_t &inputdata )
  620. {
  621. float flValue = inputdata.value.Float();
  622. //
  623. // Disallow out-of-range input values to avoid out-of-range output values.
  624. //
  625. float flClampValue = clamp(flValue, m_flInMin, m_flInMax);
  626. if ((flClampValue == flValue) || !FBitSet(m_spawnflags, SF_COLOR_BLEND_IGNORE_OUT_OF_RANGE))
  627. {
  628. //
  629. // Remap the input value to the desired output color and update the output.
  630. //
  631. color32 Color;
  632. Color.r = m_OutColor1.r + (((flClampValue - m_flInMin) * (m_OutColor2.r - m_OutColor1.r)) / (m_flInMax - m_flInMin));
  633. Color.g = m_OutColor1.g + (((flClampValue - m_flInMin) * (m_OutColor2.g - m_OutColor1.g)) / (m_flInMax - m_flInMin));
  634. Color.b = m_OutColor1.b + (((flClampValue - m_flInMin) * (m_OutColor2.b - m_OutColor1.b)) / (m_flInMax - m_flInMin));
  635. Color.a = m_OutColor1.a + (((flClampValue - m_flInMin) * (m_OutColor2.a - m_OutColor1.a)) / (m_flInMax - m_flInMin));
  636. m_OutValue.Set(Color, inputdata.pActivator, this);
  637. }
  638. }
  639. //-----------------------------------------------------------------------------
  640. // Console command to set the state of a global
  641. //-----------------------------------------------------------------------------
  642. void CC_Global_Set( const CCommand &args )
  643. {
  644. const char *szGlobal = args[1];
  645. const char *szState = args[2];
  646. if ( szGlobal == NULL || szState == NULL )
  647. {
  648. Msg( "Usage: global_set <globalname> <state>: Sets the state of the given env_global (0 = OFF, 1 = ON, 2 = DEAD).\n" );
  649. return;
  650. }
  651. int nState = atoi( szState );
  652. int nIndex = GlobalEntity_GetIndex( szGlobal );
  653. if ( nIndex >= 0 )
  654. {
  655. GlobalEntity_SetState( nIndex, ( GLOBALESTATE )nState );
  656. }
  657. else
  658. {
  659. GlobalEntity_Add( szGlobal, STRING( gpGlobals->mapname ), ( GLOBALESTATE )nState );
  660. }
  661. }
  662. static ConCommand global_set( "global_set", CC_Global_Set, "global_set <globalname> <state>: Sets the state of the given env_global (0 = OFF, 1 = ON, 2 = DEAD).", FCVAR_CHEAT );
  663. //-----------------------------------------------------------------------------
  664. // Purpose: Holds a global state that can be queried by other entities to change
  665. // their behavior, such as "predistaster".
  666. //-----------------------------------------------------------------------------
  667. const int SF_GLOBAL_SET = 1; // Set global state to initial state on spawn
  668. class CEnvGlobal : public CLogicalEntity
  669. {
  670. public:
  671. DECLARE_CLASS( CEnvGlobal, CLogicalEntity );
  672. void Spawn( void );
  673. // Input handlers
  674. void InputTurnOn( inputdata_t &inputdata );
  675. void InputTurnOff( inputdata_t &inputdata );
  676. void InputRemove( inputdata_t &inputdata );
  677. void InputToggle( inputdata_t &inputdata );
  678. void InputSetCounter( inputdata_t &inputdata );
  679. void InputAddToCounter( inputdata_t &inputdata );
  680. void InputGetCounter( inputdata_t &inputdata );
  681. int DrawDebugTextOverlays(void);
  682. DECLARE_DATADESC();
  683. COutputInt m_outCounter;
  684. string_t m_globalstate;
  685. int m_triggermode;
  686. int m_initialstate;
  687. int m_counter; // A counter value associated with this global.
  688. };
  689. BEGIN_DATADESC( CEnvGlobal )
  690. DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ),
  691. DEFINE_FIELD( m_triggermode, FIELD_INTEGER ),
  692. DEFINE_KEYFIELD( m_initialstate, FIELD_INTEGER, "initialstate" ),
  693. DEFINE_KEYFIELD( m_counter, FIELD_INTEGER, "counter" ),
  694. // Inputs
  695. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  696. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  697. DEFINE_INPUTFUNC( FIELD_VOID, "Remove", InputRemove ),
  698. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  699. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCounter", InputSetCounter ),
  700. DEFINE_INPUTFUNC( FIELD_INTEGER, "AddToCounter", InputAddToCounter ),
  701. DEFINE_INPUTFUNC( FIELD_VOID, "GetCounter", InputGetCounter ),
  702. DEFINE_OUTPUT( m_outCounter, "Counter" ),
  703. END_DATADESC()
  704. LINK_ENTITY_TO_CLASS( env_global, CEnvGlobal );
  705. //-----------------------------------------------------------------------------
  706. // Purpose:
  707. //-----------------------------------------------------------------------------
  708. void CEnvGlobal::Spawn( void )
  709. {
  710. if ( !m_globalstate )
  711. {
  712. UTIL_Remove( this );
  713. return;
  714. }
  715. #ifdef HL2_EPISODIC
  716. // if we modify the state of the physics cannon, make sure we precache the ragdoll boogie zap sound
  717. if ( ( m_globalstate != NULL_STRING ) && ( stricmp( STRING( m_globalstate ), "super_phys_gun" ) == 0 ) )
  718. {
  719. PrecacheScriptSound( "RagdollBoogie.Zap" );
  720. }
  721. #endif
  722. if ( FBitSet( m_spawnflags, SF_GLOBAL_SET ) )
  723. {
  724. if ( !GlobalEntity_IsInTable( m_globalstate ) )
  725. {
  726. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, (GLOBALESTATE)m_initialstate );
  727. }
  728. if ( m_counter != 0 )
  729. {
  730. GlobalEntity_SetCounter( m_globalstate, m_counter );
  731. }
  732. }
  733. }
  734. //------------------------------------------------------------------------------
  735. // Purpose:
  736. //------------------------------------------------------------------------------
  737. void CEnvGlobal::InputTurnOn( inputdata_t &inputdata )
  738. {
  739. if ( GlobalEntity_IsInTable( m_globalstate ) )
  740. {
  741. GlobalEntity_SetState( m_globalstate, GLOBAL_ON );
  742. }
  743. else
  744. {
  745. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
  746. }
  747. }
  748. //------------------------------------------------------------------------------
  749. // Purpose:
  750. //------------------------------------------------------------------------------
  751. void CEnvGlobal::InputTurnOff( inputdata_t &inputdata )
  752. {
  753. if ( GlobalEntity_IsInTable( m_globalstate ) )
  754. {
  755. GlobalEntity_SetState( m_globalstate, GLOBAL_OFF );
  756. }
  757. else
  758. {
  759. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_OFF );
  760. }
  761. }
  762. //------------------------------------------------------------------------------
  763. // Purpose:
  764. //------------------------------------------------------------------------------
  765. void CEnvGlobal::InputRemove( inputdata_t &inputdata )
  766. {
  767. if ( GlobalEntity_IsInTable( m_globalstate ) )
  768. {
  769. GlobalEntity_SetState( m_globalstate, GLOBAL_DEAD );
  770. }
  771. else
  772. {
  773. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_DEAD );
  774. }
  775. }
  776. //------------------------------------------------------------------------------
  777. //------------------------------------------------------------------------------
  778. void CEnvGlobal::InputSetCounter( inputdata_t &inputdata )
  779. {
  780. if ( !GlobalEntity_IsInTable( m_globalstate ) )
  781. {
  782. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
  783. }
  784. GlobalEntity_SetCounter( m_globalstate, inputdata.value.Int() );
  785. }
  786. //------------------------------------------------------------------------------
  787. //------------------------------------------------------------------------------
  788. void CEnvGlobal::InputAddToCounter( inputdata_t &inputdata )
  789. {
  790. if ( !GlobalEntity_IsInTable( m_globalstate ) )
  791. {
  792. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
  793. }
  794. GlobalEntity_AddToCounter( m_globalstate, inputdata.value.Int() );
  795. }
  796. //------------------------------------------------------------------------------
  797. //------------------------------------------------------------------------------
  798. void CEnvGlobal::InputGetCounter( inputdata_t &inputdata )
  799. {
  800. if ( !GlobalEntity_IsInTable( m_globalstate ) )
  801. {
  802. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
  803. }
  804. m_outCounter.Set( GlobalEntity_GetCounter( m_globalstate ), inputdata.pActivator, this );
  805. }
  806. //------------------------------------------------------------------------------
  807. // Purpose:
  808. //------------------------------------------------------------------------------
  809. void CEnvGlobal::InputToggle( inputdata_t &inputdata )
  810. {
  811. GLOBALESTATE oldState = GlobalEntity_GetState( m_globalstate );
  812. GLOBALESTATE newState;
  813. if ( oldState == GLOBAL_ON )
  814. {
  815. newState = GLOBAL_OFF;
  816. }
  817. else if ( oldState == GLOBAL_OFF )
  818. {
  819. newState = GLOBAL_ON;
  820. }
  821. else
  822. {
  823. return;
  824. }
  825. if ( GlobalEntity_IsInTable( m_globalstate ) )
  826. {
  827. GlobalEntity_SetState( m_globalstate, newState );
  828. }
  829. else
  830. {
  831. GlobalEntity_Add( m_globalstate, gpGlobals->mapname, newState );
  832. }
  833. }
  834. //-----------------------------------------------------------------------------
  835. // Purpose: Draw any debug text overlays
  836. // Input :
  837. // Output : Current text offset from the top
  838. //-----------------------------------------------------------------------------
  839. int CEnvGlobal::DrawDebugTextOverlays(void)
  840. {
  841. // Skip AIClass debug overlays
  842. int text_offset = CBaseEntity::DrawDebugTextOverlays();
  843. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  844. {
  845. char tempstr[512];
  846. Q_snprintf(tempstr,sizeof(tempstr),"State: %s",STRING(m_globalstate));
  847. EntityText(text_offset,tempstr,0);
  848. text_offset++;
  849. GLOBALESTATE nState = GlobalEntity_GetState( m_globalstate );
  850. switch( nState )
  851. {
  852. case GLOBAL_OFF:
  853. Q_strncpy(tempstr,"Value: OFF",sizeof(tempstr));
  854. break;
  855. case GLOBAL_ON:
  856. Q_strncpy(tempstr,"Value: ON",sizeof(tempstr));
  857. break;
  858. case GLOBAL_DEAD:
  859. Q_strncpy(tempstr,"Value: DEAD",sizeof(tempstr));
  860. break;
  861. }
  862. EntityText(text_offset,tempstr,0);
  863. text_offset++;
  864. }
  865. return text_offset;
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Purpose:
  869. //-----------------------------------------------------------------------------
  870. #define MS_MAX_TARGETS 32
  871. const int SF_MULTI_INIT = 1;
  872. class CMultiSource : public CLogicalEntity
  873. {
  874. public:
  875. DECLARE_CLASS( CMultiSource, CLogicalEntity );
  876. void Spawn( );
  877. bool KeyValue( const char *szKeyName, const char *szValue );
  878. void Use( ::CBaseEntity *pActivator, ::CBaseEntity *pCaller, USE_TYPE useType, float value );
  879. int ObjectCaps( void ) { return(BaseClass::ObjectCaps() | FCAP_MASTER); }
  880. bool IsTriggered( ::CBaseEntity *pActivator );
  881. void Register( void );
  882. DECLARE_DATADESC();
  883. EHANDLE m_rgEntities[MS_MAX_TARGETS];
  884. int m_rgTriggered[MS_MAX_TARGETS];
  885. COutputEvent m_OnTrigger; // Fired when all connections are triggered.
  886. int m_iTotal;
  887. string_t m_globalstate;
  888. };
  889. BEGIN_DATADESC( CMultiSource )
  890. //!!!BUGBUG FIX
  891. DEFINE_ARRAY( m_rgEntities, FIELD_EHANDLE, MS_MAX_TARGETS ),
  892. DEFINE_ARRAY( m_rgTriggered, FIELD_INTEGER, MS_MAX_TARGETS ),
  893. DEFINE_FIELD( m_iTotal, FIELD_INTEGER ),
  894. DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ),
  895. // Function pointers
  896. DEFINE_FUNCTION( Register ),
  897. // Outputs
  898. DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"),
  899. END_DATADESC()
  900. LINK_ENTITY_TO_CLASS( multisource, CMultiSource );
  901. //-----------------------------------------------------------------------------
  902. // Purpose: Cache user entity field values until spawn is called.
  903. // Input : szKeyName - Key to handle.
  904. // szValue - Value for key.
  905. // Output : Returns true if the key was handled, false if not.
  906. //-----------------------------------------------------------------------------
  907. bool CMultiSource::KeyValue( const char *szKeyName, const char *szValue )
  908. {
  909. if ( FStrEq(szKeyName, "style") ||
  910. FStrEq(szKeyName, "height") ||
  911. FStrEq(szKeyName, "killtarget") ||
  912. FStrEq(szKeyName, "value1") ||
  913. FStrEq(szKeyName, "value2") ||
  914. FStrEq(szKeyName, "value3"))
  915. {
  916. }
  917. else
  918. {
  919. return BaseClass::KeyValue( szKeyName, szValue );
  920. }
  921. return true;
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Purpose:
  925. //-----------------------------------------------------------------------------
  926. void CMultiSource::Spawn()
  927. {
  928. SetNextThink( gpGlobals->curtime + 0.1f );
  929. m_spawnflags |= SF_MULTI_INIT; // Until it's initialized
  930. SetThink(&CMultiSource::Register);
  931. }
  932. //-----------------------------------------------------------------------------
  933. // Purpose:
  934. // Input : pActivator -
  935. // pCaller -
  936. // useType -
  937. // value -
  938. //-----------------------------------------------------------------------------
  939. void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  940. {
  941. int i = 0;
  942. // Find the entity in our list
  943. while (i < m_iTotal)
  944. if ( m_rgEntities[i++] == pCaller )
  945. break;
  946. // if we didn't find it, report error and leave
  947. if (i > m_iTotal)
  948. {
  949. Warning("MultiSrc: Used by non member %s.\n", pCaller->edict() ? pCaller->GetClassname() : "<logical entity>");
  950. return;
  951. }
  952. // CONSIDER: a Use input to the multisource always toggles. Could check useType for ON/OFF/TOGGLE
  953. m_rgTriggered[i-1] ^= 1;
  954. //
  955. if ( IsTriggered( pActivator ) )
  956. {
  957. DevMsg( 2, "Multisource %s enabled (%d inputs)\n", GetDebugName(), m_iTotal );
  958. USE_TYPE useType = USE_TOGGLE;
  959. if ( m_globalstate != NULL_STRING )
  960. useType = USE_ON;
  961. m_OnTrigger.FireOutput(pActivator, this);
  962. }
  963. }
  964. //-----------------------------------------------------------------------------
  965. // Purpose:
  966. //-----------------------------------------------------------------------------
  967. bool CMultiSource::IsTriggered( CBaseEntity * )
  968. {
  969. // Is everything triggered?
  970. int i = 0;
  971. // Still initializing?
  972. if ( m_spawnflags & SF_MULTI_INIT )
  973. return 0;
  974. while (i < m_iTotal)
  975. {
  976. if (m_rgTriggered[i] == 0)
  977. break;
  978. i++;
  979. }
  980. if (i == m_iTotal)
  981. {
  982. if ( !m_globalstate || GlobalEntity_GetState( m_globalstate ) == GLOBAL_ON )
  983. return 1;
  984. }
  985. return 0;
  986. }
  987. //-----------------------------------------------------------------------------
  988. // Purpose:
  989. //-----------------------------------------------------------------------------
  990. void CMultiSource::Register(void)
  991. {
  992. CBaseEntity *pTarget = NULL;
  993. m_iTotal = 0;
  994. memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) );
  995. SetThink(&CMultiSource::SUB_DoNothing);
  996. // search for all entities which target this multisource (m_iName)
  997. // dvsents2: port multisource to entity I/O!
  998. pTarget = gEntList.FindEntityByTarget( NULL, STRING(GetEntityName()) );
  999. while ( pTarget && (m_iTotal < MS_MAX_TARGETS) )
  1000. {
  1001. if ( pTarget )
  1002. m_rgEntities[m_iTotal++] = pTarget;
  1003. pTarget = gEntList.FindEntityByTarget( pTarget, STRING(GetEntityName()) );
  1004. }
  1005. pTarget = gEntList.FindEntityByClassname( NULL, "multi_manager" );
  1006. while (pTarget && (m_iTotal < MS_MAX_TARGETS))
  1007. {
  1008. if ( pTarget && pTarget->HasTarget(GetEntityName()) )
  1009. m_rgEntities[m_iTotal++] = pTarget;
  1010. pTarget = gEntList.FindEntityByClassname( pTarget, "multi_manager" );
  1011. }
  1012. m_spawnflags &= ~SF_MULTI_INIT;
  1013. }
  1014. //-----------------------------------------------------------------------------
  1015. // Purpose: Holds a value that can be added to and subtracted from.
  1016. //-----------------------------------------------------------------------------
  1017. class CMathCounter : public CLogicalEntity
  1018. {
  1019. DECLARE_CLASS( CMathCounter, CLogicalEntity );
  1020. private:
  1021. float m_flMin; // Minimum clamp value. If min and max are BOTH zero, no clamping is done.
  1022. float m_flMax; // Maximum clamp value.
  1023. bool m_bHitMin; // Set when we reach or go below our minimum value, cleared if we go above it again.
  1024. bool m_bHitMax; // Set when we reach or exceed our maximum value, cleared if we fall below it again.
  1025. bool m_bDisabled;
  1026. bool KeyValue(const char *szKeyName, const char *szValue);
  1027. void Spawn(void);
  1028. int DrawDebugTextOverlays(void);
  1029. void UpdateOutValue(CBaseEntity *pActivator, float fNewValue);
  1030. // Inputs
  1031. void InputAdd( inputdata_t &inputdata );
  1032. void InputDivide( inputdata_t &inputdata );
  1033. void InputMultiply( inputdata_t &inputdata );
  1034. void InputSetValue( inputdata_t &inputdata );
  1035. void InputSetValueNoFire( inputdata_t &inputdata );
  1036. void InputSubtract( inputdata_t &inputdata );
  1037. void InputSetHitMax( inputdata_t &inputdata );
  1038. void InputSetHitMin( inputdata_t &inputdata );
  1039. void InputGetValue( inputdata_t &inputdata );
  1040. void InputEnable( inputdata_t &inputdata );
  1041. void InputDisable( inputdata_t &inputdata );
  1042. // Outputs
  1043. COutputFloat m_OutValue;
  1044. COutputFloat m_OnGetValue; // Used for polling the counter value.
  1045. COutputEvent m_OnHitMin;
  1046. COutputEvent m_OnHitMax;
  1047. DECLARE_DATADESC();
  1048. };
  1049. LINK_ENTITY_TO_CLASS(math_counter, CMathCounter);
  1050. BEGIN_DATADESC( CMathCounter )
  1051. DEFINE_FIELD(m_bHitMax, FIELD_BOOLEAN),
  1052. DEFINE_FIELD(m_bHitMin, FIELD_BOOLEAN),
  1053. // Keys
  1054. DEFINE_KEYFIELD(m_flMin, FIELD_FLOAT, "min"),
  1055. DEFINE_KEYFIELD(m_flMax, FIELD_FLOAT, "max"),
  1056. DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  1057. // Inputs
  1058. DEFINE_INPUTFUNC(FIELD_FLOAT, "Add", InputAdd),
  1059. DEFINE_INPUTFUNC(FIELD_FLOAT, "Divide", InputDivide),
  1060. DEFINE_INPUTFUNC(FIELD_FLOAT, "Multiply", InputMultiply),
  1061. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue),
  1062. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueNoFire", InputSetValueNoFire),
  1063. DEFINE_INPUTFUNC(FIELD_FLOAT, "Subtract", InputSubtract),
  1064. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetHitMax", InputSetHitMax),
  1065. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetHitMin", InputSetHitMin),
  1066. DEFINE_INPUTFUNC(FIELD_VOID, "GetValue", InputGetValue),
  1067. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  1068. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  1069. // Outputs
  1070. DEFINE_OUTPUT(m_OutValue, "OutValue"),
  1071. DEFINE_OUTPUT(m_OnHitMin, "OnHitMin"),
  1072. DEFINE_OUTPUT(m_OnHitMax, "OnHitMax"),
  1073. DEFINE_OUTPUT(m_OnGetValue, "OnGetValue"),
  1074. END_DATADESC()
  1075. //-----------------------------------------------------------------------------
  1076. // Purpose: Handles key values from the BSP before spawn is called.
  1077. //-----------------------------------------------------------------------------
  1078. bool CMathCounter::KeyValue(const char *szKeyName, const char *szValue)
  1079. {
  1080. //
  1081. // Set the initial value of the counter.
  1082. //
  1083. if (!stricmp(szKeyName, "startvalue"))
  1084. {
  1085. m_OutValue.Init(atoi(szValue));
  1086. return(true);
  1087. }
  1088. return(BaseClass::KeyValue(szKeyName, szValue));
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Purpose: Called before spawning, after key values have been set.
  1092. //-----------------------------------------------------------------------------
  1093. void CMathCounter::Spawn( void )
  1094. {
  1095. //
  1096. // Make sure max and min are ordered properly or clamp won't work.
  1097. //
  1098. if (m_flMin > m_flMax)
  1099. {
  1100. float flTemp = m_flMax;
  1101. m_flMax = m_flMin;
  1102. m_flMin = flTemp;
  1103. }
  1104. //
  1105. // Clamp initial value to within the valid range.
  1106. //
  1107. if ((m_flMin != 0) || (m_flMax != 0))
  1108. {
  1109. float flStartValue = clamp(m_OutValue.Get(), m_flMin, m_flMax);
  1110. m_OutValue.Init(flStartValue);
  1111. }
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. // Purpose: Draw any debug text overlays
  1115. // Input :
  1116. // Output : Current text offset from the top
  1117. //-----------------------------------------------------------------------------
  1118. int CMathCounter::DrawDebugTextOverlays( void )
  1119. {
  1120. int text_offset = BaseClass::DrawDebugTextOverlays();
  1121. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1122. {
  1123. char tempstr[512];
  1124. Q_snprintf(tempstr,sizeof(tempstr)," min value: %f", m_flMin);
  1125. EntityText(text_offset,tempstr,0);
  1126. text_offset++;
  1127. Q_snprintf(tempstr,sizeof(tempstr)," max value: %f", m_flMax);
  1128. EntityText(text_offset,tempstr,0);
  1129. text_offset++;
  1130. Q_snprintf(tempstr,sizeof(tempstr),"current value: %f", m_OutValue.Get());
  1131. EntityText(text_offset,tempstr,0);
  1132. text_offset++;
  1133. if( m_bDisabled )
  1134. {
  1135. Q_snprintf(tempstr,sizeof(tempstr),"*DISABLED*");
  1136. }
  1137. else
  1138. {
  1139. Q_snprintf(tempstr,sizeof(tempstr),"Enabled.");
  1140. }
  1141. EntityText(text_offset,tempstr,0);
  1142. text_offset++;
  1143. }
  1144. return text_offset;
  1145. }
  1146. //-----------------------------------------------------------------------------
  1147. // Change min/max
  1148. //-----------------------------------------------------------------------------
  1149. void CMathCounter::InputSetHitMax( inputdata_t &inputdata )
  1150. {
  1151. m_flMax = inputdata.value.Float();
  1152. if ( m_flMax < m_flMin )
  1153. {
  1154. m_flMin = m_flMax;
  1155. }
  1156. UpdateOutValue( inputdata.pActivator, m_OutValue.Get() );
  1157. }
  1158. void CMathCounter::InputSetHitMin( inputdata_t &inputdata )
  1159. {
  1160. m_flMin = inputdata.value.Float();
  1161. if ( m_flMax < m_flMin )
  1162. {
  1163. m_flMax = m_flMin;
  1164. }
  1165. UpdateOutValue( inputdata.pActivator, m_OutValue.Get() );
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. // Purpose: Input handler for adding to the accumulator value.
  1169. // Input : Float value to add.
  1170. //-----------------------------------------------------------------------------
  1171. void CMathCounter::InputAdd( inputdata_t &inputdata )
  1172. {
  1173. if( m_bDisabled )
  1174. {
  1175. DevMsg("Math Counter %s ignoring ADD because it is disabled\n", GetDebugName() );
  1176. return;
  1177. }
  1178. float fNewValue = m_OutValue.Get() + inputdata.value.Float();
  1179. UpdateOutValue( inputdata.pActivator, fNewValue );
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Purpose: Input handler for multiplying the current value.
  1183. // Input : Float value to multiply the value by.
  1184. //-----------------------------------------------------------------------------
  1185. void CMathCounter::InputDivide( inputdata_t &inputdata )
  1186. {
  1187. if( m_bDisabled )
  1188. {
  1189. DevMsg("Math Counter %s ignoring DIVIDE because it is disabled\n", GetDebugName() );
  1190. return;
  1191. }
  1192. if (inputdata.value.Float() != 0)
  1193. {
  1194. float fNewValue = m_OutValue.Get() / inputdata.value.Float();
  1195. UpdateOutValue( inputdata.pActivator, fNewValue );
  1196. }
  1197. else
  1198. {
  1199. DevMsg( 1, "LEVEL DESIGN ERROR: Divide by zero in math_value\n" );
  1200. UpdateOutValue( inputdata.pActivator, m_OutValue.Get() );
  1201. }
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // Purpose: Input handler for multiplying the current value.
  1205. // Input : Float value to multiply the value by.
  1206. //-----------------------------------------------------------------------------
  1207. void CMathCounter::InputMultiply( inputdata_t &inputdata )
  1208. {
  1209. if( m_bDisabled )
  1210. {
  1211. DevMsg("Math Counter %s ignoring MULTIPLY because it is disabled\n", GetDebugName() );
  1212. return;
  1213. }
  1214. float fNewValue = m_OutValue.Get() * inputdata.value.Float();
  1215. UpdateOutValue( inputdata.pActivator, fNewValue );
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Purpose: Input handler for updating the value.
  1219. // Input : Float value to set.
  1220. //-----------------------------------------------------------------------------
  1221. void CMathCounter::InputSetValue( inputdata_t &inputdata )
  1222. {
  1223. if( m_bDisabled )
  1224. {
  1225. DevMsg("Math Counter %s ignoring SETVALUE because it is disabled\n", GetDebugName() );
  1226. return;
  1227. }
  1228. UpdateOutValue( inputdata.pActivator, inputdata.value.Float() );
  1229. }
  1230. //-----------------------------------------------------------------------------
  1231. // Purpose: Input handler for updating the value.
  1232. // Input : Float value to set.
  1233. //-----------------------------------------------------------------------------
  1234. void CMathCounter::InputSetValueNoFire( inputdata_t &inputdata )
  1235. {
  1236. if( m_bDisabled )
  1237. {
  1238. DevMsg("Math Counter %s ignoring SETVALUENOFIRE because it is disabled\n", GetDebugName() );
  1239. return;
  1240. }
  1241. float flNewValue = inputdata.value.Float();
  1242. if (( m_flMin != 0 ) || (m_flMax != 0 ))
  1243. {
  1244. flNewValue = clamp(flNewValue, m_flMin, m_flMax);
  1245. }
  1246. m_OutValue.Init( flNewValue );
  1247. }
  1248. //-----------------------------------------------------------------------------
  1249. // Purpose: Input handler for subtracting from the current value.
  1250. // Input : Float value to subtract.
  1251. //-----------------------------------------------------------------------------
  1252. void CMathCounter::InputSubtract( inputdata_t &inputdata )
  1253. {
  1254. if( m_bDisabled )
  1255. {
  1256. DevMsg("Math Counter %s ignoring SUBTRACT because it is disabled\n", GetDebugName() );
  1257. return;
  1258. }
  1259. float fNewValue = m_OutValue.Get() - inputdata.value.Float();
  1260. UpdateOutValue( inputdata.pActivator, fNewValue );
  1261. }
  1262. //-----------------------------------------------------------------------------
  1263. //-----------------------------------------------------------------------------
  1264. void CMathCounter::InputGetValue( inputdata_t &inputdata )
  1265. {
  1266. float flOutValue = m_OutValue.Get();
  1267. m_OnGetValue.Set( flOutValue, inputdata.pActivator, inputdata.pCaller );
  1268. }
  1269. //-----------------------------------------------------------------------------
  1270. //-----------------------------------------------------------------------------
  1271. void CMathCounter::InputEnable( inputdata_t &inputdata )
  1272. {
  1273. m_bDisabled = false;
  1274. }
  1275. //-----------------------------------------------------------------------------
  1276. //-----------------------------------------------------------------------------
  1277. void CMathCounter::InputDisable( inputdata_t &inputdata )
  1278. {
  1279. m_bDisabled = true;
  1280. }
  1281. //-----------------------------------------------------------------------------
  1282. // Purpose: Sets the value to the new value, clamping and firing the output value.
  1283. // Input : fNewValue - Value to set.
  1284. //-----------------------------------------------------------------------------
  1285. void CMathCounter::UpdateOutValue(CBaseEntity *pActivator, float fNewValue)
  1286. {
  1287. if ((m_flMin != 0) || (m_flMax != 0))
  1288. {
  1289. //
  1290. // Fire an output any time we reach or exceed our maximum value.
  1291. //
  1292. if ( fNewValue >= m_flMax )
  1293. {
  1294. if ( !m_bHitMax )
  1295. {
  1296. m_bHitMax = true;
  1297. m_OnHitMax.FireOutput( pActivator, this );
  1298. }
  1299. }
  1300. else
  1301. {
  1302. m_bHitMax = false;
  1303. }
  1304. //
  1305. // Fire an output any time we reach or go below our minimum value.
  1306. //
  1307. if ( fNewValue <= m_flMin )
  1308. {
  1309. if ( !m_bHitMin )
  1310. {
  1311. m_bHitMin = true;
  1312. m_OnHitMin.FireOutput( pActivator, this );
  1313. }
  1314. }
  1315. else
  1316. {
  1317. m_bHitMin = false;
  1318. }
  1319. fNewValue = clamp(fNewValue, m_flMin, m_flMax);
  1320. }
  1321. m_OutValue.Set(fNewValue, pActivator, this);
  1322. }
  1323. //-----------------------------------------------------------------------------
  1324. // Purpose: Compares a single string input to up to 16 case values, firing an
  1325. // output corresponding to the case value that matched, or a default
  1326. // output if the input value didn't match any of the case values.
  1327. //
  1328. // This can also be used to fire a random output from a set of outputs.
  1329. //-----------------------------------------------------------------------------
  1330. #define MAX_LOGIC_CASES 16
  1331. class CLogicCase : public CLogicalEntity
  1332. {
  1333. DECLARE_CLASS( CLogicCase, CLogicalEntity );
  1334. private:
  1335. string_t m_nCase[MAX_LOGIC_CASES];
  1336. int m_nShuffleCases;
  1337. int m_nLastShuffleCase;
  1338. unsigned char m_uchShuffleCaseMap[MAX_LOGIC_CASES];
  1339. void Spawn(void);
  1340. int BuildCaseMap(unsigned char *puchMap);
  1341. // Inputs
  1342. void InputValue( inputdata_t &inputdata );
  1343. void InputPickRandom( inputdata_t &inputdata );
  1344. void InputPickRandomShuffle( inputdata_t &inputdata );
  1345. // Outputs
  1346. COutputEvent m_OnCase[MAX_LOGIC_CASES]; // Fired when the input value matches one of the case values.
  1347. COutputVariant m_OnDefault; // Fired when no match was found.
  1348. DECLARE_DATADESC();
  1349. };
  1350. LINK_ENTITY_TO_CLASS(logic_case, CLogicCase);
  1351. BEGIN_DATADESC( CLogicCase )
  1352. // Silence, Classcheck!
  1353. // DEFINE_ARRAY( m_nCase, FIELD_STRING, MAX_LOGIC_CASES ),
  1354. // Keys
  1355. DEFINE_KEYFIELD(m_nCase[0], FIELD_STRING, "Case01"),
  1356. DEFINE_KEYFIELD(m_nCase[1], FIELD_STRING, "Case02"),
  1357. DEFINE_KEYFIELD(m_nCase[2], FIELD_STRING, "Case03"),
  1358. DEFINE_KEYFIELD(m_nCase[3], FIELD_STRING, "Case04"),
  1359. DEFINE_KEYFIELD(m_nCase[4], FIELD_STRING, "Case05"),
  1360. DEFINE_KEYFIELD(m_nCase[5], FIELD_STRING, "Case06"),
  1361. DEFINE_KEYFIELD(m_nCase[6], FIELD_STRING, "Case07"),
  1362. DEFINE_KEYFIELD(m_nCase[7], FIELD_STRING, "Case08"),
  1363. DEFINE_KEYFIELD(m_nCase[8], FIELD_STRING, "Case09"),
  1364. DEFINE_KEYFIELD(m_nCase[9], FIELD_STRING, "Case10"),
  1365. DEFINE_KEYFIELD(m_nCase[10], FIELD_STRING, "Case11"),
  1366. DEFINE_KEYFIELD(m_nCase[11], FIELD_STRING, "Case12"),
  1367. DEFINE_KEYFIELD(m_nCase[12], FIELD_STRING, "Case13"),
  1368. DEFINE_KEYFIELD(m_nCase[13], FIELD_STRING, "Case14"),
  1369. DEFINE_KEYFIELD(m_nCase[14], FIELD_STRING, "Case15"),
  1370. DEFINE_KEYFIELD(m_nCase[15], FIELD_STRING, "Case16"),
  1371. DEFINE_FIELD( m_nShuffleCases, FIELD_INTEGER ),
  1372. DEFINE_FIELD( m_nLastShuffleCase, FIELD_INTEGER ),
  1373. DEFINE_ARRAY( m_uchShuffleCaseMap, FIELD_CHARACTER, MAX_LOGIC_CASES ),
  1374. // Inputs
  1375. DEFINE_INPUTFUNC(FIELD_INPUT, "InValue", InputValue),
  1376. DEFINE_INPUTFUNC(FIELD_VOID, "PickRandom", InputPickRandom),
  1377. DEFINE_INPUTFUNC(FIELD_VOID, "PickRandomShuffle", InputPickRandomShuffle),
  1378. // Outputs
  1379. DEFINE_OUTPUT(m_OnCase[0], "OnCase01"),
  1380. DEFINE_OUTPUT(m_OnCase[1], "OnCase02"),
  1381. DEFINE_OUTPUT(m_OnCase[2], "OnCase03"),
  1382. DEFINE_OUTPUT(m_OnCase[3], "OnCase04"),
  1383. DEFINE_OUTPUT(m_OnCase[4], "OnCase05"),
  1384. DEFINE_OUTPUT(m_OnCase[5], "OnCase06"),
  1385. DEFINE_OUTPUT(m_OnCase[6], "OnCase07"),
  1386. DEFINE_OUTPUT(m_OnCase[7], "OnCase08"),
  1387. DEFINE_OUTPUT(m_OnCase[8], "OnCase09"),
  1388. DEFINE_OUTPUT(m_OnCase[9], "OnCase10"),
  1389. DEFINE_OUTPUT(m_OnCase[10], "OnCase11"),
  1390. DEFINE_OUTPUT(m_OnCase[11], "OnCase12"),
  1391. DEFINE_OUTPUT(m_OnCase[12], "OnCase13"),
  1392. DEFINE_OUTPUT(m_OnCase[13], "OnCase14"),
  1393. DEFINE_OUTPUT(m_OnCase[14], "OnCase15"),
  1394. DEFINE_OUTPUT(m_OnCase[15], "OnCase16"),
  1395. DEFINE_OUTPUT(m_OnDefault, "OnDefault"),
  1396. END_DATADESC()
  1397. //-----------------------------------------------------------------------------
  1398. // Purpose: Called before spawning, after key values have been set.
  1399. //-----------------------------------------------------------------------------
  1400. void CLogicCase::Spawn( void )
  1401. {
  1402. m_nLastShuffleCase = -1;
  1403. }
  1404. //-----------------------------------------------------------------------------
  1405. // Purpose: Evaluates the new input value, firing the appropriate OnCaseX output
  1406. // if the input value matches one of the "CaseX" keys.
  1407. // Input : Value - Variant value to compare against the values of the case fields.
  1408. // We use a variant so that we can convert any input type to a string.
  1409. //-----------------------------------------------------------------------------
  1410. void CLogicCase::InputValue( inputdata_t &inputdata )
  1411. {
  1412. const char *pszValue = inputdata.value.String();
  1413. for (int i = 0; i < MAX_LOGIC_CASES; i++)
  1414. {
  1415. if ((m_nCase[i] != NULL_STRING) && !stricmp(STRING(m_nCase[i]), pszValue))
  1416. {
  1417. m_OnCase[i].FireOutput( inputdata.pActivator, this );
  1418. return;
  1419. }
  1420. }
  1421. m_OnDefault.Set( inputdata.value, inputdata.pActivator, this );
  1422. }
  1423. //-----------------------------------------------------------------------------
  1424. // Count the number of valid cases, building a packed array
  1425. // that maps 0..NumCases to the actual CaseX values.
  1426. //
  1427. // This allows our zany mappers to set up cases sparsely if they desire.
  1428. // NOTE: assumes pnMap points to an array of MAX_LOGIC_CASES
  1429. //-----------------------------------------------------------------------------
  1430. int CLogicCase::BuildCaseMap(unsigned char *puchCaseMap)
  1431. {
  1432. memset(puchCaseMap, 0, sizeof(unsigned char) * MAX_LOGIC_CASES);
  1433. int nNumCases = 0;
  1434. for (int i = 0; i < MAX_LOGIC_CASES; i++)
  1435. {
  1436. if (m_OnCase[i].NumberOfElements() > 0)
  1437. {
  1438. puchCaseMap[nNumCases] = (unsigned char)i;
  1439. nNumCases++;
  1440. }
  1441. }
  1442. return nNumCases;
  1443. }
  1444. //-----------------------------------------------------------------------------
  1445. // Purpose: Makes the case statement choose a case at random.
  1446. //-----------------------------------------------------------------------------
  1447. void CLogicCase::InputPickRandom( inputdata_t &inputdata )
  1448. {
  1449. unsigned char uchCaseMap[MAX_LOGIC_CASES];
  1450. int nNumCases = BuildCaseMap( uchCaseMap );
  1451. //
  1452. // Choose a random case from the ones that were set up by the level designer.
  1453. //
  1454. if ( nNumCases > 0 )
  1455. {
  1456. int nRandom = random->RandomInt(0, nNumCases - 1);
  1457. int nCase = (unsigned char)uchCaseMap[nRandom];
  1458. Assert(nCase < MAX_LOGIC_CASES);
  1459. if (nCase < MAX_LOGIC_CASES)
  1460. {
  1461. m_OnCase[nCase].FireOutput( inputdata.pActivator, this );
  1462. }
  1463. }
  1464. else
  1465. {
  1466. DevMsg( 1, "Firing PickRandom input on logic_case %s with no cases set up\n", GetDebugName() );
  1467. }
  1468. }
  1469. //-----------------------------------------------------------------------------
  1470. // Purpose: Makes the case statement choose a case at random.
  1471. //-----------------------------------------------------------------------------
  1472. void CLogicCase::InputPickRandomShuffle( inputdata_t &inputdata )
  1473. {
  1474. int nAvoidCase = -1;
  1475. int nCaseCount = m_nShuffleCases;
  1476. if ( nCaseCount == 0 )
  1477. {
  1478. // Starting a new shuffle batch.
  1479. nCaseCount = m_nShuffleCases = BuildCaseMap( m_uchShuffleCaseMap );
  1480. if ( ( m_nShuffleCases > 1 ) && ( m_nLastShuffleCase != -1 ) )
  1481. {
  1482. // Remove the previously picked case from the case map for this pick only.
  1483. // This avoids repeats across shuffle batch boundaries.
  1484. nAvoidCase = m_nLastShuffleCase;
  1485. for (int i = 0; i < m_nShuffleCases; i++ )
  1486. {
  1487. if ( m_uchShuffleCaseMap[i] == nAvoidCase )
  1488. {
  1489. unsigned char uchSwap = m_uchShuffleCaseMap[i];
  1490. m_uchShuffleCaseMap[i] = m_uchShuffleCaseMap[nCaseCount - 1];
  1491. m_uchShuffleCaseMap[nCaseCount - 1] = uchSwap;
  1492. nCaseCount--;
  1493. break;
  1494. }
  1495. }
  1496. }
  1497. }
  1498. //
  1499. // Choose a random case from the ones that were set up by the level designer.
  1500. // Never repeat a case within a shuffle batch, nor consecutively across batches.
  1501. //
  1502. if ( nCaseCount > 0 )
  1503. {
  1504. int nRandom = random->RandomInt( 0, nCaseCount - 1 );
  1505. int nCase = m_uchShuffleCaseMap[nRandom];
  1506. Assert(nCase < MAX_LOGIC_CASES);
  1507. if (nCase < MAX_LOGIC_CASES)
  1508. {
  1509. m_OnCase[nCase].FireOutput( inputdata.pActivator, this );
  1510. }
  1511. m_uchShuffleCaseMap[nRandom] = m_uchShuffleCaseMap[m_nShuffleCases - 1];
  1512. m_nShuffleCases--;
  1513. m_nLastShuffleCase = nCase;
  1514. }
  1515. else
  1516. {
  1517. DevMsg( 1, "Firing PickRandom input on logic_case %s with no cases set up\n", GetDebugName() );
  1518. }
  1519. }
  1520. //-----------------------------------------------------------------------------
  1521. // Purpose: Compares a floating point input to a predefined value, firing an
  1522. // output to indicate the result of the comparison.
  1523. //-----------------------------------------------------------------------------
  1524. class CLogicCompare : public CLogicalEntity
  1525. {
  1526. DECLARE_CLASS( CLogicCompare, CLogicalEntity );
  1527. public:
  1528. int DrawDebugTextOverlays(void);
  1529. private:
  1530. // Inputs
  1531. void InputSetValue( inputdata_t &inputdata );
  1532. void InputSetValueCompare( inputdata_t &inputdata );
  1533. void InputSetCompareValue( inputdata_t &inputdata );
  1534. void InputCompare( inputdata_t &inputdata );
  1535. void DoCompare(CBaseEntity *pActivator, float flInValue);
  1536. float m_flInValue; // Place to hold the last input value for a recomparison.
  1537. float m_flCompareValue; // The value to compare the input value against.
  1538. // Outputs
  1539. COutputFloat m_OnLessThan; // Fired when the input value is less than the compare value.
  1540. COutputFloat m_OnEqualTo; // Fired when the input value is equal to the compare value.
  1541. COutputFloat m_OnNotEqualTo; // Fired when the input value is not equal to the compare value.
  1542. COutputFloat m_OnGreaterThan; // Fired when the input value is greater than the compare value.
  1543. DECLARE_DATADESC();
  1544. };
  1545. LINK_ENTITY_TO_CLASS(logic_compare, CLogicCompare);
  1546. BEGIN_DATADESC( CLogicCompare )
  1547. // Keys
  1548. DEFINE_KEYFIELD(m_flCompareValue, FIELD_FLOAT, "CompareValue"),
  1549. DEFINE_KEYFIELD(m_flInValue, FIELD_FLOAT, "InitialValue"),
  1550. // Inputs
  1551. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue),
  1552. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueCompare", InputSetValueCompare),
  1553. DEFINE_INPUTFUNC(FIELD_FLOAT, "SetCompareValue", InputSetCompareValue),
  1554. DEFINE_INPUTFUNC(FIELD_VOID, "Compare", InputCompare),
  1555. // Outputs
  1556. DEFINE_OUTPUT(m_OnEqualTo, "OnEqualTo"),
  1557. DEFINE_OUTPUT(m_OnNotEqualTo, "OnNotEqualTo"),
  1558. DEFINE_OUTPUT(m_OnGreaterThan, "OnGreaterThan"),
  1559. DEFINE_OUTPUT(m_OnLessThan, "OnLessThan"),
  1560. END_DATADESC()
  1561. //-----------------------------------------------------------------------------
  1562. // Purpose: Input handler for a new input value without performing a comparison.
  1563. //-----------------------------------------------------------------------------
  1564. void CLogicCompare::InputSetValue( inputdata_t &inputdata )
  1565. {
  1566. m_flInValue = inputdata.value.Float();
  1567. }
  1568. //-----------------------------------------------------------------------------
  1569. // Purpose: Input handler for a setting a new value and doing the comparison.
  1570. //-----------------------------------------------------------------------------
  1571. void CLogicCompare::InputSetValueCompare( inputdata_t &inputdata )
  1572. {
  1573. m_flInValue = inputdata.value.Float();
  1574. DoCompare( inputdata.pActivator, m_flInValue );
  1575. }
  1576. //-----------------------------------------------------------------------------
  1577. // Purpose: Input handler for a new input value without performing a comparison.
  1578. //-----------------------------------------------------------------------------
  1579. void CLogicCompare::InputSetCompareValue( inputdata_t &inputdata )
  1580. {
  1581. m_flCompareValue = inputdata.value.Float();
  1582. }
  1583. //-----------------------------------------------------------------------------
  1584. // Purpose: Input handler for forcing a recompare of the last input value.
  1585. //-----------------------------------------------------------------------------
  1586. void CLogicCompare::InputCompare( inputdata_t &inputdata )
  1587. {
  1588. DoCompare( inputdata.pActivator, m_flInValue );
  1589. }
  1590. //-----------------------------------------------------------------------------
  1591. // Purpose: Compares the input value to the compare value, firing the appropriate
  1592. // output(s) based on the comparison result.
  1593. // Input : flInValue - Value to compare against the comparison value.
  1594. //-----------------------------------------------------------------------------
  1595. void CLogicCompare::DoCompare(CBaseEntity *pActivator, float flInValue)
  1596. {
  1597. if (flInValue == m_flCompareValue)
  1598. {
  1599. m_OnEqualTo.Set(flInValue, pActivator, this);
  1600. }
  1601. else
  1602. {
  1603. m_OnNotEqualTo.Set(flInValue, pActivator, this);
  1604. if (flInValue > m_flCompareValue)
  1605. {
  1606. m_OnGreaterThan.Set(flInValue, pActivator, this);
  1607. }
  1608. else
  1609. {
  1610. m_OnLessThan.Set(flInValue, pActivator, this);
  1611. }
  1612. }
  1613. }
  1614. //-----------------------------------------------------------------------------
  1615. // Purpose: Draw any debug text overlays
  1616. // Output : Current text offset from the top
  1617. //-----------------------------------------------------------------------------
  1618. int CLogicCompare::DrawDebugTextOverlays( void )
  1619. {
  1620. int text_offset = BaseClass::DrawDebugTextOverlays();
  1621. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1622. {
  1623. char tempstr[512];
  1624. // print duration
  1625. Q_snprintf(tempstr,sizeof(tempstr)," Initial Value: %f", m_flInValue);
  1626. EntityText(text_offset,tempstr,0);
  1627. text_offset++;
  1628. // print hold time
  1629. Q_snprintf(tempstr,sizeof(tempstr)," Compare Value: %f", m_flCompareValue);
  1630. EntityText(text_offset,tempstr,0);
  1631. text_offset++;
  1632. }
  1633. return text_offset;
  1634. }
  1635. //-----------------------------------------------------------------------------
  1636. // Purpose: Tests a boolean value, firing an output to indicate whether the
  1637. // value was true or false.
  1638. //-----------------------------------------------------------------------------
  1639. class CLogicBranch : public CLogicalEntity
  1640. {
  1641. DECLARE_CLASS( CLogicBranch, CLogicalEntity );
  1642. public:
  1643. void UpdateOnRemove();
  1644. void AddLogicBranchListener( CBaseEntity *pEntity );
  1645. inline bool GetLogicBranchState();
  1646. virtual int DrawDebugTextOverlays( void );
  1647. private:
  1648. enum LogicBranchFire_t
  1649. {
  1650. LOGIC_BRANCH_FIRE,
  1651. LOGIC_BRANCH_NO_FIRE,
  1652. };
  1653. // Inputs
  1654. void InputSetValue( inputdata_t &inputdata );
  1655. void InputSetValueTest( inputdata_t &inputdata );
  1656. void InputToggle( inputdata_t &inputdata );
  1657. void InputToggleTest( inputdata_t &inputdata );
  1658. void InputTest( inputdata_t &inputdata );
  1659. void UpdateValue(bool bNewValue, CBaseEntity *pActivator, LogicBranchFire_t eFire);
  1660. bool m_bInValue; // Place to hold the last input value for a future test.
  1661. CUtlVector<EHANDLE> m_Listeners; // A list of logic_branch_listeners that are monitoring us.
  1662. // Outputs
  1663. COutputEvent m_OnTrue; // Fired when the value is true.
  1664. COutputEvent m_OnFalse; // Fired when the value is false.
  1665. DECLARE_DATADESC();
  1666. };
  1667. LINK_ENTITY_TO_CLASS(logic_branch, CLogicBranch);
  1668. BEGIN_DATADESC( CLogicBranch )
  1669. // Keys
  1670. DEFINE_KEYFIELD(m_bInValue, FIELD_BOOLEAN, "InitialValue"),
  1671. DEFINE_UTLVECTOR( m_Listeners, FIELD_EHANDLE ),
  1672. // Inputs
  1673. DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetValue", InputSetValue),
  1674. DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetValueTest", InputSetValueTest),
  1675. DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle),
  1676. DEFINE_INPUTFUNC(FIELD_VOID, "ToggleTest", InputToggleTest),
  1677. DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest),
  1678. // Outputs
  1679. DEFINE_OUTPUT(m_OnTrue, "OnTrue"),
  1680. DEFINE_OUTPUT(m_OnFalse, "OnFalse"),
  1681. END_DATADESC()
  1682. //-----------------------------------------------------------------------------
  1683. //-----------------------------------------------------------------------------
  1684. void CLogicBranch::UpdateOnRemove()
  1685. {
  1686. for ( int i = 0; i < m_Listeners.Count(); i++ )
  1687. {
  1688. CBaseEntity *pEntity = m_Listeners.Element( i ).Get();
  1689. if ( pEntity )
  1690. {
  1691. g_EventQueue.AddEvent( this, "_OnLogicBranchRemoved", 0, this, this );
  1692. }
  1693. }
  1694. BaseClass::UpdateOnRemove();
  1695. }
  1696. //-----------------------------------------------------------------------------
  1697. // Purpose: Input handler to set a new input value without firing outputs.
  1698. // Input : Boolean value to set.
  1699. //-----------------------------------------------------------------------------
  1700. void CLogicBranch::InputSetValue( inputdata_t &inputdata )
  1701. {
  1702. UpdateValue( inputdata.value.Bool(), inputdata.pActivator, LOGIC_BRANCH_NO_FIRE );
  1703. }
  1704. //-----------------------------------------------------------------------------
  1705. // Purpose: Input handler to set a new input value and fire appropriate outputs.
  1706. // Input : Boolean value to set.
  1707. //-----------------------------------------------------------------------------
  1708. void CLogicBranch::InputSetValueTest( inputdata_t &inputdata )
  1709. {
  1710. UpdateValue( inputdata.value.Bool(), inputdata.pActivator, LOGIC_BRANCH_FIRE );
  1711. }
  1712. //-----------------------------------------------------------------------------
  1713. // Purpose: Input handler for toggling the boolean value without firing outputs.
  1714. //-----------------------------------------------------------------------------
  1715. void CLogicBranch::InputToggle( inputdata_t &inputdata )
  1716. {
  1717. UpdateValue( !m_bInValue, inputdata.pActivator, LOGIC_BRANCH_NO_FIRE );
  1718. }
  1719. //-----------------------------------------------------------------------------
  1720. // Purpose: Input handler for toggling the boolean value and then firing the
  1721. // appropriate output based on the new value.
  1722. //-----------------------------------------------------------------------------
  1723. void CLogicBranch::InputToggleTest( inputdata_t &inputdata )
  1724. {
  1725. UpdateValue( !m_bInValue, inputdata.pActivator, LOGIC_BRANCH_FIRE );
  1726. }
  1727. //-----------------------------------------------------------------------------
  1728. // Purpose: Input handler for forcing a test of the last input value.
  1729. //-----------------------------------------------------------------------------
  1730. void CLogicBranch::InputTest( inputdata_t &inputdata )
  1731. {
  1732. UpdateValue( m_bInValue, inputdata.pActivator, LOGIC_BRANCH_FIRE );
  1733. }
  1734. //-----------------------------------------------------------------------------
  1735. // Purpose: Tests the last input value, firing the appropriate output based on
  1736. // the test result.
  1737. // Input : bInValue -
  1738. //-----------------------------------------------------------------------------
  1739. void CLogicBranch::UpdateValue( bool bNewValue, CBaseEntity *pActivator, LogicBranchFire_t eFire )
  1740. {
  1741. if ( m_bInValue != bNewValue )
  1742. {
  1743. m_bInValue = bNewValue;
  1744. for ( int i = 0; i < m_Listeners.Count(); i++ )
  1745. {
  1746. CBaseEntity *pEntity = m_Listeners.Element( i ).Get();
  1747. if ( pEntity )
  1748. {
  1749. g_EventQueue.AddEvent( pEntity, "_OnLogicBranchChanged", 0, this, this );
  1750. }
  1751. }
  1752. }
  1753. if ( eFire == LOGIC_BRANCH_FIRE )
  1754. {
  1755. if ( m_bInValue )
  1756. {
  1757. m_OnTrue.FireOutput( pActivator, this );
  1758. }
  1759. else
  1760. {
  1761. m_OnFalse.FireOutput( pActivator, this );
  1762. }
  1763. }
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. // Purpose: Accessor for logic_branchlist to test the value of the branch on demand.
  1767. //-----------------------------------------------------------------------------
  1768. bool CLogicBranch::GetLogicBranchState()
  1769. {
  1770. return m_bInValue;
  1771. }
  1772. //-----------------------------------------------------------------------------
  1773. //-----------------------------------------------------------------------------
  1774. void CLogicBranch::AddLogicBranchListener( CBaseEntity *pEntity )
  1775. {
  1776. if ( m_Listeners.Find( pEntity ) == -1 )
  1777. {
  1778. m_Listeners.AddToTail( pEntity );
  1779. }
  1780. }
  1781. //-----------------------------------------------------------------------------
  1782. // Purpose:
  1783. //-----------------------------------------------------------------------------
  1784. int CLogicBranch::DrawDebugTextOverlays( void )
  1785. {
  1786. int text_offset = BaseClass::DrawDebugTextOverlays();
  1787. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1788. {
  1789. char tempstr[512];
  1790. // print refire time
  1791. Q_snprintf( tempstr, sizeof(tempstr), "Branch value: %s", (m_bInValue) ? "TRUE" : "FALSE" );
  1792. EntityText( text_offset, tempstr, 0 );
  1793. text_offset++;
  1794. }
  1795. return text_offset;
  1796. }
  1797. //-----------------------------------------------------------------------------
  1798. // Purpose: Autosaves when triggered
  1799. //-----------------------------------------------------------------------------
  1800. class CLogicAutosave : public CLogicalEntity
  1801. {
  1802. DECLARE_CLASS( CLogicAutosave, CLogicalEntity );
  1803. protected:
  1804. // Inputs
  1805. void InputSave( inputdata_t &inputdata );
  1806. void InputSaveDangerous( inputdata_t &inputdata );
  1807. void InputSetMinHitpointsThreshold( inputdata_t &inputdata );
  1808. DECLARE_DATADESC();
  1809. bool m_bForceNewLevelUnit;
  1810. int m_minHitPoints;
  1811. int m_minHitPointsToCommit;
  1812. };
  1813. LINK_ENTITY_TO_CLASS(logic_autosave, CLogicAutosave);
  1814. BEGIN_DATADESC( CLogicAutosave )
  1815. DEFINE_KEYFIELD( m_bForceNewLevelUnit, FIELD_BOOLEAN, "NewLevelUnit" ),
  1816. DEFINE_KEYFIELD( m_minHitPoints, FIELD_INTEGER, "MinimumHitPoints" ),
  1817. DEFINE_KEYFIELD( m_minHitPointsToCommit, FIELD_INTEGER, "MinHitPointsToCommit" ),
  1818. // Inputs
  1819. DEFINE_INPUTFUNC( FIELD_VOID, "Save", InputSave ),
  1820. DEFINE_INPUTFUNC( FIELD_FLOAT, "SaveDangerous", InputSaveDangerous ),
  1821. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMinHitpointsThreshold", InputSetMinHitpointsThreshold ),
  1822. END_DATADESC()
  1823. //-----------------------------------------------------------------------------
  1824. // Purpose: Save!
  1825. //-----------------------------------------------------------------------------
  1826. void CLogicAutosave::InputSave( inputdata_t &inputdata )
  1827. {
  1828. if ( m_bForceNewLevelUnit )
  1829. {
  1830. engine->ClearSaveDir();
  1831. }
  1832. engine->ServerCommand( "autosave\n" );
  1833. }
  1834. //-----------------------------------------------------------------------------
  1835. // Purpose: Save safely!
  1836. //-----------------------------------------------------------------------------
  1837. void CLogicAutosave::InputSaveDangerous( inputdata_t &inputdata )
  1838. {
  1839. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  1840. if ( g_ServerGameDLL.m_fAutoSaveDangerousTime != 0.0f && g_ServerGameDLL.m_fAutoSaveDangerousTime >= gpGlobals->curtime )
  1841. {
  1842. // A previous dangerous auto save was waiting to become safe
  1843. if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
  1844. {
  1845. // The player isn't dead, so make the dangerous auto save safe
  1846. engine->ServerCommand( "autosavedangerousissafe\n" );
  1847. }
  1848. }
  1849. if ( m_bForceNewLevelUnit )
  1850. {
  1851. engine->ClearSaveDir();
  1852. }
  1853. if ( pPlayer->GetHealth() >= m_minHitPoints )
  1854. {
  1855. engine->ServerCommand( "autosavedangerous\n" );
  1856. g_ServerGameDLL.m_fAutoSaveDangerousTime = gpGlobals->curtime + inputdata.value.Float();
  1857. // Player must have this much health when we go to commit, or we don't commit.
  1858. g_ServerGameDLL.m_fAutoSaveDangerousMinHealthToCommit = m_minHitPointsToCommit;
  1859. }
  1860. }
  1861. //-----------------------------------------------------------------------------
  1862. // Purpose: Autosaves when triggered
  1863. //-----------------------------------------------------------------------------
  1864. class CLogicActiveAutosave : public CLogicAutosave
  1865. {
  1866. DECLARE_CLASS( CLogicActiveAutosave, CLogicAutosave );
  1867. void InputEnable( inputdata_t &inputdata )
  1868. {
  1869. m_flStartTime = -1;
  1870. SetThink( &CLogicActiveAutosave::SaveThink );
  1871. SetNextThink( gpGlobals->curtime );
  1872. }
  1873. void InputDisable( inputdata_t &inputdata )
  1874. {
  1875. SetThink( NULL );
  1876. }
  1877. void SaveThink()
  1878. {
  1879. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  1880. if ( pPlayer )
  1881. {
  1882. if ( m_flStartTime < 0 )
  1883. {
  1884. if ( pPlayer->GetHealth() <= m_minHitPoints )
  1885. {
  1886. m_flStartTime = gpGlobals->curtime;
  1887. }
  1888. }
  1889. else
  1890. {
  1891. if ( pPlayer->GetHealth() >= m_TriggerHitPoints )
  1892. {
  1893. inputdata_t inputdata;
  1894. DevMsg( 2, "logic_active_autosave (%s, %d) triggered\n", STRING( GetEntityName() ), entindex() );
  1895. if ( !m_flDangerousTime )
  1896. {
  1897. InputSave( inputdata );
  1898. }
  1899. else
  1900. {
  1901. inputdata.value.SetFloat( m_flDangerousTime );
  1902. InputSaveDangerous( inputdata );
  1903. }
  1904. m_flStartTime = -1;
  1905. }
  1906. else if ( m_flTimeToTrigger > 0 && gpGlobals->curtime - m_flStartTime > m_flTimeToTrigger )
  1907. {
  1908. m_flStartTime = -1;
  1909. }
  1910. }
  1911. }
  1912. float thinkInterval = ( m_flStartTime < 0 ) ? 1.0 : 0.5;
  1913. SetNextThink( gpGlobals->curtime + thinkInterval );
  1914. }
  1915. DECLARE_DATADESC();
  1916. int m_TriggerHitPoints;
  1917. float m_flTimeToTrigger;
  1918. float m_flStartTime;
  1919. float m_flDangerousTime;
  1920. };
  1921. LINK_ENTITY_TO_CLASS(logic_active_autosave, CLogicActiveAutosave);
  1922. BEGIN_DATADESC( CLogicActiveAutosave )
  1923. DEFINE_KEYFIELD( m_TriggerHitPoints, FIELD_INTEGER, "TriggerHitPoints" ),
  1924. DEFINE_KEYFIELD( m_flTimeToTrigger, FIELD_FLOAT, "TimeToTrigger" ),
  1925. DEFINE_KEYFIELD( m_flDangerousTime, FIELD_FLOAT, "DangerousTime" ),
  1926. DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
  1927. DEFINE_THINKFUNC( SaveThink ),
  1928. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  1929. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  1930. END_DATADESC()
  1931. //-----------------------------------------------------------------------------
  1932. // Purpose: Keyfield set func
  1933. //-----------------------------------------------------------------------------
  1934. void CLogicAutosave::InputSetMinHitpointsThreshold( inputdata_t &inputdata )
  1935. {
  1936. int setTo = inputdata.value.Int();
  1937. AssertMsg1(setTo >= 0 && setTo <= 100, "Tried to set autosave MinHitpointsThreshold to %d!\n", setTo);
  1938. m_minHitPoints = setTo;
  1939. }
  1940. // Finds the named physics object. If no name, returns the world
  1941. // If a name is specified and an object not found - errors are reported
  1942. IPhysicsObject *FindPhysicsObjectByNameOrWorld( string_t name, CBaseEntity *pErrorEntity )
  1943. {
  1944. if ( !name )
  1945. return g_PhysWorldObject;
  1946. IPhysicsObject *pPhysics = FindPhysicsObjectByName( name.ToCStr(), pErrorEntity );
  1947. if ( !pPhysics )
  1948. {
  1949. DevWarning("%s: can't find %s\n", pErrorEntity->GetClassname(), name.ToCStr());
  1950. }
  1951. return pPhysics;
  1952. }
  1953. class CLogicCollisionPair : public CLogicalEntity
  1954. {
  1955. DECLARE_CLASS( CLogicCollisionPair, CLogicalEntity );
  1956. public:
  1957. void EnableCollisions( bool bEnable )
  1958. {
  1959. IPhysicsObject *pPhysics0 = FindPhysicsObjectByNameOrWorld( m_nameAttach1, this );
  1960. IPhysicsObject *pPhysics1 = FindPhysicsObjectByNameOrWorld( m_nameAttach2, this );
  1961. // need two different objects to do anything
  1962. if ( pPhysics0 && pPhysics1 && pPhysics0 != pPhysics1 )
  1963. {
  1964. m_disabled = !bEnable;
  1965. m_succeeded = true;
  1966. if ( bEnable )
  1967. {
  1968. PhysEnableEntityCollisions( pPhysics0, pPhysics1 );
  1969. }
  1970. else
  1971. {
  1972. PhysDisableEntityCollisions( pPhysics0, pPhysics1 );
  1973. }
  1974. }
  1975. else
  1976. {
  1977. m_succeeded = false;
  1978. }
  1979. }
  1980. void Activate( void )
  1981. {
  1982. if ( m_disabled )
  1983. {
  1984. EnableCollisions( false );
  1985. }
  1986. BaseClass::Activate();
  1987. }
  1988. void InputDisableCollisions( inputdata_t &inputdata )
  1989. {
  1990. if ( m_succeeded && m_disabled )
  1991. return;
  1992. EnableCollisions( false );
  1993. }
  1994. void InputEnableCollisions( inputdata_t &inputdata )
  1995. {
  1996. if ( m_succeeded && !m_disabled )
  1997. return;
  1998. EnableCollisions( true );
  1999. }
  2000. // If Activate() becomes PostSpawn()
  2001. //void OnRestore() { Activate(); }
  2002. DECLARE_DATADESC();
  2003. private:
  2004. string_t m_nameAttach1;
  2005. string_t m_nameAttach2;
  2006. bool m_disabled;
  2007. bool m_succeeded;
  2008. };
  2009. BEGIN_DATADESC( CLogicCollisionPair )
  2010. DEFINE_KEYFIELD( m_nameAttach1, FIELD_STRING, "attach1" ),
  2011. DEFINE_KEYFIELD( m_nameAttach2, FIELD_STRING, "attach2" ),
  2012. DEFINE_KEYFIELD( m_disabled, FIELD_BOOLEAN, "startdisabled" ),
  2013. DEFINE_FIELD( m_succeeded, FIELD_BOOLEAN ),
  2014. // Inputs
  2015. DEFINE_INPUTFUNC( FIELD_VOID, "DisableCollisions", InputDisableCollisions ),
  2016. DEFINE_INPUTFUNC( FIELD_VOID, "EnableCollisions", InputEnableCollisions ),
  2017. END_DATADESC()
  2018. LINK_ENTITY_TO_CLASS( logic_collision_pair, CLogicCollisionPair );
  2019. //-----------------------------------------------------------------------------
  2020. //-----------------------------------------------------------------------------
  2021. #define MAX_LOGIC_BRANCH_NAMES 16
  2022. class CLogicBranchList : public CLogicalEntity
  2023. {
  2024. DECLARE_CLASS( CLogicBranchList, CLogicalEntity );
  2025. virtual void Spawn();
  2026. virtual void Activate();
  2027. virtual int DrawDebugTextOverlays( void );
  2028. private:
  2029. enum LogicBranchListenerLastState_t
  2030. {
  2031. LOGIC_BRANCH_LISTENER_NOT_INIT = 0,
  2032. LOGIC_BRANCH_LISTENER_ALL_TRUE,
  2033. LOGIC_BRANCH_LISTENER_ALL_FALSE,
  2034. LOGIC_BRANCH_LISTENER_MIXED,
  2035. };
  2036. void DoTest( CBaseEntity *pActivator );
  2037. string_t m_nLogicBranchNames[MAX_LOGIC_BRANCH_NAMES];
  2038. CUtlVector<EHANDLE> m_LogicBranchList;
  2039. LogicBranchListenerLastState_t m_eLastState;
  2040. // Inputs
  2041. void Input_OnLogicBranchRemoved( inputdata_t &inputdata );
  2042. void Input_OnLogicBranchChanged( inputdata_t &inputdata );
  2043. void InputTest( inputdata_t &inputdata );
  2044. // Outputs
  2045. COutputEvent m_OnAllTrue; // Fired when all the registered logic_branches become true.
  2046. COutputEvent m_OnAllFalse; // Fired when all the registered logic_branches become false.
  2047. COutputEvent m_OnMixed; // Fired when one of the registered logic branches changes, but not all are true or false.
  2048. DECLARE_DATADESC();
  2049. };
  2050. LINK_ENTITY_TO_CLASS(logic_branch_listener, CLogicBranchList);
  2051. BEGIN_DATADESC( CLogicBranchList )
  2052. // Silence, classcheck!
  2053. //DEFINE_ARRAY( m_nLogicBranchNames, FIELD_STRING, MAX_LOGIC_BRANCH_NAMES ),
  2054. // Keys
  2055. DEFINE_KEYFIELD( m_nLogicBranchNames[0], FIELD_STRING, "Branch01" ),
  2056. DEFINE_KEYFIELD( m_nLogicBranchNames[1], FIELD_STRING, "Branch02" ),
  2057. DEFINE_KEYFIELD( m_nLogicBranchNames[2], FIELD_STRING, "Branch03" ),
  2058. DEFINE_KEYFIELD( m_nLogicBranchNames[3], FIELD_STRING, "Branch04" ),
  2059. DEFINE_KEYFIELD( m_nLogicBranchNames[4], FIELD_STRING, "Branch05" ),
  2060. DEFINE_KEYFIELD( m_nLogicBranchNames[5], FIELD_STRING, "Branch06" ),
  2061. DEFINE_KEYFIELD( m_nLogicBranchNames[6], FIELD_STRING, "Branch07" ),
  2062. DEFINE_KEYFIELD( m_nLogicBranchNames[7], FIELD_STRING, "Branch08" ),
  2063. DEFINE_KEYFIELD( m_nLogicBranchNames[8], FIELD_STRING, "Branch09" ),
  2064. DEFINE_KEYFIELD( m_nLogicBranchNames[9], FIELD_STRING, "Branch10" ),
  2065. DEFINE_KEYFIELD( m_nLogicBranchNames[10], FIELD_STRING, "Branch11" ),
  2066. DEFINE_KEYFIELD( m_nLogicBranchNames[11], FIELD_STRING, "Branch12" ),
  2067. DEFINE_KEYFIELD( m_nLogicBranchNames[12], FIELD_STRING, "Branch13" ),
  2068. DEFINE_KEYFIELD( m_nLogicBranchNames[13], FIELD_STRING, "Branch14" ),
  2069. DEFINE_KEYFIELD( m_nLogicBranchNames[14], FIELD_STRING, "Branch15" ),
  2070. DEFINE_KEYFIELD( m_nLogicBranchNames[15], FIELD_STRING, "Branch16" ),
  2071. DEFINE_UTLVECTOR( m_LogicBranchList, FIELD_EHANDLE ),
  2072. DEFINE_FIELD( m_eLastState, FIELD_INTEGER ),
  2073. // Inputs
  2074. DEFINE_INPUTFUNC( FIELD_INPUT, "Test", InputTest ),
  2075. DEFINE_INPUTFUNC( FIELD_INPUT, "_OnLogicBranchChanged", Input_OnLogicBranchChanged ),
  2076. DEFINE_INPUTFUNC( FIELD_INPUT, "_OnLogicBranchRemoved", Input_OnLogicBranchRemoved ),
  2077. // Outputs
  2078. DEFINE_OUTPUT( m_OnAllTrue, "OnAllTrue" ),
  2079. DEFINE_OUTPUT( m_OnAllFalse, "OnAllFalse" ),
  2080. DEFINE_OUTPUT( m_OnMixed, "OnMixed" ),
  2081. END_DATADESC()
  2082. //-----------------------------------------------------------------------------
  2083. // Purpose: Called before spawning, after key values have been set.
  2084. //-----------------------------------------------------------------------------
  2085. void CLogicBranchList::Spawn( void )
  2086. {
  2087. }
  2088. //-----------------------------------------------------------------------------
  2089. // Finds all the logic_branches that we are monitoring and register ourselves with them.
  2090. //-----------------------------------------------------------------------------
  2091. void CLogicBranchList::Activate( void )
  2092. {
  2093. for ( int i = 0; i < MAX_LOGIC_BRANCH_NAMES; i++ )
  2094. {
  2095. CBaseEntity *pEntity = NULL;
  2096. while ( ( pEntity = gEntList.FindEntityGeneric( pEntity, STRING( m_nLogicBranchNames[i] ), this ) ) != NULL )
  2097. {
  2098. if ( FClassnameIs( pEntity, "logic_branch" ) )
  2099. {
  2100. CLogicBranch *pBranch = (CLogicBranch *)pEntity;
  2101. pBranch->AddLogicBranchListener( this );
  2102. m_LogicBranchList.AddToTail( pBranch );
  2103. }
  2104. else
  2105. {
  2106. DevWarning( "logic_branchlist %s refers to entity %s, which is not a logic_branch\n", GetDebugName(), pEntity->GetDebugName() );
  2107. }
  2108. }
  2109. }
  2110. BaseClass::Activate();
  2111. }
  2112. //-----------------------------------------------------------------------------
  2113. // Called when a monitored logic branch is deleted from the world, since that
  2114. // might affect our final result.
  2115. //-----------------------------------------------------------------------------
  2116. void CLogicBranchList::Input_OnLogicBranchRemoved( inputdata_t &inputdata )
  2117. {
  2118. int nIndex = m_LogicBranchList.Find( inputdata.pActivator );
  2119. if ( nIndex != -1 )
  2120. {
  2121. m_LogicBranchList.FastRemove( nIndex );
  2122. }
  2123. // See if this logic_branch's deletion affects the final result.
  2124. DoTest( inputdata.pActivator );
  2125. }
  2126. //-----------------------------------------------------------------------------
  2127. // Called when the value of a monitored logic branch changes.
  2128. //-----------------------------------------------------------------------------
  2129. void CLogicBranchList::Input_OnLogicBranchChanged( inputdata_t &inputdata )
  2130. {
  2131. DoTest( inputdata.pActivator );
  2132. }
  2133. //-----------------------------------------------------------------------------
  2134. // Input handler to manually test the monitored logic branches and fire the
  2135. // appropriate output.
  2136. //-----------------------------------------------------------------------------
  2137. void CLogicBranchList::InputTest( inputdata_t &inputdata )
  2138. {
  2139. // Force an output.
  2140. m_eLastState = LOGIC_BRANCH_LISTENER_NOT_INIT;
  2141. DoTest( inputdata.pActivator );
  2142. }
  2143. //-----------------------------------------------------------------------------
  2144. //-----------------------------------------------------------------------------
  2145. void CLogicBranchList::DoTest( CBaseEntity *pActivator )
  2146. {
  2147. bool bOneTrue = false;
  2148. bool bOneFalse = false;
  2149. for ( int i = 0; i < m_LogicBranchList.Count(); i++ )
  2150. {
  2151. CLogicBranch *pBranch = (CLogicBranch *)m_LogicBranchList.Element( i ).Get();
  2152. if ( pBranch && pBranch->GetLogicBranchState() )
  2153. {
  2154. bOneTrue = true;
  2155. }
  2156. else
  2157. {
  2158. bOneFalse = true;
  2159. }
  2160. }
  2161. // Only fire the output if the new result differs from the last result.
  2162. if ( bOneTrue && !bOneFalse )
  2163. {
  2164. if ( m_eLastState != LOGIC_BRANCH_LISTENER_ALL_TRUE )
  2165. {
  2166. m_OnAllTrue.FireOutput( pActivator, this );
  2167. m_eLastState = LOGIC_BRANCH_LISTENER_ALL_TRUE;
  2168. }
  2169. }
  2170. else if ( bOneFalse && !bOneTrue )
  2171. {
  2172. if ( m_eLastState != LOGIC_BRANCH_LISTENER_ALL_FALSE )
  2173. {
  2174. m_OnAllFalse.FireOutput( pActivator, this );
  2175. m_eLastState = LOGIC_BRANCH_LISTENER_ALL_FALSE;
  2176. }
  2177. }
  2178. else
  2179. {
  2180. if ( m_eLastState != LOGIC_BRANCH_LISTENER_MIXED )
  2181. {
  2182. m_OnMixed.FireOutput( pActivator, this );
  2183. m_eLastState = LOGIC_BRANCH_LISTENER_MIXED;
  2184. }
  2185. }
  2186. }
  2187. //-----------------------------------------------------------------------------
  2188. // Purpose:
  2189. //-----------------------------------------------------------------------------
  2190. int CLogicBranchList::DrawDebugTextOverlays( void )
  2191. {
  2192. int text_offset = BaseClass::DrawDebugTextOverlays();
  2193. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  2194. {
  2195. char tempstr[512];
  2196. for ( int i = 0; i < m_LogicBranchList.Count(); i++ )
  2197. {
  2198. CLogicBranch *pBranch = (CLogicBranch *)m_LogicBranchList.Element( i ).Get();
  2199. if ( pBranch )
  2200. {
  2201. Q_snprintf( tempstr, sizeof(tempstr), "Branch (%s): %s", STRING(pBranch->GetEntityName()), (pBranch->GetLogicBranchState()) ? "TRUE" : "FALSE" );
  2202. EntityText( text_offset, tempstr, 0 );
  2203. text_offset++;
  2204. }
  2205. }
  2206. }
  2207. return text_offset;
  2208. }