Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3251 lines
96 KiB

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