diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/logicentities.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/logicentities.cpp')
| -rw-r--r-- | mp/src/game/server/logicentities.cpp | 5496 |
1 files changed, 2748 insertions, 2748 deletions
diff --git a/mp/src/game/server/logicentities.cpp b/mp/src/game/server/logicentities.cpp index a53298b0..cc8e6b73 100644 --- a/mp/src/game/server/logicentities.cpp +++ b/mp/src/game/server/logicentities.cpp @@ -1,2748 +1,2748 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Implements many of the entities that control logic flow within a map.
-//
-//=============================================================================
-
-#include "cbase.h"
-#include "entityinput.h"
-#include "entityoutput.h"
-#include "eventqueue.h"
-#include "mathlib/mathlib.h"
-#include "globalstate.h"
-#include "ndebugoverlay.h"
-#include "saverestore_utlvector.h"
-#include "vstdlib/random.h"
-#include "gameinterface.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-extern CServerGameDLL g_ServerGameDLL;
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Compares a set of integer inputs to the one main input
-// Outputs true if they are all equivalant, false otherwise
-//-----------------------------------------------------------------------------
-class CLogicCompareInteger : public CLogicalEntity
-{
-public:
- DECLARE_CLASS( CLogicCompareInteger, CLogicalEntity );
-
- // outputs
- COutputEvent m_OnEqual;
- COutputEvent m_OnNotEqual;
-
- // data
- int m_iIntegerValue;
- int m_iShouldCompareToValue;
-
- DECLARE_DATADESC();
-
- CMultiInputVar m_AllIntCompares;
-
- // Input handlers
- void InputValue( inputdata_t &inputdata );
- void InputCompareValues( inputdata_t &inputdata );
-};
-
-
-LINK_ENTITY_TO_CLASS( logic_multicompare, CLogicCompareInteger );
-
-
-BEGIN_DATADESC( CLogicCompareInteger )
-
- DEFINE_OUTPUT( m_OnEqual, "OnEqual" ),
- DEFINE_OUTPUT( m_OnNotEqual, "OnNotEqual" ),
-
- DEFINE_KEYFIELD( m_iIntegerValue, FIELD_INTEGER, "IntegerValue" ),
- DEFINE_KEYFIELD( m_iShouldCompareToValue, FIELD_INTEGER, "ShouldComparetoValue" ),
-
- DEFINE_FIELD( m_AllIntCompares, FIELD_INPUT ),
-
- DEFINE_INPUTFUNC( FIELD_INPUT, "InputValue", InputValue ),
- DEFINE_INPUTFUNC( FIELD_INPUT, "CompareValues", InputCompareValues ),
-
-END_DATADESC()
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Adds to the list of compared values
-//-----------------------------------------------------------------------------
-void CLogicCompareInteger::InputValue( inputdata_t &inputdata )
-{
- // make sure it's an int, if it can't be converted just throw it away
- if ( !inputdata.value.Convert(FIELD_INTEGER) )
- return;
-
- // update the value list with the new value
- m_AllIntCompares.AddValue( inputdata.value, inputdata.nOutputID );
-
- // if we haven't already this frame, send a message to ourself to update and fire
- if ( !m_AllIntCompares.m_bUpdatedThisFrame )
- {
- // TODO: need to add this event with a lower priority, so it gets called after all inputs have arrived
- g_EventQueue.AddEvent( this, "CompareValues", 0, inputdata.pActivator, this, inputdata.nOutputID );
- m_AllIntCompares.m_bUpdatedThisFrame = TRUE;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Forces a recompare
-//-----------------------------------------------------------------------------
-void CLogicCompareInteger::InputCompareValues( inputdata_t &inputdata )
-{
- m_AllIntCompares.m_bUpdatedThisFrame = FALSE;
-
- // loop through all the values comparing them
- int value = m_iIntegerValue;
- CMultiInputVar::inputitem_t *input = m_AllIntCompares.m_InputList;
-
- if ( !m_iShouldCompareToValue && input )
- {
- value = input->value.Int();
- }
-
- while ( input )
- {
- if ( input->value.Int() != value )
- {
- // false
- m_OnNotEqual.FireOutput( inputdata.pActivator, this );
- return;
- }
-
- input = input->next;
- }
-
- // true! all values equal
- m_OnEqual.FireOutput( inputdata.pActivator, this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Timer entity. Fires an output at regular or random intervals.
-//-----------------------------------------------------------------------------
-//
-// Spawnflags and others constants.
-//
-const int SF_TIMER_UPDOWN = 1;
-const float LOGIC_TIMER_MIN_INTERVAL = 0.01;
-
-
-class CTimerEntity : public CLogicalEntity
-{
-public:
- DECLARE_CLASS( CTimerEntity, CLogicalEntity );
-
- void Spawn( void );
- void Think( void );
-
- void Toggle( void );
- void Enable( void );
- void Disable( void );
- void FireTimer( void );
-
- int DrawDebugTextOverlays(void);
-
- // outputs
- COutputEvent m_OnTimer;
- COutputEvent m_OnTimerHigh;
- COutputEvent m_OnTimerLow;
-
- // inputs
- void InputToggle( inputdata_t &inputdata );
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
- void InputFireTimer( inputdata_t &inputdata );
- void InputRefireTime( inputdata_t &inputdata );
- void InputResetTimer( inputdata_t &inputdata );
- void InputAddToTimer( inputdata_t &inputdata );
- void InputSubtractFromTimer( inputdata_t &inputdata );
-
- int m_iDisabled;
- float m_flRefireTime;
- bool m_bUpDownState;
- int m_iUseRandomTime;
- float m_flLowerRandomBound;
- float m_flUpperRandomBound;
-
- // methods
- void ResetTimer( void );
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS( logic_timer, CTimerEntity );
-
-
-BEGIN_DATADESC( CTimerEntity )
-
- // Keys
- DEFINE_KEYFIELD( m_iDisabled, FIELD_INTEGER, "StartDisabled" ),
- DEFINE_KEYFIELD( m_flRefireTime, FIELD_FLOAT, "RefireTime" ),
-
- DEFINE_FIELD( m_bUpDownState, FIELD_BOOLEAN ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_FLOAT, "RefireTime", InputRefireTime ),
- DEFINE_INPUTFUNC( FIELD_VOID, "FireTimer", InputFireTimer ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "AddToTimer", InputAddToTimer ),
- DEFINE_INPUTFUNC( FIELD_VOID, "ResetTimer", InputResetTimer ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "SubtractFromTimer", InputSubtractFromTimer ),
-
- DEFINE_INPUT( m_iUseRandomTime, FIELD_INTEGER, "UseRandomTime" ),
- DEFINE_INPUT( m_flLowerRandomBound, FIELD_FLOAT, "LowerRandomBound" ),
- DEFINE_INPUT( m_flUpperRandomBound, FIELD_FLOAT, "UpperRandomBound" ),
-
-
- // Outputs
- DEFINE_OUTPUT( m_OnTimer, "OnTimer" ),
- DEFINE_OUTPUT( m_OnTimerHigh, "OnTimerHigh" ),
- DEFINE_OUTPUT( m_OnTimerLow, "OnTimerLow" ),
-
-END_DATADESC()
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::Spawn( void )
-{
- if (!m_iUseRandomTime && (m_flRefireTime < LOGIC_TIMER_MIN_INTERVAL))
- {
- m_flRefireTime = LOGIC_TIMER_MIN_INTERVAL;
- }
-
- if ( !m_iDisabled && (m_flRefireTime > 0 || m_iUseRandomTime) )
- {
- Enable();
- }
- else
- {
- Disable();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::Think( void )
-{
- FireTimer();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the time the timerentity will next fire
-//-----------------------------------------------------------------------------
-void CTimerEntity::ResetTimer( void )
-{
- if ( m_iDisabled )
- return;
-
- if ( m_iUseRandomTime )
- {
- m_flRefireTime = random->RandomFloat( m_flLowerRandomBound, m_flUpperRandomBound );
- }
-
- SetNextThink( gpGlobals->curtime + m_flRefireTime );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::Enable( void )
-{
- m_iDisabled = FALSE;
- ResetTimer();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::Disable( void )
-{
- m_iDisabled = TRUE;
- SetNextThink( TICK_NEVER_THINK );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::Toggle( void )
-{
- if ( m_iDisabled )
- {
- Enable();
- }
- else
- {
- Disable();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::FireTimer( void )
-{
- if ( !m_iDisabled )
- {
- //
- // Up/down timers alternate between two outputs.
- //
- if (m_spawnflags & SF_TIMER_UPDOWN)
- {
- if (m_bUpDownState)
- {
- m_OnTimerHigh.FireOutput( this, this );
- }
- else
- {
- m_OnTimerLow.FireOutput( this, this );
- }
-
- m_bUpDownState = !m_bUpDownState;
- }
- //
- // Regular timers only fire a single output.
- //
- else
- {
- m_OnTimer.FireOutput( this, this );
- }
-
- ResetTimer();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputEnable( inputdata_t &inputdata )
-{
- Enable();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputDisable( inputdata_t &inputdata )
-{
- Disable();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputToggle( inputdata_t &inputdata )
-{
- Toggle();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputFireTimer( inputdata_t &inputdata )
-{
- FireTimer();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Changes the time interval between timer fires
-// Resets the next firing to be time + newRefireTime
-// Input : Float refire frequency in seconds.
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputRefireTime( inputdata_t &inputdata )
-{
- float flRefireInterval = inputdata.value.Float();
-
- if ( flRefireInterval < LOGIC_TIMER_MIN_INTERVAL)
- {
- flRefireInterval = LOGIC_TIMER_MIN_INTERVAL;
- }
-
- if (m_flRefireTime != flRefireInterval )
- {
- m_flRefireTime = flRefireInterval;
- ResetTimer();
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputResetTimer( inputdata_t &inputdata )
-{
- // don't reset the timer if it isn't enabled
- if ( m_iDisabled )
- return;
-
- ResetTimer();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Adds to the time interval if the timer is enabled
-// Input : Float time to add in seconds
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputAddToTimer( inputdata_t &inputdata )
-{
- // don't add time if the timer isn't enabled
- if ( m_iDisabled )
- return;
-
- // Add time to timer
- float flNextThink = GetNextThink();
- SetNextThink( flNextThink += inputdata.value.Float() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Subtract from the time interval if the timer is enabled
-// Input : Float time to subtract in seconds
-//-----------------------------------------------------------------------------
-void CTimerEntity::InputSubtractFromTimer( inputdata_t &inputdata )
-{
- // don't add time if the timer isn't enabled
- if ( m_iDisabled )
- return;
-
- // Subtract time from the timer but don't let the timer go negative
- float flNextThink = GetNextThink();
- if ( ( flNextThink - gpGlobals->curtime ) <= inputdata.value.Float() )
- {
- SetNextThink( gpGlobals->curtime );
- }
- else
- {
- SetNextThink( flNextThink -= inputdata.value.Float() );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw any debug text overlays
-// Output : Current text offset from the top
-//-----------------------------------------------------------------------------
-int CTimerEntity::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- // print refire time
- Q_snprintf(tempstr,sizeof(tempstr),"refire interval: %.2f sec", m_flRefireTime);
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- // print seconds to next fire
- if ( !m_iDisabled )
- {
- float flNextThink = GetNextThink();
- Q_snprintf( tempstr, sizeof( tempstr ), " firing in: %.2f sec", flNextThink - gpGlobals->curtime );
- EntityText( text_offset, tempstr, 0);
- text_offset++;
- }
- }
- return text_offset;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Computes a line between two entities
-//-----------------------------------------------------------------------------
-class CLogicLineToEntity : public CLogicalEntity
-{
-public:
- DECLARE_CLASS( CLogicLineToEntity, CLogicalEntity );
-
- void Activate(void);
- void Spawn( void );
- void Think( void );
-
- // outputs
- COutputVector m_Line;
-
- DECLARE_DATADESC();
-
-private:
- string_t m_SourceName;
- EHANDLE m_StartEntity;
- EHANDLE m_EndEntity;
-};
-
-LINK_ENTITY_TO_CLASS( logic_lineto, CLogicLineToEntity );
-
-
-BEGIN_DATADESC( CLogicLineToEntity )
-
- // Keys
- // target is handled in the base class, stored in field m_target
- DEFINE_KEYFIELD( m_SourceName, FIELD_STRING, "source" ),
- DEFINE_FIELD( m_StartEntity, FIELD_EHANDLE ),
- DEFINE_FIELD( m_EndEntity, FIELD_EHANDLE ),
-
- // Outputs
- DEFINE_OUTPUT( m_Line, "Line" ),
-
-END_DATADESC()
-
-
-
-//-----------------------------------------------------------------------------
-// Find the entities
-//-----------------------------------------------------------------------------
-void CLogicLineToEntity::Activate(void)
-{
- BaseClass::Activate();
-
- if (m_target != NULL_STRING)
- {
- m_EndEntity = gEntList.FindEntityByName( NULL, m_target );
-
- //
- // If we were given a bad measure target, just measure sound where we are.
- //
- if ((m_EndEntity == NULL) || (m_EndEntity->edict() == NULL))
- {
- Warning( "logic_lineto - Target not found or target with no origin!\n");
- m_EndEntity = this;
- }
- }
- else
- {
- m_EndEntity = this;
- }
-
- if (m_SourceName != NULL_STRING)
- {
- m_StartEntity = gEntList.FindEntityByName( NULL, m_SourceName );
-
- //
- // If we were given a bad measure target, just measure sound where we are.
- //
- if ((m_StartEntity == NULL) || (m_StartEntity->edict() == NULL))
- {
- Warning( "logic_lineto - Source not found or source with no origin!\n");
- m_StartEntity = this;
- }
- }
- else
- {
- m_StartEntity = this;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Find the entities
-//-----------------------------------------------------------------------------
-void CLogicLineToEntity::Spawn(void)
-{
- SetNextThink( gpGlobals->curtime + 0.01f );
-}
-
-
-//-----------------------------------------------------------------------------
-// Find the entities
-//-----------------------------------------------------------------------------
-void CLogicLineToEntity::Think(void)
-{
- CBaseEntity* pDest = m_EndEntity.Get();
- CBaseEntity* pSrc = m_StartEntity.Get();
- if (!pDest || !pSrc || !pDest->edict() || !pSrc->edict())
- {
- // Can sleep for a long time, no more lines.
- m_Line.Set( vec3_origin, this, this );
- SetNextThink( gpGlobals->curtime + 10 );
- return;
- }
-
- Vector delta;
- VectorSubtract( pDest->GetAbsOrigin(), pSrc->GetAbsOrigin(), delta );
- m_Line.Set(delta, this, this);
-
- SetNextThink( gpGlobals->curtime + 0.01f );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Remaps a given input range to an output range.
-//-----------------------------------------------------------------------------
-const int SF_MATH_REMAP_IGNORE_OUT_OF_RANGE = 1;
-const int SF_MATH_REMAP_CLAMP_OUTPUT_TO_RANGE = 2;
-
-class CMathRemap : public CLogicalEntity
-{
-public:
-
- DECLARE_CLASS( CMathRemap, CLogicalEntity );
-
- void Spawn(void);
-
- // Keys
- float m_flInMin;
- float m_flInMax;
- float m_flOut1; // Output value when input is m_fInMin
- float m_flOut2; // Output value when input is m_fInMax
-
- bool m_bEnabled;
-
- // Inputs
- void InputValue( inputdata_t &inputdata );
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
- // Outputs
- COutputFloat m_OutValue;
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(math_remap, CMathRemap);
-
-
-BEGIN_DATADESC( CMathRemap )
-
- DEFINE_INPUTFUNC(FIELD_FLOAT, "InValue", InputValue ),
-
- DEFINE_OUTPUT(m_OutValue, "OutValue"),
-
- DEFINE_KEYFIELD(m_flInMin, FIELD_FLOAT, "in1"),
- DEFINE_KEYFIELD(m_flInMax, FIELD_FLOAT, "in2"),
- DEFINE_KEYFIELD(m_flOut1, FIELD_FLOAT, "out1"),
- DEFINE_KEYFIELD(m_flOut2, FIELD_FLOAT, "out2"),
-
- DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
-END_DATADESC()
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMathRemap::Spawn(void)
-{
- //
- // Avoid a divide by zero in ValueChanged.
- //
- if (m_flInMin == m_flInMax)
- {
- m_flInMin = 0;
- m_flInMax = 1;
- }
-
- //
- // Make sure min and max are set properly relative to one another.
- //
- if (m_flInMin > m_flInMax)
- {
- float flTemp = m_flInMin;
- m_flInMin = m_flInMax;
- m_flInMax = flTemp;
- }
-
- m_bEnabled = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMathRemap::InputEnable( inputdata_t &inputdata )
-{
- m_bEnabled = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMathRemap::InputDisable( inputdata_t &inputdata )
-{
- m_bEnabled = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler that is called when the input value changes.
-//-----------------------------------------------------------------------------
-void CMathRemap::InputValue( inputdata_t &inputdata )
-{
- float flValue = inputdata.value.Float();
-
- //
- // Disallow out-of-range input values to avoid out-of-range output values.
- //
- float flClampValue = clamp(flValue, m_flInMin, m_flInMax);
-
- if ((flClampValue == flValue) || !FBitSet(m_spawnflags, SF_MATH_REMAP_IGNORE_OUT_OF_RANGE))
- {
- //
- // Remap the input value to the desired output range and update the output.
- //
- float flRemappedValue = m_flOut1 + (((flValue - m_flInMin) * (m_flOut2 - m_flOut1)) / (m_flInMax - m_flInMin));
-
- if ( FBitSet( m_spawnflags, SF_MATH_REMAP_CLAMP_OUTPUT_TO_RANGE ) )
- {
- flRemappedValue = clamp( flRemappedValue, m_flOut1, m_flOut2 );
- }
-
- if ( m_bEnabled == true )
- {
- m_OutValue.Set(flRemappedValue, inputdata.pActivator, this);
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Remaps a given input range to an output range.
-//-----------------------------------------------------------------------------
-const int SF_COLOR_BLEND_IGNORE_OUT_OF_RANGE = 1;
-
-class CMathColorBlend : public CLogicalEntity
-{
-public:
-
- DECLARE_CLASS( CMathColorBlend, CLogicalEntity );
-
- void Spawn(void);
-
- // Keys
- float m_flInMin;
- float m_flInMax;
- color32 m_OutColor1; // Output color when input is m_fInMin
- color32 m_OutColor2; // Output color when input is m_fInMax
-
- // Inputs
- void InputValue( inputdata_t &inputdata );
-
- // Outputs
- COutputColor32 m_OutValue;
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(math_colorblend, CMathColorBlend);
-
-
-BEGIN_DATADESC( CMathColorBlend )
-
- DEFINE_INPUTFUNC(FIELD_FLOAT, "InValue", InputValue ),
-
- DEFINE_OUTPUT(m_OutValue, "OutColor"),
-
- DEFINE_KEYFIELD(m_flInMin, FIELD_FLOAT, "inmin"),
- DEFINE_KEYFIELD(m_flInMax, FIELD_FLOAT, "inmax"),
- DEFINE_KEYFIELD(m_OutColor1, FIELD_COLOR32, "colormin"),
- DEFINE_KEYFIELD(m_OutColor2, FIELD_COLOR32, "colormax"),
-
-END_DATADESC()
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMathColorBlend::Spawn(void)
-{
- //
- // Avoid a divide by zero in ValueChanged.
- //
- if (m_flInMin == m_flInMax)
- {
- m_flInMin = 0;
- m_flInMax = 1;
- }
-
- //
- // Make sure min and max are set properly relative to one another.
- //
- if (m_flInMin > m_flInMax)
- {
- float flTemp = m_flInMin;
- m_flInMin = m_flInMax;
- m_flInMax = flTemp;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler that is called when the input value changes.
-//-----------------------------------------------------------------------------
-void CMathColorBlend::InputValue( inputdata_t &inputdata )
-{
- float flValue = inputdata.value.Float();
-
- //
- // Disallow out-of-range input values to avoid out-of-range output values.
- //
- float flClampValue = clamp(flValue, m_flInMin, m_flInMax);
- if ((flClampValue == flValue) || !FBitSet(m_spawnflags, SF_COLOR_BLEND_IGNORE_OUT_OF_RANGE))
- {
- //
- // Remap the input value to the desired output color and update the output.
- //
- color32 Color;
- Color.r = m_OutColor1.r + (((flClampValue - m_flInMin) * (m_OutColor2.r - m_OutColor1.r)) / (m_flInMax - m_flInMin));
- Color.g = m_OutColor1.g + (((flClampValue - m_flInMin) * (m_OutColor2.g - m_OutColor1.g)) / (m_flInMax - m_flInMin));
- Color.b = m_OutColor1.b + (((flClampValue - m_flInMin) * (m_OutColor2.b - m_OutColor1.b)) / (m_flInMax - m_flInMin));
- Color.a = m_OutColor1.a + (((flClampValue - m_flInMin) * (m_OutColor2.a - m_OutColor1.a)) / (m_flInMax - m_flInMin));
-
- m_OutValue.Set(Color, inputdata.pActivator, this);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Console command to set the state of a global
-//-----------------------------------------------------------------------------
-void CC_Global_Set( const CCommand &args )
-{
- const char *szGlobal = args[1];
- const char *szState = args[2];
-
- if ( szGlobal == NULL || szState == NULL )
- {
- Msg( "Usage: global_set <globalname> <state>: Sets the state of the given env_global (0 = OFF, 1 = ON, 2 = DEAD).\n" );
- return;
- }
-
- int nState = atoi( szState );
-
- int nIndex = GlobalEntity_GetIndex( szGlobal );
-
- if ( nIndex >= 0 )
- {
- GlobalEntity_SetState( nIndex, ( GLOBALESTATE )nState );
- }
- else
- {
- GlobalEntity_Add( szGlobal, STRING( gpGlobals->mapname ), ( GLOBALESTATE )nState );
- }
-}
-
-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 );
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Holds a global state that can be queried by other entities to change
-// their behavior, such as "predistaster".
-//-----------------------------------------------------------------------------
-const int SF_GLOBAL_SET = 1; // Set global state to initial state on spawn
-
-class CEnvGlobal : public CLogicalEntity
-{
-public:
- DECLARE_CLASS( CEnvGlobal, CLogicalEntity );
-
- void Spawn( void );
-
- // Input handlers
- void InputTurnOn( inputdata_t &inputdata );
- void InputTurnOff( inputdata_t &inputdata );
- void InputRemove( inputdata_t &inputdata );
- void InputToggle( inputdata_t &inputdata );
- void InputSetCounter( inputdata_t &inputdata );
- void InputAddToCounter( inputdata_t &inputdata );
- void InputGetCounter( inputdata_t &inputdata );
-
- int DrawDebugTextOverlays(void);
-
- DECLARE_DATADESC();
-
- COutputInt m_outCounter;
-
- string_t m_globalstate;
- int m_triggermode;
- int m_initialstate;
- int m_counter; // A counter value associated with this global.
-};
-
-
-BEGIN_DATADESC( CEnvGlobal )
-
- DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ),
- DEFINE_FIELD( m_triggermode, FIELD_INTEGER ),
- DEFINE_KEYFIELD( m_initialstate, FIELD_INTEGER, "initialstate" ),
- DEFINE_KEYFIELD( m_counter, FIELD_INTEGER, "counter" ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
- DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Remove", InputRemove ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
-
- DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCounter", InputSetCounter ),
- DEFINE_INPUTFUNC( FIELD_INTEGER, "AddToCounter", InputAddToCounter ),
- DEFINE_INPUTFUNC( FIELD_VOID, "GetCounter", InputGetCounter ),
-
- DEFINE_OUTPUT( m_outCounter, "Counter" ),
-
-END_DATADESC()
-
-
-LINK_ENTITY_TO_CLASS( env_global, CEnvGlobal );
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CEnvGlobal::Spawn( void )
-{
- if ( !m_globalstate )
- {
- UTIL_Remove( this );
- return;
- }
-
-#ifdef HL2_EPISODIC
- // if we modify the state of the physics cannon, make sure we precache the ragdoll boogie zap sound
- if ( ( m_globalstate != NULL_STRING ) && ( stricmp( STRING( m_globalstate ), "super_phys_gun" ) == 0 ) )
- {
- PrecacheScriptSound( "RagdollBoogie.Zap" );
- }
-#endif
-
- if ( FBitSet( m_spawnflags, SF_GLOBAL_SET ) )
- {
- if ( !GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, (GLOBALESTATE)m_initialstate );
- }
-
- if ( m_counter != 0 )
- {
- GlobalEntity_SetCounter( m_globalstate, m_counter );
- }
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputTurnOn( inputdata_t &inputdata )
-{
- if ( GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_SetState( m_globalstate, GLOBAL_ON );
- }
- else
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputTurnOff( inputdata_t &inputdata )
-{
- if ( GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_SetState( m_globalstate, GLOBAL_OFF );
- }
- else
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_OFF );
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputRemove( inputdata_t &inputdata )
-{
- if ( GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_SetState( m_globalstate, GLOBAL_DEAD );
- }
- else
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_DEAD );
- }
-}
-
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputSetCounter( inputdata_t &inputdata )
-{
- if ( !GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
- }
-
- GlobalEntity_SetCounter( m_globalstate, inputdata.value.Int() );
-}
-
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputAddToCounter( inputdata_t &inputdata )
-{
- if ( !GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
- }
-
- GlobalEntity_AddToCounter( m_globalstate, inputdata.value.Int() );
-}
-
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputGetCounter( inputdata_t &inputdata )
-{
- if ( !GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON );
- }
-
- m_outCounter.Set( GlobalEntity_GetCounter( m_globalstate ), inputdata.pActivator, this );
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose:
-//------------------------------------------------------------------------------
-void CEnvGlobal::InputToggle( inputdata_t &inputdata )
-{
- GLOBALESTATE oldState = GlobalEntity_GetState( m_globalstate );
- GLOBALESTATE newState;
-
- if ( oldState == GLOBAL_ON )
- {
- newState = GLOBAL_OFF;
- }
- else if ( oldState == GLOBAL_OFF )
- {
- newState = GLOBAL_ON;
- }
- else
- {
- return;
- }
-
- if ( GlobalEntity_IsInTable( m_globalstate ) )
- {
- GlobalEntity_SetState( m_globalstate, newState );
- }
- else
- {
- GlobalEntity_Add( m_globalstate, gpGlobals->mapname, newState );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw any debug text overlays
-// Input :
-// Output : Current text offset from the top
-//-----------------------------------------------------------------------------
-int CEnvGlobal::DrawDebugTextOverlays(void)
-{
- // Skip AIClass debug overlays
- int text_offset = CBaseEntity::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
- Q_snprintf(tempstr,sizeof(tempstr),"State: %s",STRING(m_globalstate));
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- GLOBALESTATE nState = GlobalEntity_GetState( m_globalstate );
-
- switch( nState )
- {
- case GLOBAL_OFF:
- Q_strncpy(tempstr,"Value: OFF",sizeof(tempstr));
- break;
-
- case GLOBAL_ON:
- Q_strncpy(tempstr,"Value: ON",sizeof(tempstr));
- break;
-
- case GLOBAL_DEAD:
- Q_strncpy(tempstr,"Value: DEAD",sizeof(tempstr));
- break;
- }
- EntityText(text_offset,tempstr,0);
- text_offset++;
- }
- return text_offset;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-#define MS_MAX_TARGETS 32
-
-const int SF_MULTI_INIT = 1;
-
-class CMultiSource : public CLogicalEntity
-{
-public:
- DECLARE_CLASS( CMultiSource, CLogicalEntity );
-
- void Spawn( );
- bool KeyValue( const char *szKeyName, const char *szValue );
- void Use( ::CBaseEntity *pActivator, ::CBaseEntity *pCaller, USE_TYPE useType, float value );
- int ObjectCaps( void ) { return(BaseClass::ObjectCaps() | FCAP_MASTER); }
- bool IsTriggered( ::CBaseEntity *pActivator );
- void Register( void );
-
- DECLARE_DATADESC();
-
- EHANDLE m_rgEntities[MS_MAX_TARGETS];
- int m_rgTriggered[MS_MAX_TARGETS];
-
- COutputEvent m_OnTrigger; // Fired when all connections are triggered.
-
- int m_iTotal;
- string_t m_globalstate;
-};
-
-BEGIN_DATADESC( CMultiSource )
-
- //!!!BUGBUG FIX
- DEFINE_ARRAY( m_rgEntities, FIELD_EHANDLE, MS_MAX_TARGETS ),
- DEFINE_ARRAY( m_rgTriggered, FIELD_INTEGER, MS_MAX_TARGETS ),
- DEFINE_FIELD( m_iTotal, FIELD_INTEGER ),
-
- DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ),
-
- // Function pointers
- DEFINE_FUNCTION( Register ),
-
- // Outputs
- DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"),
-
-END_DATADESC()
-
-
-LINK_ENTITY_TO_CLASS( multisource, CMultiSource );
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Cache user entity field values until spawn is called.
-// Input : szKeyName - Key to handle.
-// szValue - Value for key.
-// Output : Returns true if the key was handled, false if not.
-//-----------------------------------------------------------------------------
-bool CMultiSource::KeyValue( const char *szKeyName, const char *szValue )
-{
- if ( FStrEq(szKeyName, "style") ||
- FStrEq(szKeyName, "height") ||
- FStrEq(szKeyName, "killtarget") ||
- FStrEq(szKeyName, "value1") ||
- FStrEq(szKeyName, "value2") ||
- FStrEq(szKeyName, "value3"))
- {
- }
- else
- {
- return BaseClass::KeyValue( szKeyName, szValue );
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMultiSource::Spawn()
-{
- SetNextThink( gpGlobals->curtime + 0.1f );
- m_spawnflags |= SF_MULTI_INIT; // Until it's initialized
- SetThink(&CMultiSource::Register);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pActivator -
-// pCaller -
-// useType -
-// value -
-//-----------------------------------------------------------------------------
-void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
-{
- int i = 0;
-
- // Find the entity in our list
- while (i < m_iTotal)
- if ( m_rgEntities[i++] == pCaller )
- break;
-
- // if we didn't find it, report error and leave
- if (i > m_iTotal)
- {
- Warning("MultiSrc: Used by non member %s.\n", pCaller->edict() ? pCaller->GetClassname() : "<logical entity>");
- return;
- }
-
- // CONSIDER: a Use input to the multisource always toggles. Could check useType for ON/OFF/TOGGLE
-
- m_rgTriggered[i-1] ^= 1;
-
- //
- if ( IsTriggered( pActivator ) )
- {
- DevMsg( 2, "Multisource %s enabled (%d inputs)\n", GetDebugName(), m_iTotal );
- USE_TYPE useType = USE_TOGGLE;
- if ( m_globalstate != NULL_STRING )
- useType = USE_ON;
-
- m_OnTrigger.FireOutput(pActivator, this);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CMultiSource::IsTriggered( CBaseEntity * )
-{
- // Is everything triggered?
- int i = 0;
-
- // Still initializing?
- if ( m_spawnflags & SF_MULTI_INIT )
- return 0;
-
- while (i < m_iTotal)
- {
- if (m_rgTriggered[i] == 0)
- break;
- i++;
- }
-
- if (i == m_iTotal)
- {
- if ( !m_globalstate || GlobalEntity_GetState( m_globalstate ) == GLOBAL_ON )
- return 1;
- }
-
- return 0;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMultiSource::Register(void)
-{
- CBaseEntity *pTarget = NULL;
-
- m_iTotal = 0;
- memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) );
-
- SetThink(&CMultiSource::SUB_DoNothing);
-
- // search for all entities which target this multisource (m_iName)
- // dvsents2: port multisource to entity I/O!
-
- pTarget = gEntList.FindEntityByTarget( NULL, STRING(GetEntityName()) );
-
- while ( pTarget && (m_iTotal < MS_MAX_TARGETS) )
- {
- if ( pTarget )
- m_rgEntities[m_iTotal++] = pTarget;
-
- pTarget = gEntList.FindEntityByTarget( pTarget, STRING(GetEntityName()) );
- }
-
- pTarget = gEntList.FindEntityByClassname( NULL, "multi_manager" );
- while (pTarget && (m_iTotal < MS_MAX_TARGETS))
- {
- if ( pTarget && pTarget->HasTarget(GetEntityName()) )
- m_rgEntities[m_iTotal++] = pTarget;
-
- pTarget = gEntList.FindEntityByClassname( pTarget, "multi_manager" );
- }
-
- m_spawnflags &= ~SF_MULTI_INIT;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Holds a value that can be added to and subtracted from.
-//-----------------------------------------------------------------------------
-class CMathCounter : public CLogicalEntity
-{
- DECLARE_CLASS( CMathCounter, CLogicalEntity );
-private:
- float m_flMin; // Minimum clamp value. If min and max are BOTH zero, no clamping is done.
- float m_flMax; // Maximum clamp value.
- bool m_bHitMin; // Set when we reach or go below our minimum value, cleared if we go above it again.
- bool m_bHitMax; // Set when we reach or exceed our maximum value, cleared if we fall below it again.
-
- bool m_bDisabled;
-
- bool KeyValue(const char *szKeyName, const char *szValue);
- void Spawn(void);
-
- int DrawDebugTextOverlays(void);
-
- void UpdateOutValue(CBaseEntity *pActivator, float fNewValue);
-
- // Inputs
- void InputAdd( inputdata_t &inputdata );
- void InputDivide( inputdata_t &inputdata );
- void InputMultiply( inputdata_t &inputdata );
- void InputSetValue( inputdata_t &inputdata );
- void InputSetValueNoFire( inputdata_t &inputdata );
- void InputSubtract( inputdata_t &inputdata );
- void InputSetHitMax( inputdata_t &inputdata );
- void InputSetHitMin( inputdata_t &inputdata );
- void InputGetValue( inputdata_t &inputdata );
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-
- // Outputs
- COutputFloat m_OutValue;
- COutputFloat m_OnGetValue; // Used for polling the counter value.
- COutputEvent m_OnHitMin;
- COutputEvent m_OnHitMax;
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(math_counter, CMathCounter);
-
-
-BEGIN_DATADESC( CMathCounter )
-
- DEFINE_FIELD(m_bHitMax, FIELD_BOOLEAN),
- DEFINE_FIELD(m_bHitMin, FIELD_BOOLEAN),
-
- // Keys
- DEFINE_KEYFIELD(m_flMin, FIELD_FLOAT, "min"),
- DEFINE_KEYFIELD(m_flMax, FIELD_FLOAT, "max"),
-
- DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
-
- // Inputs
- DEFINE_INPUTFUNC(FIELD_FLOAT, "Add", InputAdd),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "Divide", InputDivide),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "Multiply", InputMultiply),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueNoFire", InputSetValueNoFire),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "Subtract", InputSubtract),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetHitMax", InputSetHitMax),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetHitMin", InputSetHitMin),
- DEFINE_INPUTFUNC(FIELD_VOID, "GetValue", InputGetValue),
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
- // Outputs
- DEFINE_OUTPUT(m_OutValue, "OutValue"),
- DEFINE_OUTPUT(m_OnHitMin, "OnHitMin"),
- DEFINE_OUTPUT(m_OnHitMax, "OnHitMax"),
- DEFINE_OUTPUT(m_OnGetValue, "OnGetValue"),
-
-END_DATADESC()
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Handles key values from the BSP before spawn is called.
-//-----------------------------------------------------------------------------
-bool CMathCounter::KeyValue(const char *szKeyName, const char *szValue)
-{
- //
- // Set the initial value of the counter.
- //
- if (!stricmp(szKeyName, "startvalue"))
- {
- m_OutValue.Init(atoi(szValue));
- return(true);
- }
-
- return(BaseClass::KeyValue(szKeyName, szValue));
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Called before spawning, after key values have been set.
-//-----------------------------------------------------------------------------
-void CMathCounter::Spawn( void )
-{
- //
- // Make sure max and min are ordered properly or clamp won't work.
- //
- if (m_flMin > m_flMax)
- {
- float flTemp = m_flMax;
- m_flMax = m_flMin;
- m_flMin = flTemp;
- }
-
- //
- // Clamp initial value to within the valid range.
- //
- if ((m_flMin != 0) || (m_flMax != 0))
- {
- float flStartValue = clamp(m_OutValue.Get(), m_flMin, m_flMax);
- m_OutValue.Init(flStartValue);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw any debug text overlays
-// Input :
-// Output : Current text offset from the top
-//-----------------------------------------------------------------------------
-int CMathCounter::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- Q_snprintf(tempstr,sizeof(tempstr)," min value: %f", m_flMin);
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- Q_snprintf(tempstr,sizeof(tempstr)," max value: %f", m_flMax);
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- Q_snprintf(tempstr,sizeof(tempstr),"current value: %f", m_OutValue.Get());
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- if( m_bDisabled )
- {
- Q_snprintf(tempstr,sizeof(tempstr),"*DISABLED*");
- }
- else
- {
- Q_snprintf(tempstr,sizeof(tempstr),"Enabled.");
- }
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- }
- return text_offset;
-}
-
-//-----------------------------------------------------------------------------
-// Change min/max
-//-----------------------------------------------------------------------------
-void CMathCounter::InputSetHitMax( inputdata_t &inputdata )
-{
- m_flMax = inputdata.value.Float();
- if ( m_flMax < m_flMin )
- {
- m_flMin = m_flMax;
- }
- UpdateOutValue( inputdata.pActivator, m_OutValue.Get() );
-}
-
-void CMathCounter::InputSetHitMin( inputdata_t &inputdata )
-{
- m_flMin = inputdata.value.Float();
- if ( m_flMax < m_flMin )
- {
- m_flMax = m_flMin;
- }
- UpdateOutValue( inputdata.pActivator, m_OutValue.Get() );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for adding to the accumulator value.
-// Input : Float value to add.
-//-----------------------------------------------------------------------------
-void CMathCounter::InputAdd( inputdata_t &inputdata )
-{
- if( m_bDisabled )
- {
- DevMsg("Math Counter %s ignoring ADD because it is disabled\n", GetDebugName() );
- return;
- }
-
- float fNewValue = m_OutValue.Get() + inputdata.value.Float();
- UpdateOutValue( inputdata.pActivator, fNewValue );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for multiplying the current value.
-// Input : Float value to multiply the value by.
-//-----------------------------------------------------------------------------
-void CMathCounter::InputDivide( inputdata_t &inputdata )
-{
- if( m_bDisabled )
- {
- DevMsg("Math Counter %s ignoring DIVIDE because it is disabled\n", GetDebugName() );
- return;
- }
-
- if (inputdata.value.Float() != 0)
- {
- float fNewValue = m_OutValue.Get() / inputdata.value.Float();
- UpdateOutValue( inputdata.pActivator, fNewValue );
- }
- else
- {
- DevMsg( 1, "LEVEL DESIGN ERROR: Divide by zero in math_value\n" );
- UpdateOutValue( inputdata.pActivator, m_OutValue.Get() );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for multiplying the current value.
-// Input : Float value to multiply the value by.
-//-----------------------------------------------------------------------------
-void CMathCounter::InputMultiply( inputdata_t &inputdata )
-{
- if( m_bDisabled )
- {
- DevMsg("Math Counter %s ignoring MULTIPLY because it is disabled\n", GetDebugName() );
- return;
- }
-
- float fNewValue = m_OutValue.Get() * inputdata.value.Float();
- UpdateOutValue( inputdata.pActivator, fNewValue );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for updating the value.
-// Input : Float value to set.
-//-----------------------------------------------------------------------------
-void CMathCounter::InputSetValue( inputdata_t &inputdata )
-{
- if( m_bDisabled )
- {
- DevMsg("Math Counter %s ignoring SETVALUE because it is disabled\n", GetDebugName() );
- return;
- }
-
- UpdateOutValue( inputdata.pActivator, inputdata.value.Float() );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for updating the value.
-// Input : Float value to set.
-//-----------------------------------------------------------------------------
-void CMathCounter::InputSetValueNoFire( inputdata_t &inputdata )
-{
- if( m_bDisabled )
- {
- DevMsg("Math Counter %s ignoring SETVALUENOFIRE because it is disabled\n", GetDebugName() );
- return;
- }
-
- float flNewValue = inputdata.value.Float();
- if (( m_flMin != 0 ) || (m_flMax != 0 ))
- {
- flNewValue = clamp(flNewValue, m_flMin, m_flMax);
- }
-
- m_OutValue.Init( flNewValue );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for subtracting from the current value.
-// Input : Float value to subtract.
-//-----------------------------------------------------------------------------
-void CMathCounter::InputSubtract( inputdata_t &inputdata )
-{
- if( m_bDisabled )
- {
- DevMsg("Math Counter %s ignoring SUBTRACT because it is disabled\n", GetDebugName() );
- return;
- }
-
- float fNewValue = m_OutValue.Get() - inputdata.value.Float();
- UpdateOutValue( inputdata.pActivator, fNewValue );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CMathCounter::InputGetValue( inputdata_t &inputdata )
-{
- float flOutValue = m_OutValue.Get();
- m_OnGetValue.Set( flOutValue, inputdata.pActivator, inputdata.pCaller );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CMathCounter::InputEnable( inputdata_t &inputdata )
-{
- m_bDisabled = false;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CMathCounter::InputDisable( inputdata_t &inputdata )
-{
- m_bDisabled = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the value to the new value, clamping and firing the output value.
-// Input : fNewValue - Value to set.
-//-----------------------------------------------------------------------------
-void CMathCounter::UpdateOutValue(CBaseEntity *pActivator, float fNewValue)
-{
- if ((m_flMin != 0) || (m_flMax != 0))
- {
- //
- // Fire an output any time we reach or exceed our maximum value.
- //
- if ( fNewValue >= m_flMax )
- {
- if ( !m_bHitMax )
- {
- m_bHitMax = true;
- m_OnHitMax.FireOutput( pActivator, this );
- }
- }
- else
- {
- m_bHitMax = false;
- }
-
- //
- // Fire an output any time we reach or go below our minimum value.
- //
- if ( fNewValue <= m_flMin )
- {
- if ( !m_bHitMin )
- {
- m_bHitMin = true;
- m_OnHitMin.FireOutput( pActivator, this );
- }
- }
- else
- {
- m_bHitMin = false;
- }
-
- fNewValue = clamp(fNewValue, m_flMin, m_flMax);
- }
-
- m_OutValue.Set(fNewValue, pActivator, this);
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Compares a single string input to up to 16 case values, firing an
-// output corresponding to the case value that matched, or a default
-// output if the input value didn't match any of the case values.
-//
-// This can also be used to fire a random output from a set of outputs.
-//-----------------------------------------------------------------------------
-#define MAX_LOGIC_CASES 16
-
-class CLogicCase : public CLogicalEntity
-{
- DECLARE_CLASS( CLogicCase, CLogicalEntity );
-private:
- string_t m_nCase[MAX_LOGIC_CASES];
-
- int m_nShuffleCases;
- int m_nLastShuffleCase;
- unsigned char m_uchShuffleCaseMap[MAX_LOGIC_CASES];
-
- void Spawn(void);
-
- int BuildCaseMap(unsigned char *puchMap);
-
- // Inputs
- void InputValue( inputdata_t &inputdata );
- void InputPickRandom( inputdata_t &inputdata );
- void InputPickRandomShuffle( inputdata_t &inputdata );
-
- // Outputs
- COutputEvent m_OnCase[MAX_LOGIC_CASES]; // Fired when the input value matches one of the case values.
- COutputVariant m_OnDefault; // Fired when no match was found.
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(logic_case, CLogicCase);
-
-
-BEGIN_DATADESC( CLogicCase )
-
-// Silence, Classcheck!
-// DEFINE_ARRAY( m_nCase, FIELD_STRING, MAX_LOGIC_CASES ),
-
- // Keys
- DEFINE_KEYFIELD(m_nCase[0], FIELD_STRING, "Case01"),
- DEFINE_KEYFIELD(m_nCase[1], FIELD_STRING, "Case02"),
- DEFINE_KEYFIELD(m_nCase[2], FIELD_STRING, "Case03"),
- DEFINE_KEYFIELD(m_nCase[3], FIELD_STRING, "Case04"),
- DEFINE_KEYFIELD(m_nCase[4], FIELD_STRING, "Case05"),
- DEFINE_KEYFIELD(m_nCase[5], FIELD_STRING, "Case06"),
- DEFINE_KEYFIELD(m_nCase[6], FIELD_STRING, "Case07"),
- DEFINE_KEYFIELD(m_nCase[7], FIELD_STRING, "Case08"),
- DEFINE_KEYFIELD(m_nCase[8], FIELD_STRING, "Case09"),
- DEFINE_KEYFIELD(m_nCase[9], FIELD_STRING, "Case10"),
- DEFINE_KEYFIELD(m_nCase[10], FIELD_STRING, "Case11"),
- DEFINE_KEYFIELD(m_nCase[11], FIELD_STRING, "Case12"),
- DEFINE_KEYFIELD(m_nCase[12], FIELD_STRING, "Case13"),
- DEFINE_KEYFIELD(m_nCase[13], FIELD_STRING, "Case14"),
- DEFINE_KEYFIELD(m_nCase[14], FIELD_STRING, "Case15"),
- DEFINE_KEYFIELD(m_nCase[15], FIELD_STRING, "Case16"),
-
- DEFINE_FIELD( m_nShuffleCases, FIELD_INTEGER ),
- DEFINE_FIELD( m_nLastShuffleCase, FIELD_INTEGER ),
- DEFINE_ARRAY( m_uchShuffleCaseMap, FIELD_CHARACTER, MAX_LOGIC_CASES ),
-
- // Inputs
- DEFINE_INPUTFUNC(FIELD_INPUT, "InValue", InputValue),
- DEFINE_INPUTFUNC(FIELD_VOID, "PickRandom", InputPickRandom),
- DEFINE_INPUTFUNC(FIELD_VOID, "PickRandomShuffle", InputPickRandomShuffle),
-
- // Outputs
- DEFINE_OUTPUT(m_OnCase[0], "OnCase01"),
- DEFINE_OUTPUT(m_OnCase[1], "OnCase02"),
- DEFINE_OUTPUT(m_OnCase[2], "OnCase03"),
- DEFINE_OUTPUT(m_OnCase[3], "OnCase04"),
- DEFINE_OUTPUT(m_OnCase[4], "OnCase05"),
- DEFINE_OUTPUT(m_OnCase[5], "OnCase06"),
- DEFINE_OUTPUT(m_OnCase[6], "OnCase07"),
- DEFINE_OUTPUT(m_OnCase[7], "OnCase08"),
- DEFINE_OUTPUT(m_OnCase[8], "OnCase09"),
- DEFINE_OUTPUT(m_OnCase[9], "OnCase10"),
- DEFINE_OUTPUT(m_OnCase[10], "OnCase11"),
- DEFINE_OUTPUT(m_OnCase[11], "OnCase12"),
- DEFINE_OUTPUT(m_OnCase[12], "OnCase13"),
- DEFINE_OUTPUT(m_OnCase[13], "OnCase14"),
- DEFINE_OUTPUT(m_OnCase[14], "OnCase15"),
- DEFINE_OUTPUT(m_OnCase[15], "OnCase16"),
-
- DEFINE_OUTPUT(m_OnDefault, "OnDefault"),
-
-END_DATADESC()
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Called before spawning, after key values have been set.
-//-----------------------------------------------------------------------------
-void CLogicCase::Spawn( void )
-{
- m_nLastShuffleCase = -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Evaluates the new input value, firing the appropriate OnCaseX output
-// if the input value matches one of the "CaseX" keys.
-// Input : Value - Variant value to compare against the values of the case fields.
-// We use a variant so that we can convert any input type to a string.
-//-----------------------------------------------------------------------------
-void CLogicCase::InputValue( inputdata_t &inputdata )
-{
- const char *pszValue = inputdata.value.String();
- for (int i = 0; i < MAX_LOGIC_CASES; i++)
- {
- if ((m_nCase[i] != NULL_STRING) && !stricmp(STRING(m_nCase[i]), pszValue))
- {
- m_OnCase[i].FireOutput( inputdata.pActivator, this );
- return;
- }
- }
-
- m_OnDefault.Set( inputdata.value, inputdata.pActivator, this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Count the number of valid cases, building a packed array
-// that maps 0..NumCases to the actual CaseX values.
-//
-// This allows our zany mappers to set up cases sparsely if they desire.
-// NOTE: assumes pnMap points to an array of MAX_LOGIC_CASES
-//-----------------------------------------------------------------------------
-int CLogicCase::BuildCaseMap(unsigned char *puchCaseMap)
-{
- memset(puchCaseMap, 0, sizeof(unsigned char) * MAX_LOGIC_CASES);
-
- int nNumCases = 0;
- for (int i = 0; i < MAX_LOGIC_CASES; i++)
- {
- if (m_OnCase[i].NumberOfElements() > 0)
- {
- puchCaseMap[nNumCases] = (unsigned char)i;
- nNumCases++;
- }
- }
-
- return nNumCases;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Makes the case statement choose a case at random.
-//-----------------------------------------------------------------------------
-void CLogicCase::InputPickRandom( inputdata_t &inputdata )
-{
- unsigned char uchCaseMap[MAX_LOGIC_CASES];
- int nNumCases = BuildCaseMap( uchCaseMap );
-
- //
- // Choose a random case from the ones that were set up by the level designer.
- //
- if ( nNumCases > 0 )
- {
- int nRandom = random->RandomInt(0, nNumCases - 1);
- int nCase = (unsigned char)uchCaseMap[nRandom];
-
- Assert(nCase < MAX_LOGIC_CASES);
-
- if (nCase < MAX_LOGIC_CASES)
- {
- m_OnCase[nCase].FireOutput( inputdata.pActivator, this );
- }
- }
- else
- {
- DevMsg( 1, "Firing PickRandom input on logic_case %s with no cases set up\n", GetDebugName() );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Makes the case statement choose a case at random.
-//-----------------------------------------------------------------------------
-void CLogicCase::InputPickRandomShuffle( inputdata_t &inputdata )
-{
- int nAvoidCase = -1;
- int nCaseCount = m_nShuffleCases;
-
- if ( nCaseCount == 0 )
- {
- // Starting a new shuffle batch.
- nCaseCount = m_nShuffleCases = BuildCaseMap( m_uchShuffleCaseMap );
-
- if ( ( m_nShuffleCases > 1 ) && ( m_nLastShuffleCase != -1 ) )
- {
- // Remove the previously picked case from the case map for this pick only.
- // This avoids repeats across shuffle batch boundaries.
- nAvoidCase = m_nLastShuffleCase;
-
- for (int i = 0; i < m_nShuffleCases; i++ )
- {
- if ( m_uchShuffleCaseMap[i] == nAvoidCase )
- {
- unsigned char uchSwap = m_uchShuffleCaseMap[i];
- m_uchShuffleCaseMap[i] = m_uchShuffleCaseMap[nCaseCount - 1];
- m_uchShuffleCaseMap[nCaseCount - 1] = uchSwap;
- nCaseCount--;
- break;
- }
- }
- }
- }
-
- //
- // Choose a random case from the ones that were set up by the level designer.
- // Never repeat a case within a shuffle batch, nor consecutively across batches.
- //
- if ( nCaseCount > 0 )
- {
- int nRandom = random->RandomInt( 0, nCaseCount - 1 );
-
- int nCase = m_uchShuffleCaseMap[nRandom];
- Assert(nCase < MAX_LOGIC_CASES);
-
- if (nCase < MAX_LOGIC_CASES)
- {
- m_OnCase[nCase].FireOutput( inputdata.pActivator, this );
- }
-
- m_uchShuffleCaseMap[nRandom] = m_uchShuffleCaseMap[m_nShuffleCases - 1];
- m_nShuffleCases--;
-
- m_nLastShuffleCase = nCase;
- }
- else
- {
- DevMsg( 1, "Firing PickRandom input on logic_case %s with no cases set up\n", GetDebugName() );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Compares a floating point input to a predefined value, firing an
-// output to indicate the result of the comparison.
-//-----------------------------------------------------------------------------
-class CLogicCompare : public CLogicalEntity
-{
- DECLARE_CLASS( CLogicCompare, CLogicalEntity );
-
-public:
- int DrawDebugTextOverlays(void);
-
-private:
- // Inputs
- void InputSetValue( inputdata_t &inputdata );
- void InputSetValueCompare( inputdata_t &inputdata );
- void InputSetCompareValue( inputdata_t &inputdata );
- void InputCompare( inputdata_t &inputdata );
-
- void DoCompare(CBaseEntity *pActivator, float flInValue);
-
- float m_flInValue; // Place to hold the last input value for a recomparison.
- float m_flCompareValue; // The value to compare the input value against.
-
- // Outputs
- COutputFloat m_OnLessThan; // Fired when the input value is less than the compare value.
- COutputFloat m_OnEqualTo; // Fired when the input value is equal to the compare value.
- COutputFloat m_OnNotEqualTo; // Fired when the input value is not equal to the compare value.
- COutputFloat m_OnGreaterThan; // Fired when the input value is greater than the compare value.
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(logic_compare, CLogicCompare);
-
-
-BEGIN_DATADESC( CLogicCompare )
-
- // Keys
- DEFINE_KEYFIELD(m_flCompareValue, FIELD_FLOAT, "CompareValue"),
- DEFINE_KEYFIELD(m_flInValue, FIELD_FLOAT, "InitialValue"),
-
- // Inputs
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueCompare", InputSetValueCompare),
- DEFINE_INPUTFUNC(FIELD_FLOAT, "SetCompareValue", InputSetCompareValue),
- DEFINE_INPUTFUNC(FIELD_VOID, "Compare", InputCompare),
-
- // Outputs
- DEFINE_OUTPUT(m_OnEqualTo, "OnEqualTo"),
- DEFINE_OUTPUT(m_OnNotEqualTo, "OnNotEqualTo"),
- DEFINE_OUTPUT(m_OnGreaterThan, "OnGreaterThan"),
- DEFINE_OUTPUT(m_OnLessThan, "OnLessThan"),
-
-END_DATADESC()
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for a new input value without performing a comparison.
-//-----------------------------------------------------------------------------
-void CLogicCompare::InputSetValue( inputdata_t &inputdata )
-{
- m_flInValue = inputdata.value.Float();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for a setting a new value and doing the comparison.
-//-----------------------------------------------------------------------------
-void CLogicCompare::InputSetValueCompare( inputdata_t &inputdata )
-{
- m_flInValue = inputdata.value.Float();
- DoCompare( inputdata.pActivator, m_flInValue );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for a new input value without performing a comparison.
-//-----------------------------------------------------------------------------
-void CLogicCompare::InputSetCompareValue( inputdata_t &inputdata )
-{
- m_flCompareValue = inputdata.value.Float();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for forcing a recompare of the last input value.
-//-----------------------------------------------------------------------------
-void CLogicCompare::InputCompare( inputdata_t &inputdata )
-{
- DoCompare( inputdata.pActivator, m_flInValue );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Compares the input value to the compare value, firing the appropriate
-// output(s) based on the comparison result.
-// Input : flInValue - Value to compare against the comparison value.
-//-----------------------------------------------------------------------------
-void CLogicCompare::DoCompare(CBaseEntity *pActivator, float flInValue)
-{
- if (flInValue == m_flCompareValue)
- {
- m_OnEqualTo.Set(flInValue, pActivator, this);
- }
- else
- {
- m_OnNotEqualTo.Set(flInValue, pActivator, this);
-
- if (flInValue > m_flCompareValue)
- {
- m_OnGreaterThan.Set(flInValue, pActivator, this);
- }
- else
- {
- m_OnLessThan.Set(flInValue, pActivator, this);
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw any debug text overlays
-// Output : Current text offset from the top
-//-----------------------------------------------------------------------------
-int CLogicCompare::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- // print duration
- Q_snprintf(tempstr,sizeof(tempstr)," Initial Value: %f", m_flInValue);
- EntityText(text_offset,tempstr,0);
- text_offset++;
-
- // print hold time
- Q_snprintf(tempstr,sizeof(tempstr)," Compare Value: %f", m_flCompareValue);
- EntityText(text_offset,tempstr,0);
- text_offset++;
- }
- return text_offset;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Tests a boolean value, firing an output to indicate whether the
-// value was true or false.
-//-----------------------------------------------------------------------------
-class CLogicBranch : public CLogicalEntity
-{
- DECLARE_CLASS( CLogicBranch, CLogicalEntity );
-
-public:
-
- void UpdateOnRemove();
-
- void AddLogicBranchListener( CBaseEntity *pEntity );
- inline bool GetLogicBranchState();
- virtual int DrawDebugTextOverlays( void );
-
-private:
-
- enum LogicBranchFire_t
- {
- LOGIC_BRANCH_FIRE,
- LOGIC_BRANCH_NO_FIRE,
- };
-
- // Inputs
- void InputSetValue( inputdata_t &inputdata );
- void InputSetValueTest( inputdata_t &inputdata );
- void InputToggle( inputdata_t &inputdata );
- void InputToggleTest( inputdata_t &inputdata );
- void InputTest( inputdata_t &inputdata );
-
- void UpdateValue(bool bNewValue, CBaseEntity *pActivator, LogicBranchFire_t eFire);
-
- bool m_bInValue; // Place to hold the last input value for a future test.
-
- CUtlVector<EHANDLE> m_Listeners; // A list of logic_branch_listeners that are monitoring us.
-
- // Outputs
- COutputEvent m_OnTrue; // Fired when the value is true.
- COutputEvent m_OnFalse; // Fired when the value is false.
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(logic_branch, CLogicBranch);
-
-
-BEGIN_DATADESC( CLogicBranch )
-
- // Keys
- DEFINE_KEYFIELD(m_bInValue, FIELD_BOOLEAN, "InitialValue"),
-
- DEFINE_UTLVECTOR( m_Listeners, FIELD_EHANDLE ),
-
- // Inputs
- DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetValue", InputSetValue),
- DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetValueTest", InputSetValueTest),
- DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle),
- DEFINE_INPUTFUNC(FIELD_VOID, "ToggleTest", InputToggleTest),
- DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest),
-
- // Outputs
- DEFINE_OUTPUT(m_OnTrue, "OnTrue"),
- DEFINE_OUTPUT(m_OnFalse, "OnFalse"),
-
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CLogicBranch::UpdateOnRemove()
-{
- for ( int i = 0; i < m_Listeners.Count(); i++ )
- {
- CBaseEntity *pEntity = m_Listeners.Element( i ).Get();
- if ( pEntity )
- {
- g_EventQueue.AddEvent( this, "_OnLogicBranchRemoved", 0, this, this );
- }
- }
-
- BaseClass::UpdateOnRemove();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to set a new input value without firing outputs.
-// Input : Boolean value to set.
-//-----------------------------------------------------------------------------
-void CLogicBranch::InputSetValue( inputdata_t &inputdata )
-{
- UpdateValue( inputdata.value.Bool(), inputdata.pActivator, LOGIC_BRANCH_NO_FIRE );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler to set a new input value and fire appropriate outputs.
-// Input : Boolean value to set.
-//-----------------------------------------------------------------------------
-void CLogicBranch::InputSetValueTest( inputdata_t &inputdata )
-{
- UpdateValue( inputdata.value.Bool(), inputdata.pActivator, LOGIC_BRANCH_FIRE );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for toggling the boolean value without firing outputs.
-//-----------------------------------------------------------------------------
-void CLogicBranch::InputToggle( inputdata_t &inputdata )
-{
- UpdateValue( !m_bInValue, inputdata.pActivator, LOGIC_BRANCH_NO_FIRE );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for toggling the boolean value and then firing the
-// appropriate output based on the new value.
-//-----------------------------------------------------------------------------
-void CLogicBranch::InputToggleTest( inputdata_t &inputdata )
-{
- UpdateValue( !m_bInValue, inputdata.pActivator, LOGIC_BRANCH_FIRE );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handler for forcing a test of the last input value.
-//-----------------------------------------------------------------------------
-void CLogicBranch::InputTest( inputdata_t &inputdata )
-{
- UpdateValue( m_bInValue, inputdata.pActivator, LOGIC_BRANCH_FIRE );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Tests the last input value, firing the appropriate output based on
-// the test result.
-// Input : bInValue -
-//-----------------------------------------------------------------------------
-void CLogicBranch::UpdateValue( bool bNewValue, CBaseEntity *pActivator, LogicBranchFire_t eFire )
-{
- if ( m_bInValue != bNewValue )
- {
- m_bInValue = bNewValue;
-
- for ( int i = 0; i < m_Listeners.Count(); i++ )
- {
- CBaseEntity *pEntity = m_Listeners.Element( i ).Get();
- if ( pEntity )
- {
- g_EventQueue.AddEvent( pEntity, "_OnLogicBranchChanged", 0, this, this );
- }
- }
- }
-
- if ( eFire == LOGIC_BRANCH_FIRE )
- {
- if ( m_bInValue )
- {
- m_OnTrue.FireOutput( pActivator, this );
- }
- else
- {
- m_OnFalse.FireOutput( pActivator, this );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Accessor for logic_branchlist to test the value of the branch on demand.
-//-----------------------------------------------------------------------------
-bool CLogicBranch::GetLogicBranchState()
-{
- return m_bInValue;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CLogicBranch::AddLogicBranchListener( CBaseEntity *pEntity )
-{
- if ( m_Listeners.Find( pEntity ) == -1 )
- {
- m_Listeners.AddToTail( pEntity );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CLogicBranch::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- // print refire time
- Q_snprintf( tempstr, sizeof(tempstr), "Branch value: %s", (m_bInValue) ? "TRUE" : "FALSE" );
- EntityText( text_offset, tempstr, 0 );
- text_offset++;
- }
-
- return text_offset;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Autosaves when triggered
-//-----------------------------------------------------------------------------
-class CLogicAutosave : public CLogicalEntity
-{
- DECLARE_CLASS( CLogicAutosave, CLogicalEntity );
-
-protected:
- // Inputs
- void InputSave( inputdata_t &inputdata );
- void InputSaveDangerous( inputdata_t &inputdata );
- void InputSetMinHitpointsThreshold( inputdata_t &inputdata );
-
- DECLARE_DATADESC();
- bool m_bForceNewLevelUnit;
- int m_minHitPoints;
- int m_minHitPointsToCommit;
-};
-
-LINK_ENTITY_TO_CLASS(logic_autosave, CLogicAutosave);
-
-BEGIN_DATADESC( CLogicAutosave )
- DEFINE_KEYFIELD( m_bForceNewLevelUnit, FIELD_BOOLEAN, "NewLevelUnit" ),
- DEFINE_KEYFIELD( m_minHitPoints, FIELD_INTEGER, "MinimumHitPoints" ),
- DEFINE_KEYFIELD( m_minHitPointsToCommit, FIELD_INTEGER, "MinHitPointsToCommit" ),
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "Save", InputSave ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "SaveDangerous", InputSaveDangerous ),
- DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMinHitpointsThreshold", InputSetMinHitpointsThreshold ),
-END_DATADESC()
-
-//-----------------------------------------------------------------------------
-// Purpose: Save!
-//-----------------------------------------------------------------------------
-void CLogicAutosave::InputSave( inputdata_t &inputdata )
-{
- if ( m_bForceNewLevelUnit )
- {
- engine->ClearSaveDir();
- }
-
- engine->ServerCommand( "autosave\n" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Save safely!
-//-----------------------------------------------------------------------------
-void CLogicAutosave::InputSaveDangerous( inputdata_t &inputdata )
-{
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
-
- if ( g_ServerGameDLL.m_fAutoSaveDangerousTime != 0.0f && g_ServerGameDLL.m_fAutoSaveDangerousTime >= gpGlobals->curtime )
- {
- // A previous dangerous auto save was waiting to become safe
-
- if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
- {
- // The player isn't dead, so make the dangerous auto save safe
- engine->ServerCommand( "autosavedangerousissafe\n" );
- }
- }
-
- if ( m_bForceNewLevelUnit )
- {
- engine->ClearSaveDir();
- }
-
- if ( pPlayer->GetHealth() >= m_minHitPoints )
- {
- engine->ServerCommand( "autosavedangerous\n" );
- g_ServerGameDLL.m_fAutoSaveDangerousTime = gpGlobals->curtime + inputdata.value.Float();
-
- // Player must have this much health when we go to commit, or we don't commit.
- g_ServerGameDLL.m_fAutoSaveDangerousMinHealthToCommit = m_minHitPointsToCommit;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Autosaves when triggered
-//-----------------------------------------------------------------------------
-class CLogicActiveAutosave : public CLogicAutosave
-{
- DECLARE_CLASS( CLogicActiveAutosave, CLogicAutosave );
-
- void InputEnable( inputdata_t &inputdata )
- {
- m_flStartTime = -1;
- SetThink( &CLogicActiveAutosave::SaveThink );
- SetNextThink( gpGlobals->curtime );
- }
-
- void InputDisable( inputdata_t &inputdata )
- {
- SetThink( NULL );
- }
-
- void SaveThink()
- {
- CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
- if ( pPlayer )
- {
- if ( m_flStartTime < 0 )
- {
- if ( pPlayer->GetHealth() <= m_minHitPoints )
- {
- m_flStartTime = gpGlobals->curtime;
- }
- }
- else
- {
- if ( pPlayer->GetHealth() >= m_TriggerHitPoints )
- {
- inputdata_t inputdata;
- DevMsg( 2, "logic_active_autosave (%s, %d) triggered\n", STRING( GetEntityName() ), entindex() );
- if ( !m_flDangerousTime )
- {
- InputSave( inputdata );
- }
- else
- {
- inputdata.value.SetFloat( m_flDangerousTime );
- InputSaveDangerous( inputdata );
- }
- m_flStartTime = -1;
- }
- else if ( m_flTimeToTrigger > 0 && gpGlobals->curtime - m_flStartTime > m_flTimeToTrigger )
- {
- m_flStartTime = -1;
- }
- }
- }
-
- float thinkInterval = ( m_flStartTime < 0 ) ? 1.0 : 0.5;
- SetNextThink( gpGlobals->curtime + thinkInterval );
- }
-
- DECLARE_DATADESC();
-
- int m_TriggerHitPoints;
- float m_flTimeToTrigger;
- float m_flStartTime;
- float m_flDangerousTime;
-};
-
-LINK_ENTITY_TO_CLASS(logic_active_autosave, CLogicActiveAutosave);
-
-BEGIN_DATADESC( CLogicActiveAutosave )
- DEFINE_KEYFIELD( m_TriggerHitPoints, FIELD_INTEGER, "TriggerHitPoints" ),
- DEFINE_KEYFIELD( m_flTimeToTrigger, FIELD_FLOAT, "TimeToTrigger" ),
- DEFINE_KEYFIELD( m_flDangerousTime, FIELD_FLOAT, "DangerousTime" ),
- DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
- DEFINE_THINKFUNC( SaveThink ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Keyfield set func
-//-----------------------------------------------------------------------------
-void CLogicAutosave::InputSetMinHitpointsThreshold( inputdata_t &inputdata )
-{
- int setTo = inputdata.value.Int();
- AssertMsg1(setTo >= 0 && setTo <= 100, "Tried to set autosave MinHitpointsThreshold to %d!\n", setTo);
- m_minHitPoints = setTo;
-}
-
-// Finds the named physics object. If no name, returns the world
-// If a name is specified and an object not found - errors are reported
-IPhysicsObject *FindPhysicsObjectByNameOrWorld( string_t name, CBaseEntity *pErrorEntity )
-{
- if ( !name )
- return g_PhysWorldObject;
-
- IPhysicsObject *pPhysics = FindPhysicsObjectByName( name.ToCStr(), pErrorEntity );
- if ( !pPhysics )
- {
- DevWarning("%s: can't find %s\n", pErrorEntity->GetClassname(), name.ToCStr());
- }
- return pPhysics;
-}
-
-class CLogicCollisionPair : public CLogicalEntity
-{
- DECLARE_CLASS( CLogicCollisionPair, CLogicalEntity );
-public:
-
- void EnableCollisions( bool bEnable )
- {
- IPhysicsObject *pPhysics0 = FindPhysicsObjectByNameOrWorld( m_nameAttach1, this );
- IPhysicsObject *pPhysics1 = FindPhysicsObjectByNameOrWorld( m_nameAttach2, this );
-
- // need two different objects to do anything
- if ( pPhysics0 && pPhysics1 && pPhysics0 != pPhysics1 )
- {
- m_disabled = !bEnable;
- m_succeeded = true;
- if ( bEnable )
- {
- PhysEnableEntityCollisions( pPhysics0, pPhysics1 );
- }
- else
- {
- PhysDisableEntityCollisions( pPhysics0, pPhysics1 );
- }
- }
- else
- {
- m_succeeded = false;
- }
- }
-
- void Activate( void )
- {
- if ( m_disabled )
- {
- EnableCollisions( false );
- }
- BaseClass::Activate();
- }
-
- void InputDisableCollisions( inputdata_t &inputdata )
- {
- if ( m_succeeded && m_disabled )
- return;
- EnableCollisions( false );
- }
-
- void InputEnableCollisions( inputdata_t &inputdata )
- {
- if ( m_succeeded && !m_disabled )
- return;
- EnableCollisions( true );
- }
- // If Activate() becomes PostSpawn()
- //void OnRestore() { Activate(); }
-
- DECLARE_DATADESC();
-
-private:
- string_t m_nameAttach1;
- string_t m_nameAttach2;
- bool m_disabled;
- bool m_succeeded;
-};
-
-BEGIN_DATADESC( CLogicCollisionPair )
- DEFINE_KEYFIELD( m_nameAttach1, FIELD_STRING, "attach1" ),
- DEFINE_KEYFIELD( m_nameAttach2, FIELD_STRING, "attach2" ),
- DEFINE_KEYFIELD( m_disabled, FIELD_BOOLEAN, "startdisabled" ),
- DEFINE_FIELD( m_succeeded, FIELD_BOOLEAN ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "DisableCollisions", InputDisableCollisions ),
- DEFINE_INPUTFUNC( FIELD_VOID, "EnableCollisions", InputEnableCollisions ),
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( logic_collision_pair, CLogicCollisionPair );
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-#define MAX_LOGIC_BRANCH_NAMES 16
-
-class CLogicBranchList : public CLogicalEntity
-{
- DECLARE_CLASS( CLogicBranchList, CLogicalEntity );
-
- virtual void Spawn();
- virtual void Activate();
- virtual int DrawDebugTextOverlays( void );
-
-private:
-
- enum LogicBranchListenerLastState_t
- {
- LOGIC_BRANCH_LISTENER_NOT_INIT = 0,
- LOGIC_BRANCH_LISTENER_ALL_TRUE,
- LOGIC_BRANCH_LISTENER_ALL_FALSE,
- LOGIC_BRANCH_LISTENER_MIXED,
- };
-
- void DoTest( CBaseEntity *pActivator );
-
- string_t m_nLogicBranchNames[MAX_LOGIC_BRANCH_NAMES];
- CUtlVector<EHANDLE> m_LogicBranchList;
- LogicBranchListenerLastState_t m_eLastState;
-
- // Inputs
- void Input_OnLogicBranchRemoved( inputdata_t &inputdata );
- void Input_OnLogicBranchChanged( inputdata_t &inputdata );
- void InputTest( inputdata_t &inputdata );
-
- // Outputs
- COutputEvent m_OnAllTrue; // Fired when all the registered logic_branches become true.
- COutputEvent m_OnAllFalse; // Fired when all the registered logic_branches become false.
- COutputEvent m_OnMixed; // Fired when one of the registered logic branches changes, but not all are true or false.
-
- DECLARE_DATADESC();
-};
-
-LINK_ENTITY_TO_CLASS(logic_branch_listener, CLogicBranchList);
-
-
-BEGIN_DATADESC( CLogicBranchList )
-
- // Silence, classcheck!
- //DEFINE_ARRAY( m_nLogicBranchNames, FIELD_STRING, MAX_LOGIC_BRANCH_NAMES ),
-
- // Keys
- DEFINE_KEYFIELD( m_nLogicBranchNames[0], FIELD_STRING, "Branch01" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[1], FIELD_STRING, "Branch02" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[2], FIELD_STRING, "Branch03" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[3], FIELD_STRING, "Branch04" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[4], FIELD_STRING, "Branch05" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[5], FIELD_STRING, "Branch06" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[6], FIELD_STRING, "Branch07" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[7], FIELD_STRING, "Branch08" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[8], FIELD_STRING, "Branch09" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[9], FIELD_STRING, "Branch10" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[10], FIELD_STRING, "Branch11" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[11], FIELD_STRING, "Branch12" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[12], FIELD_STRING, "Branch13" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[13], FIELD_STRING, "Branch14" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[14], FIELD_STRING, "Branch15" ),
- DEFINE_KEYFIELD( m_nLogicBranchNames[15], FIELD_STRING, "Branch16" ),
-
- DEFINE_UTLVECTOR( m_LogicBranchList, FIELD_EHANDLE ),
-
- DEFINE_FIELD( m_eLastState, FIELD_INTEGER ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_INPUT, "Test", InputTest ),
- DEFINE_INPUTFUNC( FIELD_INPUT, "_OnLogicBranchChanged", Input_OnLogicBranchChanged ),
- DEFINE_INPUTFUNC( FIELD_INPUT, "_OnLogicBranchRemoved", Input_OnLogicBranchRemoved ),
-
- // Outputs
- DEFINE_OUTPUT( m_OnAllTrue, "OnAllTrue" ),
- DEFINE_OUTPUT( m_OnAllFalse, "OnAllFalse" ),
- DEFINE_OUTPUT( m_OnMixed, "OnMixed" ),
-
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Called before spawning, after key values have been set.
-//-----------------------------------------------------------------------------
-void CLogicBranchList::Spawn( void )
-{
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds all the logic_branches that we are monitoring and register ourselves with them.
-//-----------------------------------------------------------------------------
-void CLogicBranchList::Activate( void )
-{
- for ( int i = 0; i < MAX_LOGIC_BRANCH_NAMES; i++ )
- {
- CBaseEntity *pEntity = NULL;
- while ( ( pEntity = gEntList.FindEntityGeneric( pEntity, STRING( m_nLogicBranchNames[i] ), this ) ) != NULL )
- {
- if ( FClassnameIs( pEntity, "logic_branch" ) )
- {
- CLogicBranch *pBranch = (CLogicBranch *)pEntity;
- pBranch->AddLogicBranchListener( this );
- m_LogicBranchList.AddToTail( pBranch );
- }
- else
- {
- DevWarning( "logic_branchlist %s refers to entity %s, which is not a logic_branch\n", GetDebugName(), pEntity->GetDebugName() );
- }
- }
- }
-
- BaseClass::Activate();
-}
-
-
-//-----------------------------------------------------------------------------
-// Called when a monitored logic branch is deleted from the world, since that
-// might affect our final result.
-//-----------------------------------------------------------------------------
-void CLogicBranchList::Input_OnLogicBranchRemoved( inputdata_t &inputdata )
-{
- int nIndex = m_LogicBranchList.Find( inputdata.pActivator );
- if ( nIndex != -1 )
- {
- m_LogicBranchList.FastRemove( nIndex );
- }
-
- // See if this logic_branch's deletion affects the final result.
- DoTest( inputdata.pActivator );
-}
-
-
-//-----------------------------------------------------------------------------
-// Called when the value of a monitored logic branch changes.
-//-----------------------------------------------------------------------------
-void CLogicBranchList::Input_OnLogicBranchChanged( inputdata_t &inputdata )
-{
- DoTest( inputdata.pActivator );
-}
-
-
-//-----------------------------------------------------------------------------
-// Input handler to manually test the monitored logic branches and fire the
-// appropriate output.
-//-----------------------------------------------------------------------------
-void CLogicBranchList::InputTest( inputdata_t &inputdata )
-{
- // Force an output.
- m_eLastState = LOGIC_BRANCH_LISTENER_NOT_INIT;
-
- DoTest( inputdata.pActivator );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CLogicBranchList::DoTest( CBaseEntity *pActivator )
-{
- bool bOneTrue = false;
- bool bOneFalse = false;
-
- for ( int i = 0; i < m_LogicBranchList.Count(); i++ )
- {
- CLogicBranch *pBranch = (CLogicBranch *)m_LogicBranchList.Element( i ).Get();
- if ( pBranch && pBranch->GetLogicBranchState() )
- {
- bOneTrue = true;
- }
- else
- {
- bOneFalse = true;
- }
- }
-
- // Only fire the output if the new result differs from the last result.
- if ( bOneTrue && !bOneFalse )
- {
- if ( m_eLastState != LOGIC_BRANCH_LISTENER_ALL_TRUE )
- {
- m_OnAllTrue.FireOutput( pActivator, this );
- m_eLastState = LOGIC_BRANCH_LISTENER_ALL_TRUE;
- }
- }
- else if ( bOneFalse && !bOneTrue )
- {
- if ( m_eLastState != LOGIC_BRANCH_LISTENER_ALL_FALSE )
- {
- m_OnAllFalse.FireOutput( pActivator, this );
- m_eLastState = LOGIC_BRANCH_LISTENER_ALL_FALSE;
- }
- }
- else
- {
- if ( m_eLastState != LOGIC_BRANCH_LISTENER_MIXED )
- {
- m_OnMixed.FireOutput( pActivator, this );
- m_eLastState = LOGIC_BRANCH_LISTENER_MIXED;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CLogicBranchList::DrawDebugTextOverlays( void )
-{
- int text_offset = BaseClass::DrawDebugTextOverlays();
-
- if (m_debugOverlays & OVERLAY_TEXT_BIT)
- {
- char tempstr[512];
-
- for ( int i = 0; i < m_LogicBranchList.Count(); i++ )
- {
- CLogicBranch *pBranch = (CLogicBranch *)m_LogicBranchList.Element( i ).Get();
- if ( pBranch )
- {
- Q_snprintf( tempstr, sizeof(tempstr), "Branch (%s): %s", STRING(pBranch->GetEntityName()), (pBranch->GetLogicBranchState()) ? "TRUE" : "FALSE" );
- EntityText( text_offset, tempstr, 0 );
- text_offset++;
- }
- }
- }
-
- return text_offset;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements many of the entities that control logic flow within a map. +// +//============================================================================= + +#include "cbase.h" +#include "entityinput.h" +#include "entityoutput.h" +#include "eventqueue.h" +#include "mathlib/mathlib.h" +#include "globalstate.h" +#include "ndebugoverlay.h" +#include "saverestore_utlvector.h" +#include "vstdlib/random.h" +#include "gameinterface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +extern CServerGameDLL g_ServerGameDLL; + + +//----------------------------------------------------------------------------- +// Purpose: Compares a set of integer inputs to the one main input +// Outputs true if they are all equivalant, false otherwise +//----------------------------------------------------------------------------- +class CLogicCompareInteger : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicCompareInteger, CLogicalEntity ); + + // outputs + COutputEvent m_OnEqual; + COutputEvent m_OnNotEqual; + + // data + int m_iIntegerValue; + int m_iShouldCompareToValue; + + DECLARE_DATADESC(); + + CMultiInputVar m_AllIntCompares; + + // Input handlers + void InputValue( inputdata_t &inputdata ); + void InputCompareValues( inputdata_t &inputdata ); +}; + + +LINK_ENTITY_TO_CLASS( logic_multicompare, CLogicCompareInteger ); + + +BEGIN_DATADESC( CLogicCompareInteger ) + + DEFINE_OUTPUT( m_OnEqual, "OnEqual" ), + DEFINE_OUTPUT( m_OnNotEqual, "OnNotEqual" ), + + DEFINE_KEYFIELD( m_iIntegerValue, FIELD_INTEGER, "IntegerValue" ), + DEFINE_KEYFIELD( m_iShouldCompareToValue, FIELD_INTEGER, "ShouldComparetoValue" ), + + DEFINE_FIELD( m_AllIntCompares, FIELD_INPUT ), + + DEFINE_INPUTFUNC( FIELD_INPUT, "InputValue", InputValue ), + DEFINE_INPUTFUNC( FIELD_INPUT, "CompareValues", InputCompareValues ), + +END_DATADESC() + + + + +//----------------------------------------------------------------------------- +// Purpose: Adds to the list of compared values +//----------------------------------------------------------------------------- +void CLogicCompareInteger::InputValue( inputdata_t &inputdata ) +{ + // make sure it's an int, if it can't be converted just throw it away + if ( !inputdata.value.Convert(FIELD_INTEGER) ) + return; + + // update the value list with the new value + m_AllIntCompares.AddValue( inputdata.value, inputdata.nOutputID ); + + // if we haven't already this frame, send a message to ourself to update and fire + if ( !m_AllIntCompares.m_bUpdatedThisFrame ) + { + // TODO: need to add this event with a lower priority, so it gets called after all inputs have arrived + g_EventQueue.AddEvent( this, "CompareValues", 0, inputdata.pActivator, this, inputdata.nOutputID ); + m_AllIntCompares.m_bUpdatedThisFrame = TRUE; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Forces a recompare +//----------------------------------------------------------------------------- +void CLogicCompareInteger::InputCompareValues( inputdata_t &inputdata ) +{ + m_AllIntCompares.m_bUpdatedThisFrame = FALSE; + + // loop through all the values comparing them + int value = m_iIntegerValue; + CMultiInputVar::inputitem_t *input = m_AllIntCompares.m_InputList; + + if ( !m_iShouldCompareToValue && input ) + { + value = input->value.Int(); + } + + while ( input ) + { + if ( input->value.Int() != value ) + { + // false + m_OnNotEqual.FireOutput( inputdata.pActivator, this ); + return; + } + + input = input->next; + } + + // true! all values equal + m_OnEqual.FireOutput( inputdata.pActivator, this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Timer entity. Fires an output at regular or random intervals. +//----------------------------------------------------------------------------- +// +// Spawnflags and others constants. +// +const int SF_TIMER_UPDOWN = 1; +const float LOGIC_TIMER_MIN_INTERVAL = 0.01; + + +class CTimerEntity : public CLogicalEntity +{ +public: + DECLARE_CLASS( CTimerEntity, CLogicalEntity ); + + void Spawn( void ); + void Think( void ); + + void Toggle( void ); + void Enable( void ); + void Disable( void ); + void FireTimer( void ); + + int DrawDebugTextOverlays(void); + + // outputs + COutputEvent m_OnTimer; + COutputEvent m_OnTimerHigh; + COutputEvent m_OnTimerLow; + + // inputs + void InputToggle( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + void InputFireTimer( inputdata_t &inputdata ); + void InputRefireTime( inputdata_t &inputdata ); + void InputResetTimer( inputdata_t &inputdata ); + void InputAddToTimer( inputdata_t &inputdata ); + void InputSubtractFromTimer( inputdata_t &inputdata ); + + int m_iDisabled; + float m_flRefireTime; + bool m_bUpDownState; + int m_iUseRandomTime; + float m_flLowerRandomBound; + float m_flUpperRandomBound; + + // methods + void ResetTimer( void ); + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( logic_timer, CTimerEntity ); + + +BEGIN_DATADESC( CTimerEntity ) + + // Keys + DEFINE_KEYFIELD( m_iDisabled, FIELD_INTEGER, "StartDisabled" ), + DEFINE_KEYFIELD( m_flRefireTime, FIELD_FLOAT, "RefireTime" ), + + DEFINE_FIELD( m_bUpDownState, FIELD_BOOLEAN ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_FLOAT, "RefireTime", InputRefireTime ), + DEFINE_INPUTFUNC( FIELD_VOID, "FireTimer", InputFireTimer ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "AddToTimer", InputAddToTimer ), + DEFINE_INPUTFUNC( FIELD_VOID, "ResetTimer", InputResetTimer ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SubtractFromTimer", InputSubtractFromTimer ), + + DEFINE_INPUT( m_iUseRandomTime, FIELD_INTEGER, "UseRandomTime" ), + DEFINE_INPUT( m_flLowerRandomBound, FIELD_FLOAT, "LowerRandomBound" ), + DEFINE_INPUT( m_flUpperRandomBound, FIELD_FLOAT, "UpperRandomBound" ), + + + // Outputs + DEFINE_OUTPUT( m_OnTimer, "OnTimer" ), + DEFINE_OUTPUT( m_OnTimerHigh, "OnTimerHigh" ), + DEFINE_OUTPUT( m_OnTimerLow, "OnTimerLow" ), + +END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::Spawn( void ) +{ + if (!m_iUseRandomTime && (m_flRefireTime < LOGIC_TIMER_MIN_INTERVAL)) + { + m_flRefireTime = LOGIC_TIMER_MIN_INTERVAL; + } + + if ( !m_iDisabled && (m_flRefireTime > 0 || m_iUseRandomTime) ) + { + Enable(); + } + else + { + Disable(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::Think( void ) +{ + FireTimer(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the time the timerentity will next fire +//----------------------------------------------------------------------------- +void CTimerEntity::ResetTimer( void ) +{ + if ( m_iDisabled ) + return; + + if ( m_iUseRandomTime ) + { + m_flRefireTime = random->RandomFloat( m_flLowerRandomBound, m_flUpperRandomBound ); + } + + SetNextThink( gpGlobals->curtime + m_flRefireTime ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::Enable( void ) +{ + m_iDisabled = FALSE; + ResetTimer(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::Disable( void ) +{ + m_iDisabled = TRUE; + SetNextThink( TICK_NEVER_THINK ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::Toggle( void ) +{ + if ( m_iDisabled ) + { + Enable(); + } + else + { + Disable(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::FireTimer( void ) +{ + if ( !m_iDisabled ) + { + // + // Up/down timers alternate between two outputs. + // + if (m_spawnflags & SF_TIMER_UPDOWN) + { + if (m_bUpDownState) + { + m_OnTimerHigh.FireOutput( this, this ); + } + else + { + m_OnTimerLow.FireOutput( this, this ); + } + + m_bUpDownState = !m_bUpDownState; + } + // + // Regular timers only fire a single output. + // + else + { + m_OnTimer.FireOutput( this, this ); + } + + ResetTimer(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::InputToggle( inputdata_t &inputdata ) +{ + Toggle(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTimerEntity::InputFireTimer( inputdata_t &inputdata ) +{ + FireTimer(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Changes the time interval between timer fires +// Resets the next firing to be time + newRefireTime +// Input : Float refire frequency in seconds. +//----------------------------------------------------------------------------- +void CTimerEntity::InputRefireTime( inputdata_t &inputdata ) +{ + float flRefireInterval = inputdata.value.Float(); + + if ( flRefireInterval < LOGIC_TIMER_MIN_INTERVAL) + { + flRefireInterval = LOGIC_TIMER_MIN_INTERVAL; + } + + if (m_flRefireTime != flRefireInterval ) + { + m_flRefireTime = flRefireInterval; + ResetTimer(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTimerEntity::InputResetTimer( inputdata_t &inputdata ) +{ + // don't reset the timer if it isn't enabled + if ( m_iDisabled ) + return; + + ResetTimer(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds to the time interval if the timer is enabled +// Input : Float time to add in seconds +//----------------------------------------------------------------------------- +void CTimerEntity::InputAddToTimer( inputdata_t &inputdata ) +{ + // don't add time if the timer isn't enabled + if ( m_iDisabled ) + return; + + // Add time to timer + float flNextThink = GetNextThink(); + SetNextThink( flNextThink += inputdata.value.Float() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Subtract from the time interval if the timer is enabled +// Input : Float time to subtract in seconds +//----------------------------------------------------------------------------- +void CTimerEntity::InputSubtractFromTimer( inputdata_t &inputdata ) +{ + // don't add time if the timer isn't enabled + if ( m_iDisabled ) + return; + + // Subtract time from the timer but don't let the timer go negative + float flNextThink = GetNextThink(); + if ( ( flNextThink - gpGlobals->curtime ) <= inputdata.value.Float() ) + { + SetNextThink( gpGlobals->curtime ); + } + else + { + SetNextThink( flNextThink -= inputdata.value.Float() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CTimerEntity::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + // print refire time + Q_snprintf(tempstr,sizeof(tempstr),"refire interval: %.2f sec", m_flRefireTime); + EntityText(text_offset,tempstr,0); + text_offset++; + + // print seconds to next fire + if ( !m_iDisabled ) + { + float flNextThink = GetNextThink(); + Q_snprintf( tempstr, sizeof( tempstr ), " firing in: %.2f sec", flNextThink - gpGlobals->curtime ); + EntityText( text_offset, tempstr, 0); + text_offset++; + } + } + return text_offset; +} + + +//----------------------------------------------------------------------------- +// Purpose: Computes a line between two entities +//----------------------------------------------------------------------------- +class CLogicLineToEntity : public CLogicalEntity +{ +public: + DECLARE_CLASS( CLogicLineToEntity, CLogicalEntity ); + + void Activate(void); + void Spawn( void ); + void Think( void ); + + // outputs + COutputVector m_Line; + + DECLARE_DATADESC(); + +private: + string_t m_SourceName; + EHANDLE m_StartEntity; + EHANDLE m_EndEntity; +}; + +LINK_ENTITY_TO_CLASS( logic_lineto, CLogicLineToEntity ); + + +BEGIN_DATADESC( CLogicLineToEntity ) + + // Keys + // target is handled in the base class, stored in field m_target + DEFINE_KEYFIELD( m_SourceName, FIELD_STRING, "source" ), + DEFINE_FIELD( m_StartEntity, FIELD_EHANDLE ), + DEFINE_FIELD( m_EndEntity, FIELD_EHANDLE ), + + // Outputs + DEFINE_OUTPUT( m_Line, "Line" ), + +END_DATADESC() + + + +//----------------------------------------------------------------------------- +// Find the entities +//----------------------------------------------------------------------------- +void CLogicLineToEntity::Activate(void) +{ + BaseClass::Activate(); + + if (m_target != NULL_STRING) + { + m_EndEntity = gEntList.FindEntityByName( NULL, m_target ); + + // + // If we were given a bad measure target, just measure sound where we are. + // + if ((m_EndEntity == NULL) || (m_EndEntity->edict() == NULL)) + { + Warning( "logic_lineto - Target not found or target with no origin!\n"); + m_EndEntity = this; + } + } + else + { + m_EndEntity = this; + } + + if (m_SourceName != NULL_STRING) + { + m_StartEntity = gEntList.FindEntityByName( NULL, m_SourceName ); + + // + // If we were given a bad measure target, just measure sound where we are. + // + if ((m_StartEntity == NULL) || (m_StartEntity->edict() == NULL)) + { + Warning( "logic_lineto - Source not found or source with no origin!\n"); + m_StartEntity = this; + } + } + else + { + m_StartEntity = this; + } +} + + +//----------------------------------------------------------------------------- +// Find the entities +//----------------------------------------------------------------------------- +void CLogicLineToEntity::Spawn(void) +{ + SetNextThink( gpGlobals->curtime + 0.01f ); +} + + +//----------------------------------------------------------------------------- +// Find the entities +//----------------------------------------------------------------------------- +void CLogicLineToEntity::Think(void) +{ + CBaseEntity* pDest = m_EndEntity.Get(); + CBaseEntity* pSrc = m_StartEntity.Get(); + if (!pDest || !pSrc || !pDest->edict() || !pSrc->edict()) + { + // Can sleep for a long time, no more lines. + m_Line.Set( vec3_origin, this, this ); + SetNextThink( gpGlobals->curtime + 10 ); + return; + } + + Vector delta; + VectorSubtract( pDest->GetAbsOrigin(), pSrc->GetAbsOrigin(), delta ); + m_Line.Set(delta, this, this); + + SetNextThink( gpGlobals->curtime + 0.01f ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Remaps a given input range to an output range. +//----------------------------------------------------------------------------- +const int SF_MATH_REMAP_IGNORE_OUT_OF_RANGE = 1; +const int SF_MATH_REMAP_CLAMP_OUTPUT_TO_RANGE = 2; + +class CMathRemap : public CLogicalEntity +{ +public: + + DECLARE_CLASS( CMathRemap, CLogicalEntity ); + + void Spawn(void); + + // Keys + float m_flInMin; + float m_flInMax; + float m_flOut1; // Output value when input is m_fInMin + float m_flOut2; // Output value when input is m_fInMax + + bool m_bEnabled; + + // Inputs + void InputValue( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + // Outputs + COutputFloat m_OutValue; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_remap, CMathRemap); + + +BEGIN_DATADESC( CMathRemap ) + + DEFINE_INPUTFUNC(FIELD_FLOAT, "InValue", InputValue ), + + DEFINE_OUTPUT(m_OutValue, "OutValue"), + + DEFINE_KEYFIELD(m_flInMin, FIELD_FLOAT, "in1"), + DEFINE_KEYFIELD(m_flInMax, FIELD_FLOAT, "in2"), + DEFINE_KEYFIELD(m_flOut1, FIELD_FLOAT, "out1"), + DEFINE_KEYFIELD(m_flOut2, FIELD_FLOAT, "out2"), + + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + +END_DATADESC() + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathRemap::Spawn(void) +{ + // + // Avoid a divide by zero in ValueChanged. + // + if (m_flInMin == m_flInMax) + { + m_flInMin = 0; + m_flInMax = 1; + } + + // + // Make sure min and max are set properly relative to one another. + // + if (m_flInMin > m_flInMax) + { + float flTemp = m_flInMin; + m_flInMin = m_flInMax; + m_flInMax = flTemp; + } + + m_bEnabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathRemap::InputEnable( inputdata_t &inputdata ) +{ + m_bEnabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathRemap::InputDisable( inputdata_t &inputdata ) +{ + m_bEnabled = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler that is called when the input value changes. +//----------------------------------------------------------------------------- +void CMathRemap::InputValue( inputdata_t &inputdata ) +{ + float flValue = inputdata.value.Float(); + + // + // Disallow out-of-range input values to avoid out-of-range output values. + // + float flClampValue = clamp(flValue, m_flInMin, m_flInMax); + + if ((flClampValue == flValue) || !FBitSet(m_spawnflags, SF_MATH_REMAP_IGNORE_OUT_OF_RANGE)) + { + // + // Remap the input value to the desired output range and update the output. + // + float flRemappedValue = m_flOut1 + (((flValue - m_flInMin) * (m_flOut2 - m_flOut1)) / (m_flInMax - m_flInMin)); + + if ( FBitSet( m_spawnflags, SF_MATH_REMAP_CLAMP_OUTPUT_TO_RANGE ) ) + { + flRemappedValue = clamp( flRemappedValue, m_flOut1, m_flOut2 ); + } + + if ( m_bEnabled == true ) + { + m_OutValue.Set(flRemappedValue, inputdata.pActivator, this); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Remaps a given input range to an output range. +//----------------------------------------------------------------------------- +const int SF_COLOR_BLEND_IGNORE_OUT_OF_RANGE = 1; + +class CMathColorBlend : public CLogicalEntity +{ +public: + + DECLARE_CLASS( CMathColorBlend, CLogicalEntity ); + + void Spawn(void); + + // Keys + float m_flInMin; + float m_flInMax; + color32 m_OutColor1; // Output color when input is m_fInMin + color32 m_OutColor2; // Output color when input is m_fInMax + + // Inputs + void InputValue( inputdata_t &inputdata ); + + // Outputs + COutputColor32 m_OutValue; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_colorblend, CMathColorBlend); + + +BEGIN_DATADESC( CMathColorBlend ) + + DEFINE_INPUTFUNC(FIELD_FLOAT, "InValue", InputValue ), + + DEFINE_OUTPUT(m_OutValue, "OutColor"), + + DEFINE_KEYFIELD(m_flInMin, FIELD_FLOAT, "inmin"), + DEFINE_KEYFIELD(m_flInMax, FIELD_FLOAT, "inmax"), + DEFINE_KEYFIELD(m_OutColor1, FIELD_COLOR32, "colormin"), + DEFINE_KEYFIELD(m_OutColor2, FIELD_COLOR32, "colormax"), + +END_DATADESC() + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMathColorBlend::Spawn(void) +{ + // + // Avoid a divide by zero in ValueChanged. + // + if (m_flInMin == m_flInMax) + { + m_flInMin = 0; + m_flInMax = 1; + } + + // + // Make sure min and max are set properly relative to one another. + // + if (m_flInMin > m_flInMax) + { + float flTemp = m_flInMin; + m_flInMin = m_flInMax; + m_flInMax = flTemp; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler that is called when the input value changes. +//----------------------------------------------------------------------------- +void CMathColorBlend::InputValue( inputdata_t &inputdata ) +{ + float flValue = inputdata.value.Float(); + + // + // Disallow out-of-range input values to avoid out-of-range output values. + // + float flClampValue = clamp(flValue, m_flInMin, m_flInMax); + if ((flClampValue == flValue) || !FBitSet(m_spawnflags, SF_COLOR_BLEND_IGNORE_OUT_OF_RANGE)) + { + // + // Remap the input value to the desired output color and update the output. + // + color32 Color; + Color.r = m_OutColor1.r + (((flClampValue - m_flInMin) * (m_OutColor2.r - m_OutColor1.r)) / (m_flInMax - m_flInMin)); + Color.g = m_OutColor1.g + (((flClampValue - m_flInMin) * (m_OutColor2.g - m_OutColor1.g)) / (m_flInMax - m_flInMin)); + Color.b = m_OutColor1.b + (((flClampValue - m_flInMin) * (m_OutColor2.b - m_OutColor1.b)) / (m_flInMax - m_flInMin)); + Color.a = m_OutColor1.a + (((flClampValue - m_flInMin) * (m_OutColor2.a - m_OutColor1.a)) / (m_flInMax - m_flInMin)); + + m_OutValue.Set(Color, inputdata.pActivator, this); + } +} + + +//----------------------------------------------------------------------------- +// Console command to set the state of a global +//----------------------------------------------------------------------------- +void CC_Global_Set( const CCommand &args ) +{ + const char *szGlobal = args[1]; + const char *szState = args[2]; + + if ( szGlobal == NULL || szState == NULL ) + { + Msg( "Usage: global_set <globalname> <state>: Sets the state of the given env_global (0 = OFF, 1 = ON, 2 = DEAD).\n" ); + return; + } + + int nState = atoi( szState ); + + int nIndex = GlobalEntity_GetIndex( szGlobal ); + + if ( nIndex >= 0 ) + { + GlobalEntity_SetState( nIndex, ( GLOBALESTATE )nState ); + } + else + { + GlobalEntity_Add( szGlobal, STRING( gpGlobals->mapname ), ( GLOBALESTATE )nState ); + } +} + +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 ); + + +//----------------------------------------------------------------------------- +// Purpose: Holds a global state that can be queried by other entities to change +// their behavior, such as "predistaster". +//----------------------------------------------------------------------------- +const int SF_GLOBAL_SET = 1; // Set global state to initial state on spawn + +class CEnvGlobal : public CLogicalEntity +{ +public: + DECLARE_CLASS( CEnvGlobal, CLogicalEntity ); + + void Spawn( void ); + + // Input handlers + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + void InputRemove( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + void InputSetCounter( inputdata_t &inputdata ); + void InputAddToCounter( inputdata_t &inputdata ); + void InputGetCounter( inputdata_t &inputdata ); + + int DrawDebugTextOverlays(void); + + DECLARE_DATADESC(); + + COutputInt m_outCounter; + + string_t m_globalstate; + int m_triggermode; + int m_initialstate; + int m_counter; // A counter value associated with this global. +}; + + +BEGIN_DATADESC( CEnvGlobal ) + + DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ), + DEFINE_FIELD( m_triggermode, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_initialstate, FIELD_INTEGER, "initialstate" ), + DEFINE_KEYFIELD( m_counter, FIELD_INTEGER, "counter" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), + DEFINE_INPUTFUNC( FIELD_VOID, "Remove", InputRemove ), + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCounter", InputSetCounter ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "AddToCounter", InputAddToCounter ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetCounter", InputGetCounter ), + + DEFINE_OUTPUT( m_outCounter, "Counter" ), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( env_global, CEnvGlobal ); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEnvGlobal::Spawn( void ) +{ + if ( !m_globalstate ) + { + UTIL_Remove( this ); + return; + } + +#ifdef HL2_EPISODIC + // if we modify the state of the physics cannon, make sure we precache the ragdoll boogie zap sound + if ( ( m_globalstate != NULL_STRING ) && ( stricmp( STRING( m_globalstate ), "super_phys_gun" ) == 0 ) ) + { + PrecacheScriptSound( "RagdollBoogie.Zap" ); + } +#endif + + if ( FBitSet( m_spawnflags, SF_GLOBAL_SET ) ) + { + if ( !GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, (GLOBALESTATE)m_initialstate ); + } + + if ( m_counter != 0 ) + { + GlobalEntity_SetCounter( m_globalstate, m_counter ); + } + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CEnvGlobal::InputTurnOn( inputdata_t &inputdata ) +{ + if ( GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_SetState( m_globalstate, GLOBAL_ON ); + } + else + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON ); + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CEnvGlobal::InputTurnOff( inputdata_t &inputdata ) +{ + if ( GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_SetState( m_globalstate, GLOBAL_OFF ); + } + else + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_OFF ); + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CEnvGlobal::InputRemove( inputdata_t &inputdata ) +{ + if ( GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_SetState( m_globalstate, GLOBAL_DEAD ); + } + else + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_DEAD ); + } +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CEnvGlobal::InputSetCounter( inputdata_t &inputdata ) +{ + if ( !GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON ); + } + + GlobalEntity_SetCounter( m_globalstate, inputdata.value.Int() ); +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CEnvGlobal::InputAddToCounter( inputdata_t &inputdata ) +{ + if ( !GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON ); + } + + GlobalEntity_AddToCounter( m_globalstate, inputdata.value.Int() ); +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CEnvGlobal::InputGetCounter( inputdata_t &inputdata ) +{ + if ( !GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, GLOBAL_ON ); + } + + m_outCounter.Set( GlobalEntity_GetCounter( m_globalstate ), inputdata.pActivator, this ); +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CEnvGlobal::InputToggle( inputdata_t &inputdata ) +{ + GLOBALESTATE oldState = GlobalEntity_GetState( m_globalstate ); + GLOBALESTATE newState; + + if ( oldState == GLOBAL_ON ) + { + newState = GLOBAL_OFF; + } + else if ( oldState == GLOBAL_OFF ) + { + newState = GLOBAL_ON; + } + else + { + return; + } + + if ( GlobalEntity_IsInTable( m_globalstate ) ) + { + GlobalEntity_SetState( m_globalstate, newState ); + } + else + { + GlobalEntity_Add( m_globalstate, gpGlobals->mapname, newState ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CEnvGlobal::DrawDebugTextOverlays(void) +{ + // Skip AIClass debug overlays + int text_offset = CBaseEntity::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + Q_snprintf(tempstr,sizeof(tempstr),"State: %s",STRING(m_globalstate)); + EntityText(text_offset,tempstr,0); + text_offset++; + + GLOBALESTATE nState = GlobalEntity_GetState( m_globalstate ); + + switch( nState ) + { + case GLOBAL_OFF: + Q_strncpy(tempstr,"Value: OFF",sizeof(tempstr)); + break; + + case GLOBAL_ON: + Q_strncpy(tempstr,"Value: ON",sizeof(tempstr)); + break; + + case GLOBAL_DEAD: + Q_strncpy(tempstr,"Value: DEAD",sizeof(tempstr)); + break; + } + EntityText(text_offset,tempstr,0); + text_offset++; + } + return text_offset; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#define MS_MAX_TARGETS 32 + +const int SF_MULTI_INIT = 1; + +class CMultiSource : public CLogicalEntity +{ +public: + DECLARE_CLASS( CMultiSource, CLogicalEntity ); + + void Spawn( ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void Use( ::CBaseEntity *pActivator, ::CBaseEntity *pCaller, USE_TYPE useType, float value ); + int ObjectCaps( void ) { return(BaseClass::ObjectCaps() | FCAP_MASTER); } + bool IsTriggered( ::CBaseEntity *pActivator ); + void Register( void ); + + DECLARE_DATADESC(); + + EHANDLE m_rgEntities[MS_MAX_TARGETS]; + int m_rgTriggered[MS_MAX_TARGETS]; + + COutputEvent m_OnTrigger; // Fired when all connections are triggered. + + int m_iTotal; + string_t m_globalstate; +}; + +BEGIN_DATADESC( CMultiSource ) + + //!!!BUGBUG FIX + DEFINE_ARRAY( m_rgEntities, FIELD_EHANDLE, MS_MAX_TARGETS ), + DEFINE_ARRAY( m_rgTriggered, FIELD_INTEGER, MS_MAX_TARGETS ), + DEFINE_FIELD( m_iTotal, FIELD_INTEGER ), + + DEFINE_KEYFIELD( m_globalstate, FIELD_STRING, "globalstate" ), + + // Function pointers + DEFINE_FUNCTION( Register ), + + // Outputs + DEFINE_OUTPUT(m_OnTrigger, "OnTrigger"), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( multisource, CMultiSource ); + + +//----------------------------------------------------------------------------- +// Purpose: Cache user entity field values until spawn is called. +// Input : szKeyName - Key to handle. +// szValue - Value for key. +// Output : Returns true if the key was handled, false if not. +//----------------------------------------------------------------------------- +bool CMultiSource::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq(szKeyName, "style") || + FStrEq(szKeyName, "height") || + FStrEq(szKeyName, "killtarget") || + FStrEq(szKeyName, "value1") || + FStrEq(szKeyName, "value2") || + FStrEq(szKeyName, "value3")) + { + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiSource::Spawn() +{ + SetNextThink( gpGlobals->curtime + 0.1f ); + m_spawnflags |= SF_MULTI_INIT; // Until it's initialized + SetThink(&CMultiSource::Register); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pActivator - +// pCaller - +// useType - +// value - +//----------------------------------------------------------------------------- +void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + int i = 0; + + // Find the entity in our list + while (i < m_iTotal) + if ( m_rgEntities[i++] == pCaller ) + break; + + // if we didn't find it, report error and leave + if (i > m_iTotal) + { + Warning("MultiSrc: Used by non member %s.\n", pCaller->edict() ? pCaller->GetClassname() : "<logical entity>"); + return; + } + + // CONSIDER: a Use input to the multisource always toggles. Could check useType for ON/OFF/TOGGLE + + m_rgTriggered[i-1] ^= 1; + + // + if ( IsTriggered( pActivator ) ) + { + DevMsg( 2, "Multisource %s enabled (%d inputs)\n", GetDebugName(), m_iTotal ); + USE_TYPE useType = USE_TOGGLE; + if ( m_globalstate != NULL_STRING ) + useType = USE_ON; + + m_OnTrigger.FireOutput(pActivator, this); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMultiSource::IsTriggered( CBaseEntity * ) +{ + // Is everything triggered? + int i = 0; + + // Still initializing? + if ( m_spawnflags & SF_MULTI_INIT ) + return 0; + + while (i < m_iTotal) + { + if (m_rgTriggered[i] == 0) + break; + i++; + } + + if (i == m_iTotal) + { + if ( !m_globalstate || GlobalEntity_GetState( m_globalstate ) == GLOBAL_ON ) + return 1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiSource::Register(void) +{ + CBaseEntity *pTarget = NULL; + + m_iTotal = 0; + memset( m_rgEntities, 0, MS_MAX_TARGETS * sizeof(EHANDLE) ); + + SetThink(&CMultiSource::SUB_DoNothing); + + // search for all entities which target this multisource (m_iName) + // dvsents2: port multisource to entity I/O! + + pTarget = gEntList.FindEntityByTarget( NULL, STRING(GetEntityName()) ); + + while ( pTarget && (m_iTotal < MS_MAX_TARGETS) ) + { + if ( pTarget ) + m_rgEntities[m_iTotal++] = pTarget; + + pTarget = gEntList.FindEntityByTarget( pTarget, STRING(GetEntityName()) ); + } + + pTarget = gEntList.FindEntityByClassname( NULL, "multi_manager" ); + while (pTarget && (m_iTotal < MS_MAX_TARGETS)) + { + if ( pTarget && pTarget->HasTarget(GetEntityName()) ) + m_rgEntities[m_iTotal++] = pTarget; + + pTarget = gEntList.FindEntityByClassname( pTarget, "multi_manager" ); + } + + m_spawnflags &= ~SF_MULTI_INIT; +} + + +//----------------------------------------------------------------------------- +// Purpose: Holds a value that can be added to and subtracted from. +//----------------------------------------------------------------------------- +class CMathCounter : public CLogicalEntity +{ + DECLARE_CLASS( CMathCounter, CLogicalEntity ); +private: + float m_flMin; // Minimum clamp value. If min and max are BOTH zero, no clamping is done. + float m_flMax; // Maximum clamp value. + bool m_bHitMin; // Set when we reach or go below our minimum value, cleared if we go above it again. + bool m_bHitMax; // Set when we reach or exceed our maximum value, cleared if we fall below it again. + + bool m_bDisabled; + + bool KeyValue(const char *szKeyName, const char *szValue); + void Spawn(void); + + int DrawDebugTextOverlays(void); + + void UpdateOutValue(CBaseEntity *pActivator, float fNewValue); + + // Inputs + void InputAdd( inputdata_t &inputdata ); + void InputDivide( inputdata_t &inputdata ); + void InputMultiply( inputdata_t &inputdata ); + void InputSetValue( inputdata_t &inputdata ); + void InputSetValueNoFire( inputdata_t &inputdata ); + void InputSubtract( inputdata_t &inputdata ); + void InputSetHitMax( inputdata_t &inputdata ); + void InputSetHitMin( inputdata_t &inputdata ); + void InputGetValue( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + // Outputs + COutputFloat m_OutValue; + COutputFloat m_OnGetValue; // Used for polling the counter value. + COutputEvent m_OnHitMin; + COutputEvent m_OnHitMax; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(math_counter, CMathCounter); + + +BEGIN_DATADESC( CMathCounter ) + + DEFINE_FIELD(m_bHitMax, FIELD_BOOLEAN), + DEFINE_FIELD(m_bHitMin, FIELD_BOOLEAN), + + // Keys + DEFINE_KEYFIELD(m_flMin, FIELD_FLOAT, "min"), + DEFINE_KEYFIELD(m_flMax, FIELD_FLOAT, "max"), + + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_FLOAT, "Add", InputAdd), + DEFINE_INPUTFUNC(FIELD_FLOAT, "Divide", InputDivide), + DEFINE_INPUTFUNC(FIELD_FLOAT, "Multiply", InputMultiply), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueNoFire", InputSetValueNoFire), + DEFINE_INPUTFUNC(FIELD_FLOAT, "Subtract", InputSubtract), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetHitMax", InputSetHitMax), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetHitMin", InputSetHitMin), + DEFINE_INPUTFUNC(FIELD_VOID, "GetValue", InputGetValue), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + // Outputs + DEFINE_OUTPUT(m_OutValue, "OutValue"), + DEFINE_OUTPUT(m_OnHitMin, "OnHitMin"), + DEFINE_OUTPUT(m_OnHitMax, "OnHitMax"), + DEFINE_OUTPUT(m_OnGetValue, "OnGetValue"), + +END_DATADESC() + + + + +//----------------------------------------------------------------------------- +// Purpose: Handles key values from the BSP before spawn is called. +//----------------------------------------------------------------------------- +bool CMathCounter::KeyValue(const char *szKeyName, const char *szValue) +{ + // + // Set the initial value of the counter. + // + if (!stricmp(szKeyName, "startvalue")) + { + m_OutValue.Init(atoi(szValue)); + return(true); + } + + return(BaseClass::KeyValue(szKeyName, szValue)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called before spawning, after key values have been set. +//----------------------------------------------------------------------------- +void CMathCounter::Spawn( void ) +{ + // + // Make sure max and min are ordered properly or clamp won't work. + // + if (m_flMin > m_flMax) + { + float flTemp = m_flMax; + m_flMax = m_flMin; + m_flMin = flTemp; + } + + // + // Clamp initial value to within the valid range. + // + if ((m_flMin != 0) || (m_flMax != 0)) + { + float flStartValue = clamp(m_OutValue.Get(), m_flMin, m_flMax); + m_OutValue.Init(flStartValue); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CMathCounter::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + Q_snprintf(tempstr,sizeof(tempstr)," min value: %f", m_flMin); + EntityText(text_offset,tempstr,0); + text_offset++; + + Q_snprintf(tempstr,sizeof(tempstr)," max value: %f", m_flMax); + EntityText(text_offset,tempstr,0); + text_offset++; + + Q_snprintf(tempstr,sizeof(tempstr),"current value: %f", m_OutValue.Get()); + EntityText(text_offset,tempstr,0); + text_offset++; + + if( m_bDisabled ) + { + Q_snprintf(tempstr,sizeof(tempstr),"*DISABLED*"); + } + else + { + Q_snprintf(tempstr,sizeof(tempstr),"Enabled."); + } + EntityText(text_offset,tempstr,0); + text_offset++; + + } + return text_offset; +} + +//----------------------------------------------------------------------------- +// Change min/max +//----------------------------------------------------------------------------- +void CMathCounter::InputSetHitMax( inputdata_t &inputdata ) +{ + m_flMax = inputdata.value.Float(); + if ( m_flMax < m_flMin ) + { + m_flMin = m_flMax; + } + UpdateOutValue( inputdata.pActivator, m_OutValue.Get() ); +} + +void CMathCounter::InputSetHitMin( inputdata_t &inputdata ) +{ + m_flMin = inputdata.value.Float(); + if ( m_flMax < m_flMin ) + { + m_flMax = m_flMin; + } + UpdateOutValue( inputdata.pActivator, m_OutValue.Get() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for adding to the accumulator value. +// Input : Float value to add. +//----------------------------------------------------------------------------- +void CMathCounter::InputAdd( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring ADD because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = m_OutValue.Get() + inputdata.value.Float(); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for multiplying the current value. +// Input : Float value to multiply the value by. +//----------------------------------------------------------------------------- +void CMathCounter::InputDivide( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring DIVIDE because it is disabled\n", GetDebugName() ); + return; + } + + if (inputdata.value.Float() != 0) + { + float fNewValue = m_OutValue.Get() / inputdata.value.Float(); + UpdateOutValue( inputdata.pActivator, fNewValue ); + } + else + { + DevMsg( 1, "LEVEL DESIGN ERROR: Divide by zero in math_value\n" ); + UpdateOutValue( inputdata.pActivator, m_OutValue.Get() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for multiplying the current value. +// Input : Float value to multiply the value by. +//----------------------------------------------------------------------------- +void CMathCounter::InputMultiply( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring MULTIPLY because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = m_OutValue.Get() * inputdata.value.Float(); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for updating the value. +// Input : Float value to set. +//----------------------------------------------------------------------------- +void CMathCounter::InputSetValue( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SETVALUE because it is disabled\n", GetDebugName() ); + return; + } + + UpdateOutValue( inputdata.pActivator, inputdata.value.Float() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for updating the value. +// Input : Float value to set. +//----------------------------------------------------------------------------- +void CMathCounter::InputSetValueNoFire( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SETVALUENOFIRE because it is disabled\n", GetDebugName() ); + return; + } + + float flNewValue = inputdata.value.Float(); + if (( m_flMin != 0 ) || (m_flMax != 0 )) + { + flNewValue = clamp(flNewValue, m_flMin, m_flMax); + } + + m_OutValue.Init( flNewValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for subtracting from the current value. +// Input : Float value to subtract. +//----------------------------------------------------------------------------- +void CMathCounter::InputSubtract( inputdata_t &inputdata ) +{ + if( m_bDisabled ) + { + DevMsg("Math Counter %s ignoring SUBTRACT because it is disabled\n", GetDebugName() ); + return; + } + + float fNewValue = m_OutValue.Get() - inputdata.value.Float(); + UpdateOutValue( inputdata.pActivator, fNewValue ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathCounter::InputGetValue( inputdata_t &inputdata ) +{ + float flOutValue = m_OutValue.Get(); + m_OnGetValue.Set( flOutValue, inputdata.pActivator, inputdata.pCaller ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathCounter::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMathCounter::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the value to the new value, clamping and firing the output value. +// Input : fNewValue - Value to set. +//----------------------------------------------------------------------------- +void CMathCounter::UpdateOutValue(CBaseEntity *pActivator, float fNewValue) +{ + if ((m_flMin != 0) || (m_flMax != 0)) + { + // + // Fire an output any time we reach or exceed our maximum value. + // + if ( fNewValue >= m_flMax ) + { + if ( !m_bHitMax ) + { + m_bHitMax = true; + m_OnHitMax.FireOutput( pActivator, this ); + } + } + else + { + m_bHitMax = false; + } + + // + // Fire an output any time we reach or go below our minimum value. + // + if ( fNewValue <= m_flMin ) + { + if ( !m_bHitMin ) + { + m_bHitMin = true; + m_OnHitMin.FireOutput( pActivator, this ); + } + } + else + { + m_bHitMin = false; + } + + fNewValue = clamp(fNewValue, m_flMin, m_flMax); + } + + m_OutValue.Set(fNewValue, pActivator, this); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Compares a single string input to up to 16 case values, firing an +// output corresponding to the case value that matched, or a default +// output if the input value didn't match any of the case values. +// +// This can also be used to fire a random output from a set of outputs. +//----------------------------------------------------------------------------- +#define MAX_LOGIC_CASES 16 + +class CLogicCase : public CLogicalEntity +{ + DECLARE_CLASS( CLogicCase, CLogicalEntity ); +private: + string_t m_nCase[MAX_LOGIC_CASES]; + + int m_nShuffleCases; + int m_nLastShuffleCase; + unsigned char m_uchShuffleCaseMap[MAX_LOGIC_CASES]; + + void Spawn(void); + + int BuildCaseMap(unsigned char *puchMap); + + // Inputs + void InputValue( inputdata_t &inputdata ); + void InputPickRandom( inputdata_t &inputdata ); + void InputPickRandomShuffle( inputdata_t &inputdata ); + + // Outputs + COutputEvent m_OnCase[MAX_LOGIC_CASES]; // Fired when the input value matches one of the case values. + COutputVariant m_OnDefault; // Fired when no match was found. + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_case, CLogicCase); + + +BEGIN_DATADESC( CLogicCase ) + +// Silence, Classcheck! +// DEFINE_ARRAY( m_nCase, FIELD_STRING, MAX_LOGIC_CASES ), + + // Keys + DEFINE_KEYFIELD(m_nCase[0], FIELD_STRING, "Case01"), + DEFINE_KEYFIELD(m_nCase[1], FIELD_STRING, "Case02"), + DEFINE_KEYFIELD(m_nCase[2], FIELD_STRING, "Case03"), + DEFINE_KEYFIELD(m_nCase[3], FIELD_STRING, "Case04"), + DEFINE_KEYFIELD(m_nCase[4], FIELD_STRING, "Case05"), + DEFINE_KEYFIELD(m_nCase[5], FIELD_STRING, "Case06"), + DEFINE_KEYFIELD(m_nCase[6], FIELD_STRING, "Case07"), + DEFINE_KEYFIELD(m_nCase[7], FIELD_STRING, "Case08"), + DEFINE_KEYFIELD(m_nCase[8], FIELD_STRING, "Case09"), + DEFINE_KEYFIELD(m_nCase[9], FIELD_STRING, "Case10"), + DEFINE_KEYFIELD(m_nCase[10], FIELD_STRING, "Case11"), + DEFINE_KEYFIELD(m_nCase[11], FIELD_STRING, "Case12"), + DEFINE_KEYFIELD(m_nCase[12], FIELD_STRING, "Case13"), + DEFINE_KEYFIELD(m_nCase[13], FIELD_STRING, "Case14"), + DEFINE_KEYFIELD(m_nCase[14], FIELD_STRING, "Case15"), + DEFINE_KEYFIELD(m_nCase[15], FIELD_STRING, "Case16"), + + DEFINE_FIELD( m_nShuffleCases, FIELD_INTEGER ), + DEFINE_FIELD( m_nLastShuffleCase, FIELD_INTEGER ), + DEFINE_ARRAY( m_uchShuffleCaseMap, FIELD_CHARACTER, MAX_LOGIC_CASES ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_INPUT, "InValue", InputValue), + DEFINE_INPUTFUNC(FIELD_VOID, "PickRandom", InputPickRandom), + DEFINE_INPUTFUNC(FIELD_VOID, "PickRandomShuffle", InputPickRandomShuffle), + + // Outputs + DEFINE_OUTPUT(m_OnCase[0], "OnCase01"), + DEFINE_OUTPUT(m_OnCase[1], "OnCase02"), + DEFINE_OUTPUT(m_OnCase[2], "OnCase03"), + DEFINE_OUTPUT(m_OnCase[3], "OnCase04"), + DEFINE_OUTPUT(m_OnCase[4], "OnCase05"), + DEFINE_OUTPUT(m_OnCase[5], "OnCase06"), + DEFINE_OUTPUT(m_OnCase[6], "OnCase07"), + DEFINE_OUTPUT(m_OnCase[7], "OnCase08"), + DEFINE_OUTPUT(m_OnCase[8], "OnCase09"), + DEFINE_OUTPUT(m_OnCase[9], "OnCase10"), + DEFINE_OUTPUT(m_OnCase[10], "OnCase11"), + DEFINE_OUTPUT(m_OnCase[11], "OnCase12"), + DEFINE_OUTPUT(m_OnCase[12], "OnCase13"), + DEFINE_OUTPUT(m_OnCase[13], "OnCase14"), + DEFINE_OUTPUT(m_OnCase[14], "OnCase15"), + DEFINE_OUTPUT(m_OnCase[15], "OnCase16"), + + DEFINE_OUTPUT(m_OnDefault, "OnDefault"), + +END_DATADESC() + + + + +//----------------------------------------------------------------------------- +// Purpose: Called before spawning, after key values have been set. +//----------------------------------------------------------------------------- +void CLogicCase::Spawn( void ) +{ + m_nLastShuffleCase = -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: Evaluates the new input value, firing the appropriate OnCaseX output +// if the input value matches one of the "CaseX" keys. +// Input : Value - Variant value to compare against the values of the case fields. +// We use a variant so that we can convert any input type to a string. +//----------------------------------------------------------------------------- +void CLogicCase::InputValue( inputdata_t &inputdata ) +{ + const char *pszValue = inputdata.value.String(); + for (int i = 0; i < MAX_LOGIC_CASES; i++) + { + if ((m_nCase[i] != NULL_STRING) && !stricmp(STRING(m_nCase[i]), pszValue)) + { + m_OnCase[i].FireOutput( inputdata.pActivator, this ); + return; + } + } + + m_OnDefault.Set( inputdata.value, inputdata.pActivator, this ); +} + + +//----------------------------------------------------------------------------- +// Count the number of valid cases, building a packed array +// that maps 0..NumCases to the actual CaseX values. +// +// This allows our zany mappers to set up cases sparsely if they desire. +// NOTE: assumes pnMap points to an array of MAX_LOGIC_CASES +//----------------------------------------------------------------------------- +int CLogicCase::BuildCaseMap(unsigned char *puchCaseMap) +{ + memset(puchCaseMap, 0, sizeof(unsigned char) * MAX_LOGIC_CASES); + + int nNumCases = 0; + for (int i = 0; i < MAX_LOGIC_CASES; i++) + { + if (m_OnCase[i].NumberOfElements() > 0) + { + puchCaseMap[nNumCases] = (unsigned char)i; + nNumCases++; + } + } + + return nNumCases; +} + + +//----------------------------------------------------------------------------- +// Purpose: Makes the case statement choose a case at random. +//----------------------------------------------------------------------------- +void CLogicCase::InputPickRandom( inputdata_t &inputdata ) +{ + unsigned char uchCaseMap[MAX_LOGIC_CASES]; + int nNumCases = BuildCaseMap( uchCaseMap ); + + // + // Choose a random case from the ones that were set up by the level designer. + // + if ( nNumCases > 0 ) + { + int nRandom = random->RandomInt(0, nNumCases - 1); + int nCase = (unsigned char)uchCaseMap[nRandom]; + + Assert(nCase < MAX_LOGIC_CASES); + + if (nCase < MAX_LOGIC_CASES) + { + m_OnCase[nCase].FireOutput( inputdata.pActivator, this ); + } + } + else + { + DevMsg( 1, "Firing PickRandom input on logic_case %s with no cases set up\n", GetDebugName() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Makes the case statement choose a case at random. +//----------------------------------------------------------------------------- +void CLogicCase::InputPickRandomShuffle( inputdata_t &inputdata ) +{ + int nAvoidCase = -1; + int nCaseCount = m_nShuffleCases; + + if ( nCaseCount == 0 ) + { + // Starting a new shuffle batch. + nCaseCount = m_nShuffleCases = BuildCaseMap( m_uchShuffleCaseMap ); + + if ( ( m_nShuffleCases > 1 ) && ( m_nLastShuffleCase != -1 ) ) + { + // Remove the previously picked case from the case map for this pick only. + // This avoids repeats across shuffle batch boundaries. + nAvoidCase = m_nLastShuffleCase; + + for (int i = 0; i < m_nShuffleCases; i++ ) + { + if ( m_uchShuffleCaseMap[i] == nAvoidCase ) + { + unsigned char uchSwap = m_uchShuffleCaseMap[i]; + m_uchShuffleCaseMap[i] = m_uchShuffleCaseMap[nCaseCount - 1]; + m_uchShuffleCaseMap[nCaseCount - 1] = uchSwap; + nCaseCount--; + break; + } + } + } + } + + // + // Choose a random case from the ones that were set up by the level designer. + // Never repeat a case within a shuffle batch, nor consecutively across batches. + // + if ( nCaseCount > 0 ) + { + int nRandom = random->RandomInt( 0, nCaseCount - 1 ); + + int nCase = m_uchShuffleCaseMap[nRandom]; + Assert(nCase < MAX_LOGIC_CASES); + + if (nCase < MAX_LOGIC_CASES) + { + m_OnCase[nCase].FireOutput( inputdata.pActivator, this ); + } + + m_uchShuffleCaseMap[nRandom] = m_uchShuffleCaseMap[m_nShuffleCases - 1]; + m_nShuffleCases--; + + m_nLastShuffleCase = nCase; + } + else + { + DevMsg( 1, "Firing PickRandom input on logic_case %s with no cases set up\n", GetDebugName() ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Compares a floating point input to a predefined value, firing an +// output to indicate the result of the comparison. +//----------------------------------------------------------------------------- +class CLogicCompare : public CLogicalEntity +{ + DECLARE_CLASS( CLogicCompare, CLogicalEntity ); + +public: + int DrawDebugTextOverlays(void); + +private: + // Inputs + void InputSetValue( inputdata_t &inputdata ); + void InputSetValueCompare( inputdata_t &inputdata ); + void InputSetCompareValue( inputdata_t &inputdata ); + void InputCompare( inputdata_t &inputdata ); + + void DoCompare(CBaseEntity *pActivator, float flInValue); + + float m_flInValue; // Place to hold the last input value for a recomparison. + float m_flCompareValue; // The value to compare the input value against. + + // Outputs + COutputFloat m_OnLessThan; // Fired when the input value is less than the compare value. + COutputFloat m_OnEqualTo; // Fired when the input value is equal to the compare value. + COutputFloat m_OnNotEqualTo; // Fired when the input value is not equal to the compare value. + COutputFloat m_OnGreaterThan; // Fired when the input value is greater than the compare value. + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_compare, CLogicCompare); + + +BEGIN_DATADESC( CLogicCompare ) + + // Keys + DEFINE_KEYFIELD(m_flCompareValue, FIELD_FLOAT, "CompareValue"), + DEFINE_KEYFIELD(m_flInValue, FIELD_FLOAT, "InitialValue"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValue", InputSetValue), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetValueCompare", InputSetValueCompare), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetCompareValue", InputSetCompareValue), + DEFINE_INPUTFUNC(FIELD_VOID, "Compare", InputCompare), + + // Outputs + DEFINE_OUTPUT(m_OnEqualTo, "OnEqualTo"), + DEFINE_OUTPUT(m_OnNotEqualTo, "OnNotEqualTo"), + DEFINE_OUTPUT(m_OnGreaterThan, "OnGreaterThan"), + DEFINE_OUTPUT(m_OnLessThan, "OnLessThan"), + +END_DATADESC() + + + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for a new input value without performing a comparison. +//----------------------------------------------------------------------------- +void CLogicCompare::InputSetValue( inputdata_t &inputdata ) +{ + m_flInValue = inputdata.value.Float(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for a setting a new value and doing the comparison. +//----------------------------------------------------------------------------- +void CLogicCompare::InputSetValueCompare( inputdata_t &inputdata ) +{ + m_flInValue = inputdata.value.Float(); + DoCompare( inputdata.pActivator, m_flInValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for a new input value without performing a comparison. +//----------------------------------------------------------------------------- +void CLogicCompare::InputSetCompareValue( inputdata_t &inputdata ) +{ + m_flCompareValue = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for forcing a recompare of the last input value. +//----------------------------------------------------------------------------- +void CLogicCompare::InputCompare( inputdata_t &inputdata ) +{ + DoCompare( inputdata.pActivator, m_flInValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Compares the input value to the compare value, firing the appropriate +// output(s) based on the comparison result. +// Input : flInValue - Value to compare against the comparison value. +//----------------------------------------------------------------------------- +void CLogicCompare::DoCompare(CBaseEntity *pActivator, float flInValue) +{ + if (flInValue == m_flCompareValue) + { + m_OnEqualTo.Set(flInValue, pActivator, this); + } + else + { + m_OnNotEqualTo.Set(flInValue, pActivator, this); + + if (flInValue > m_flCompareValue) + { + m_OnGreaterThan.Set(flInValue, pActivator, this); + } + else + { + m_OnLessThan.Set(flInValue, pActivator, this); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CLogicCompare::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + // print duration + Q_snprintf(tempstr,sizeof(tempstr)," Initial Value: %f", m_flInValue); + EntityText(text_offset,tempstr,0); + text_offset++; + + // print hold time + Q_snprintf(tempstr,sizeof(tempstr)," Compare Value: %f", m_flCompareValue); + EntityText(text_offset,tempstr,0); + text_offset++; + } + return text_offset; +} + +//----------------------------------------------------------------------------- +// Purpose: Tests a boolean value, firing an output to indicate whether the +// value was true or false. +//----------------------------------------------------------------------------- +class CLogicBranch : public CLogicalEntity +{ + DECLARE_CLASS( CLogicBranch, CLogicalEntity ); + +public: + + void UpdateOnRemove(); + + void AddLogicBranchListener( CBaseEntity *pEntity ); + inline bool GetLogicBranchState(); + virtual int DrawDebugTextOverlays( void ); + +private: + + enum LogicBranchFire_t + { + LOGIC_BRANCH_FIRE, + LOGIC_BRANCH_NO_FIRE, + }; + + // Inputs + void InputSetValue( inputdata_t &inputdata ); + void InputSetValueTest( inputdata_t &inputdata ); + void InputToggle( inputdata_t &inputdata ); + void InputToggleTest( inputdata_t &inputdata ); + void InputTest( inputdata_t &inputdata ); + + void UpdateValue(bool bNewValue, CBaseEntity *pActivator, LogicBranchFire_t eFire); + + bool m_bInValue; // Place to hold the last input value for a future test. + + CUtlVector<EHANDLE> m_Listeners; // A list of logic_branch_listeners that are monitoring us. + + // Outputs + COutputEvent m_OnTrue; // Fired when the value is true. + COutputEvent m_OnFalse; // Fired when the value is false. + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_branch, CLogicBranch); + + +BEGIN_DATADESC( CLogicBranch ) + + // Keys + DEFINE_KEYFIELD(m_bInValue, FIELD_BOOLEAN, "InitialValue"), + + DEFINE_UTLVECTOR( m_Listeners, FIELD_EHANDLE ), + + // Inputs + DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetValue", InputSetValue), + DEFINE_INPUTFUNC(FIELD_BOOLEAN, "SetValueTest", InputSetValueTest), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + DEFINE_INPUTFUNC(FIELD_VOID, "ToggleTest", InputToggleTest), + DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest), + + // Outputs + DEFINE_OUTPUT(m_OnTrue, "OnTrue"), + DEFINE_OUTPUT(m_OnFalse, "OnFalse"), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicBranch::UpdateOnRemove() +{ + for ( int i = 0; i < m_Listeners.Count(); i++ ) + { + CBaseEntity *pEntity = m_Listeners.Element( i ).Get(); + if ( pEntity ) + { + g_EventQueue.AddEvent( this, "_OnLogicBranchRemoved", 0, this, this ); + } + } + + BaseClass::UpdateOnRemove(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler to set a new input value without firing outputs. +// Input : Boolean value to set. +//----------------------------------------------------------------------------- +void CLogicBranch::InputSetValue( inputdata_t &inputdata ) +{ + UpdateValue( inputdata.value.Bool(), inputdata.pActivator, LOGIC_BRANCH_NO_FIRE ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler to set a new input value and fire appropriate outputs. +// Input : Boolean value to set. +//----------------------------------------------------------------------------- +void CLogicBranch::InputSetValueTest( inputdata_t &inputdata ) +{ + UpdateValue( inputdata.value.Bool(), inputdata.pActivator, LOGIC_BRANCH_FIRE ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for toggling the boolean value without firing outputs. +//----------------------------------------------------------------------------- +void CLogicBranch::InputToggle( inputdata_t &inputdata ) +{ + UpdateValue( !m_bInValue, inputdata.pActivator, LOGIC_BRANCH_NO_FIRE ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for toggling the boolean value and then firing the +// appropriate output based on the new value. +//----------------------------------------------------------------------------- +void CLogicBranch::InputToggleTest( inputdata_t &inputdata ) +{ + UpdateValue( !m_bInValue, inputdata.pActivator, LOGIC_BRANCH_FIRE ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for forcing a test of the last input value. +//----------------------------------------------------------------------------- +void CLogicBranch::InputTest( inputdata_t &inputdata ) +{ + UpdateValue( m_bInValue, inputdata.pActivator, LOGIC_BRANCH_FIRE ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Tests the last input value, firing the appropriate output based on +// the test result. +// Input : bInValue - +//----------------------------------------------------------------------------- +void CLogicBranch::UpdateValue( bool bNewValue, CBaseEntity *pActivator, LogicBranchFire_t eFire ) +{ + if ( m_bInValue != bNewValue ) + { + m_bInValue = bNewValue; + + for ( int i = 0; i < m_Listeners.Count(); i++ ) + { + CBaseEntity *pEntity = m_Listeners.Element( i ).Get(); + if ( pEntity ) + { + g_EventQueue.AddEvent( pEntity, "_OnLogicBranchChanged", 0, this, this ); + } + } + } + + if ( eFire == LOGIC_BRANCH_FIRE ) + { + if ( m_bInValue ) + { + m_OnTrue.FireOutput( pActivator, this ); + } + else + { + m_OnFalse.FireOutput( pActivator, this ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Accessor for logic_branchlist to test the value of the branch on demand. +//----------------------------------------------------------------------------- +bool CLogicBranch::GetLogicBranchState() +{ + return m_bInValue; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicBranch::AddLogicBranchListener( CBaseEntity *pEntity ) +{ + if ( m_Listeners.Find( pEntity ) == -1 ) + { + m_Listeners.AddToTail( pEntity ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CLogicBranch::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + // print refire time + Q_snprintf( tempstr, sizeof(tempstr), "Branch value: %s", (m_bInValue) ? "TRUE" : "FALSE" ); + EntityText( text_offset, tempstr, 0 ); + text_offset++; + } + + return text_offset; +} + +//----------------------------------------------------------------------------- +// Purpose: Autosaves when triggered +//----------------------------------------------------------------------------- +class CLogicAutosave : public CLogicalEntity +{ + DECLARE_CLASS( CLogicAutosave, CLogicalEntity ); + +protected: + // Inputs + void InputSave( inputdata_t &inputdata ); + void InputSaveDangerous( inputdata_t &inputdata ); + void InputSetMinHitpointsThreshold( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + bool m_bForceNewLevelUnit; + int m_minHitPoints; + int m_minHitPointsToCommit; +}; + +LINK_ENTITY_TO_CLASS(logic_autosave, CLogicAutosave); + +BEGIN_DATADESC( CLogicAutosave ) + DEFINE_KEYFIELD( m_bForceNewLevelUnit, FIELD_BOOLEAN, "NewLevelUnit" ), + DEFINE_KEYFIELD( m_minHitPoints, FIELD_INTEGER, "MinimumHitPoints" ), + DEFINE_KEYFIELD( m_minHitPointsToCommit, FIELD_INTEGER, "MinHitPointsToCommit" ), + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Save", InputSave ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SaveDangerous", InputSaveDangerous ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMinHitpointsThreshold", InputSetMinHitpointsThreshold ), +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Save! +//----------------------------------------------------------------------------- +void CLogicAutosave::InputSave( inputdata_t &inputdata ) +{ + if ( m_bForceNewLevelUnit ) + { + engine->ClearSaveDir(); + } + + engine->ServerCommand( "autosave\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Save safely! +//----------------------------------------------------------------------------- +void CLogicAutosave::InputSaveDangerous( inputdata_t &inputdata ) +{ + CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); + + if ( g_ServerGameDLL.m_fAutoSaveDangerousTime != 0.0f && g_ServerGameDLL.m_fAutoSaveDangerousTime >= gpGlobals->curtime ) + { + // A previous dangerous auto save was waiting to become safe + + if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime ) + { + // The player isn't dead, so make the dangerous auto save safe + engine->ServerCommand( "autosavedangerousissafe\n" ); + } + } + + if ( m_bForceNewLevelUnit ) + { + engine->ClearSaveDir(); + } + + if ( pPlayer->GetHealth() >= m_minHitPoints ) + { + engine->ServerCommand( "autosavedangerous\n" ); + g_ServerGameDLL.m_fAutoSaveDangerousTime = gpGlobals->curtime + inputdata.value.Float(); + + // Player must have this much health when we go to commit, or we don't commit. + g_ServerGameDLL.m_fAutoSaveDangerousMinHealthToCommit = m_minHitPointsToCommit; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Autosaves when triggered +//----------------------------------------------------------------------------- +class CLogicActiveAutosave : public CLogicAutosave +{ + DECLARE_CLASS( CLogicActiveAutosave, CLogicAutosave ); + + void InputEnable( inputdata_t &inputdata ) + { + m_flStartTime = -1; + SetThink( &CLogicActiveAutosave::SaveThink ); + SetNextThink( gpGlobals->curtime ); + } + + void InputDisable( inputdata_t &inputdata ) + { + SetThink( NULL ); + } + + void SaveThink() + { + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer ) + { + if ( m_flStartTime < 0 ) + { + if ( pPlayer->GetHealth() <= m_minHitPoints ) + { + m_flStartTime = gpGlobals->curtime; + } + } + else + { + if ( pPlayer->GetHealth() >= m_TriggerHitPoints ) + { + inputdata_t inputdata; + DevMsg( 2, "logic_active_autosave (%s, %d) triggered\n", STRING( GetEntityName() ), entindex() ); + if ( !m_flDangerousTime ) + { + InputSave( inputdata ); + } + else + { + inputdata.value.SetFloat( m_flDangerousTime ); + InputSaveDangerous( inputdata ); + } + m_flStartTime = -1; + } + else if ( m_flTimeToTrigger > 0 && gpGlobals->curtime - m_flStartTime > m_flTimeToTrigger ) + { + m_flStartTime = -1; + } + } + } + + float thinkInterval = ( m_flStartTime < 0 ) ? 1.0 : 0.5; + SetNextThink( gpGlobals->curtime + thinkInterval ); + } + + DECLARE_DATADESC(); + + int m_TriggerHitPoints; + float m_flTimeToTrigger; + float m_flStartTime; + float m_flDangerousTime; +}; + +LINK_ENTITY_TO_CLASS(logic_active_autosave, CLogicActiveAutosave); + +BEGIN_DATADESC( CLogicActiveAutosave ) + DEFINE_KEYFIELD( m_TriggerHitPoints, FIELD_INTEGER, "TriggerHitPoints" ), + DEFINE_KEYFIELD( m_flTimeToTrigger, FIELD_FLOAT, "TimeToTrigger" ), + DEFINE_KEYFIELD( m_flDangerousTime, FIELD_FLOAT, "DangerousTime" ), + DEFINE_FIELD( m_flStartTime, FIELD_TIME ), + DEFINE_THINKFUNC( SaveThink ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: Keyfield set func +//----------------------------------------------------------------------------- +void CLogicAutosave::InputSetMinHitpointsThreshold( inputdata_t &inputdata ) +{ + int setTo = inputdata.value.Int(); + AssertMsg1(setTo >= 0 && setTo <= 100, "Tried to set autosave MinHitpointsThreshold to %d!\n", setTo); + m_minHitPoints = setTo; +} + +// Finds the named physics object. If no name, returns the world +// If a name is specified and an object not found - errors are reported +IPhysicsObject *FindPhysicsObjectByNameOrWorld( string_t name, CBaseEntity *pErrorEntity ) +{ + if ( !name ) + return g_PhysWorldObject; + + IPhysicsObject *pPhysics = FindPhysicsObjectByName( name.ToCStr(), pErrorEntity ); + if ( !pPhysics ) + { + DevWarning("%s: can't find %s\n", pErrorEntity->GetClassname(), name.ToCStr()); + } + return pPhysics; +} + +class CLogicCollisionPair : public CLogicalEntity +{ + DECLARE_CLASS( CLogicCollisionPair, CLogicalEntity ); +public: + + void EnableCollisions( bool bEnable ) + { + IPhysicsObject *pPhysics0 = FindPhysicsObjectByNameOrWorld( m_nameAttach1, this ); + IPhysicsObject *pPhysics1 = FindPhysicsObjectByNameOrWorld( m_nameAttach2, this ); + + // need two different objects to do anything + if ( pPhysics0 && pPhysics1 && pPhysics0 != pPhysics1 ) + { + m_disabled = !bEnable; + m_succeeded = true; + if ( bEnable ) + { + PhysEnableEntityCollisions( pPhysics0, pPhysics1 ); + } + else + { + PhysDisableEntityCollisions( pPhysics0, pPhysics1 ); + } + } + else + { + m_succeeded = false; + } + } + + void Activate( void ) + { + if ( m_disabled ) + { + EnableCollisions( false ); + } + BaseClass::Activate(); + } + + void InputDisableCollisions( inputdata_t &inputdata ) + { + if ( m_succeeded && m_disabled ) + return; + EnableCollisions( false ); + } + + void InputEnableCollisions( inputdata_t &inputdata ) + { + if ( m_succeeded && !m_disabled ) + return; + EnableCollisions( true ); + } + // If Activate() becomes PostSpawn() + //void OnRestore() { Activate(); } + + DECLARE_DATADESC(); + +private: + string_t m_nameAttach1; + string_t m_nameAttach2; + bool m_disabled; + bool m_succeeded; +}; + +BEGIN_DATADESC( CLogicCollisionPair ) + DEFINE_KEYFIELD( m_nameAttach1, FIELD_STRING, "attach1" ), + DEFINE_KEYFIELD( m_nameAttach2, FIELD_STRING, "attach2" ), + DEFINE_KEYFIELD( m_disabled, FIELD_BOOLEAN, "startdisabled" ), + DEFINE_FIELD( m_succeeded, FIELD_BOOLEAN ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "DisableCollisions", InputDisableCollisions ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableCollisions", InputEnableCollisions ), +END_DATADESC() + +LINK_ENTITY_TO_CLASS( logic_collision_pair, CLogicCollisionPair ); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#define MAX_LOGIC_BRANCH_NAMES 16 + +class CLogicBranchList : public CLogicalEntity +{ + DECLARE_CLASS( CLogicBranchList, CLogicalEntity ); + + virtual void Spawn(); + virtual void Activate(); + virtual int DrawDebugTextOverlays( void ); + +private: + + enum LogicBranchListenerLastState_t + { + LOGIC_BRANCH_LISTENER_NOT_INIT = 0, + LOGIC_BRANCH_LISTENER_ALL_TRUE, + LOGIC_BRANCH_LISTENER_ALL_FALSE, + LOGIC_BRANCH_LISTENER_MIXED, + }; + + void DoTest( CBaseEntity *pActivator ); + + string_t m_nLogicBranchNames[MAX_LOGIC_BRANCH_NAMES]; + CUtlVector<EHANDLE> m_LogicBranchList; + LogicBranchListenerLastState_t m_eLastState; + + // Inputs + void Input_OnLogicBranchRemoved( inputdata_t &inputdata ); + void Input_OnLogicBranchChanged( inputdata_t &inputdata ); + void InputTest( inputdata_t &inputdata ); + + // Outputs + COutputEvent m_OnAllTrue; // Fired when all the registered logic_branches become true. + COutputEvent m_OnAllFalse; // Fired when all the registered logic_branches become false. + COutputEvent m_OnMixed; // Fired when one of the registered logic branches changes, but not all are true or false. + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(logic_branch_listener, CLogicBranchList); + + +BEGIN_DATADESC( CLogicBranchList ) + + // Silence, classcheck! + //DEFINE_ARRAY( m_nLogicBranchNames, FIELD_STRING, MAX_LOGIC_BRANCH_NAMES ), + + // Keys + DEFINE_KEYFIELD( m_nLogicBranchNames[0], FIELD_STRING, "Branch01" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[1], FIELD_STRING, "Branch02" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[2], FIELD_STRING, "Branch03" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[3], FIELD_STRING, "Branch04" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[4], FIELD_STRING, "Branch05" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[5], FIELD_STRING, "Branch06" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[6], FIELD_STRING, "Branch07" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[7], FIELD_STRING, "Branch08" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[8], FIELD_STRING, "Branch09" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[9], FIELD_STRING, "Branch10" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[10], FIELD_STRING, "Branch11" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[11], FIELD_STRING, "Branch12" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[12], FIELD_STRING, "Branch13" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[13], FIELD_STRING, "Branch14" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[14], FIELD_STRING, "Branch15" ), + DEFINE_KEYFIELD( m_nLogicBranchNames[15], FIELD_STRING, "Branch16" ), + + DEFINE_UTLVECTOR( m_LogicBranchList, FIELD_EHANDLE ), + + DEFINE_FIELD( m_eLastState, FIELD_INTEGER ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_INPUT, "Test", InputTest ), + DEFINE_INPUTFUNC( FIELD_INPUT, "_OnLogicBranchChanged", Input_OnLogicBranchChanged ), + DEFINE_INPUTFUNC( FIELD_INPUT, "_OnLogicBranchRemoved", Input_OnLogicBranchRemoved ), + + // Outputs + DEFINE_OUTPUT( m_OnAllTrue, "OnAllTrue" ), + DEFINE_OUTPUT( m_OnAllFalse, "OnAllFalse" ), + DEFINE_OUTPUT( m_OnMixed, "OnMixed" ), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: Called before spawning, after key values have been set. +//----------------------------------------------------------------------------- +void CLogicBranchList::Spawn( void ) +{ +} + + +//----------------------------------------------------------------------------- +// Finds all the logic_branches that we are monitoring and register ourselves with them. +//----------------------------------------------------------------------------- +void CLogicBranchList::Activate( void ) +{ + for ( int i = 0; i < MAX_LOGIC_BRANCH_NAMES; i++ ) + { + CBaseEntity *pEntity = NULL; + while ( ( pEntity = gEntList.FindEntityGeneric( pEntity, STRING( m_nLogicBranchNames[i] ), this ) ) != NULL ) + { + if ( FClassnameIs( pEntity, "logic_branch" ) ) + { + CLogicBranch *pBranch = (CLogicBranch *)pEntity; + pBranch->AddLogicBranchListener( this ); + m_LogicBranchList.AddToTail( pBranch ); + } + else + { + DevWarning( "logic_branchlist %s refers to entity %s, which is not a logic_branch\n", GetDebugName(), pEntity->GetDebugName() ); + } + } + } + + BaseClass::Activate(); +} + + +//----------------------------------------------------------------------------- +// Called when a monitored logic branch is deleted from the world, since that +// might affect our final result. +//----------------------------------------------------------------------------- +void CLogicBranchList::Input_OnLogicBranchRemoved( inputdata_t &inputdata ) +{ + int nIndex = m_LogicBranchList.Find( inputdata.pActivator ); + if ( nIndex != -1 ) + { + m_LogicBranchList.FastRemove( nIndex ); + } + + // See if this logic_branch's deletion affects the final result. + DoTest( inputdata.pActivator ); +} + + +//----------------------------------------------------------------------------- +// Called when the value of a monitored logic branch changes. +//----------------------------------------------------------------------------- +void CLogicBranchList::Input_OnLogicBranchChanged( inputdata_t &inputdata ) +{ + DoTest( inputdata.pActivator ); +} + + +//----------------------------------------------------------------------------- +// Input handler to manually test the monitored logic branches and fire the +// appropriate output. +//----------------------------------------------------------------------------- +void CLogicBranchList::InputTest( inputdata_t &inputdata ) +{ + // Force an output. + m_eLastState = LOGIC_BRANCH_LISTENER_NOT_INIT; + + DoTest( inputdata.pActivator ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicBranchList::DoTest( CBaseEntity *pActivator ) +{ + bool bOneTrue = false; + bool bOneFalse = false; + + for ( int i = 0; i < m_LogicBranchList.Count(); i++ ) + { + CLogicBranch *pBranch = (CLogicBranch *)m_LogicBranchList.Element( i ).Get(); + if ( pBranch && pBranch->GetLogicBranchState() ) + { + bOneTrue = true; + } + else + { + bOneFalse = true; + } + } + + // Only fire the output if the new result differs from the last result. + if ( bOneTrue && !bOneFalse ) + { + if ( m_eLastState != LOGIC_BRANCH_LISTENER_ALL_TRUE ) + { + m_OnAllTrue.FireOutput( pActivator, this ); + m_eLastState = LOGIC_BRANCH_LISTENER_ALL_TRUE; + } + } + else if ( bOneFalse && !bOneTrue ) + { + if ( m_eLastState != LOGIC_BRANCH_LISTENER_ALL_FALSE ) + { + m_OnAllFalse.FireOutput( pActivator, this ); + m_eLastState = LOGIC_BRANCH_LISTENER_ALL_FALSE; + } + } + else + { + if ( m_eLastState != LOGIC_BRANCH_LISTENER_MIXED ) + { + m_OnMixed.FireOutput( pActivator, this ); + m_eLastState = LOGIC_BRANCH_LISTENER_MIXED; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CLogicBranchList::DrawDebugTextOverlays( void ) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + + for ( int i = 0; i < m_LogicBranchList.Count(); i++ ) + { + CLogicBranch *pBranch = (CLogicBranch *)m_LogicBranchList.Element( i ).Get(); + if ( pBranch ) + { + Q_snprintf( tempstr, sizeof(tempstr), "Branch (%s): %s", STRING(pBranch->GetEntityName()), (pBranch->GetLogicBranchState()) ? "TRUE" : "FALSE" ); + EntityText( text_offset, tempstr, 0 ); + text_offset++; + } + } + } + + return text_offset; +} |