From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/logicentities.cpp | 5496 +++++++++++++++++----------------- 1 file changed, 2748 insertions(+), 2748 deletions(-) (limited to 'mp/src/game/server/logicentities.cpp') 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 : 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 : 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() : ""); - 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 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 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 : 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 : 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() : ""); + 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 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 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; +} -- cgit v1.2.3