aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/cbase.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/cbase.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/cbase.cpp')
-rw-r--r--sp/src/game/server/cbase.cpp3674
1 files changed, 1837 insertions, 1837 deletions
diff --git a/sp/src/game/server/cbase.cpp b/sp/src/game/server/cbase.cpp
index 944e6841..d748a1dd 100644
--- a/sp/src/game/server/cbase.cpp
+++ b/sp/src/game/server/cbase.cpp
@@ -1,1837 +1,1837 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-/*
-Entity Data Descriptions
-
-Each entity has an array which defines it's data in way that is useful for
-entity communication, parsing initial values from the map, and save/restore.
-
-each entity has to have the following line in it's class declaration:
-
- DECLARE_DATADESC();
-
-this line defines that it has an m_DataDesc[] array, and declares functions through
-which various subsystems can iterate the data.
-
-In it's implementation, each entity has to have:
-
- typedescription_t CBaseEntity::m_DataDesc[] = { ... }
-
-in which all it's data is defined (see below), followed by:
-
-
-which implements the functions necessary for iterating through an entities data desc.
-
-There are several types of data:
-
- FIELD : this is a variable which gets saved & loaded from disk
- KEY : this variable can be read in from the map file
- GLOBAL : a global field is actually local; it is saved/restored, but is actually
- unique to the entity on each level.
- CUSTOM : the save/restore parsing functions are described by the user.
- ARRAY : an array of values
- OUTPUT : a variable or event that can be connected to other entities (see below)
- INPUTFUNC : maps a string input to a function pointer. Outputs connected to this input
- will call the notify function when fired.
- INPUT : maps a string input to a member variable. Outputs connected to this input
- will update the input data value when fired.
- INPUTNOTIFY : maps a string input to a member variable/function pointer combo. Outputs
- connected to this input will update the data value and call the notify
- function when fired.
-
-some of these can overlap. all the data descriptions usable are:
-
- DEFINE_FIELD( name, fieldtype )
- DEFINE_KEYFIELD( name, fieldtype, mapname )
- DEFINE_KEYFIELD_NOTSAVED( name, fieldtype, mapname )
- DEFINE_ARRAY( name, fieldtype, count )
- DEFINE_GLOBAL_FIELD(name, fieldtype )
- DEFINE_CUSTOM_FIELD(name, datafuncs, mapname )
- DEFINE_GLOBAL_KEYFIELD(name, fieldtype, mapname )
-
-where:
- type is the name of the class (eg. CBaseEntity)
- name is the name of the variable in the class (eg. m_iHealth)
- fieldtype is the type of data (FIELD_STRING, FIELD_INTEGER, etc)
- mapname is the string by which this variable is associated with map file data
- count is the number of items in the array
- datafuncs is a struct containing function pointers for a custom-defined save/restore/parse
-
-OUTPUTS:
-
- DEFINE_OUTPUT( outputvar, outputname )
-
- This maps the string 'outputname' to the COutput-derived member variable outputvar. In the VMF
- file these outputs can be hooked up to inputs (see above). Whenever the internal state
- of an entity changes it will often fire off outputs so that map makers can hook up behaviors.
- e.g. A door entity would have OnDoorOpen, OnDoorClose, OnTouched, etc outputs.
-*/
-
-
-#include "cbase.h"
-#include "entitylist.h"
-#include "mapentities_shared.h"
-#include "isaverestore.h"
-#include "eventqueue.h"
-#include "entityinput.h"
-#include "entityoutput.h"
-#include "mempool.h"
-#include "tier1/strtools.h"
-#include "datacache/imdlcache.h"
-#include "env_debughistory.h"
-
-#include "tier0/vprof.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-extern ISaveRestoreOps *variantFuncs; // function pointer set for save/restoring variants
-
-BEGIN_SIMPLE_DATADESC( CEventAction )
- DEFINE_FIELD( m_iTarget, FIELD_STRING ),
- DEFINE_FIELD( m_iTargetInput, FIELD_STRING ),
- DEFINE_FIELD( m_iParameter, FIELD_STRING ),
- DEFINE_FIELD( m_flDelay, FIELD_FLOAT ),
- DEFINE_FIELD( m_nTimesToFire, FIELD_INTEGER ),
- DEFINE_FIELD( m_iIDStamp, FIELD_INTEGER ),
-
- // This is dealt with by the Restore method
- // DEFINE_FIELD( m_pNext, CEventAction ),
-END_DATADESC()
-
-
-// ID Stamp used to uniquely identify every output
-int CEventAction::s_iNextIDStamp = 0;
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Creates an event action and assigns it an unique ID stamp.
-// Input : ActionData - the map file data block descibing the event action.
-//-----------------------------------------------------------------------------
-CEventAction::CEventAction( const char *ActionData )
-{
- m_pNext = NULL;
- m_iIDStamp = ++s_iNextIDStamp;
-
- m_flDelay = 0;
- m_iTarget = NULL_STRING;
- m_iParameter = NULL_STRING;
- m_iTargetInput = NULL_STRING;
- m_nTimesToFire = EVENT_FIRE_ALWAYS;
-
- if (ActionData == NULL)
- return;
-
- char szToken[256];
-
- //
- // Parse the target name.
- //
- const char *psz = nexttoken(szToken, ActionData, ',');
- if (szToken[0] != '\0')
- {
- m_iTarget = AllocPooledString(szToken);
- }
-
- //
- // Parse the input name.
- //
- psz = nexttoken(szToken, psz, ',');
- if (szToken[0] != '\0')
- {
- m_iTargetInput = AllocPooledString(szToken);
- }
- else
- {
- m_iTargetInput = AllocPooledString("Use");
- }
-
- //
- // Parse the parameter override.
- //
- psz = nexttoken(szToken, psz, ',');
- if (szToken[0] != '\0')
- {
- m_iParameter = AllocPooledString(szToken);
- }
-
- //
- // Parse the delay.
- //
- psz = nexttoken(szToken, psz, ',');
- if (szToken[0] != '\0')
- {
- m_flDelay = atof(szToken);
- }
-
- //
- // Parse the number of times to fire.
- //
- nexttoken(szToken, psz, ',');
- if (szToken[0] != '\0')
- {
- m_nTimesToFire = atoi(szToken);
- if (m_nTimesToFire == 0)
- {
- m_nTimesToFire = EVENT_FIRE_ALWAYS;
- }
- }
-}
-
-
-// this memory pool stores blocks around the size of CEventAction/inputitem_t structs
-// can be used for other blocks; will error if to big a block is tried to be allocated
-CUtlMemoryPool g_EntityListPool( MAX(sizeof(CEventAction),sizeof(CMultiInputVar::inputitem_t)), 512, CUtlMemoryPool::GROW_FAST, "g_EntityListPool" );
-
-#include "tier0/memdbgoff.h"
-
-void *CEventAction::operator new( size_t stAllocateBlock )
-{
- return g_EntityListPool.Alloc( stAllocateBlock );
-}
-
-void *CEventAction::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
-{
- return g_EntityListPool.Alloc( stAllocateBlock );
-}
-
-void CEventAction::operator delete( void *pMem )
-{
- g_EntityListPool.Free( pMem );
-}
-
-#include "tier0/memdbgon.h"
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the highest-valued delay in our list of event actions.
-//-----------------------------------------------------------------------------
-float CBaseEntityOutput::GetMaxDelay(void)
-{
- float flMaxDelay = 0;
- CEventAction *ev = m_ActionList;
-
- while (ev != NULL)
- {
- if (ev->m_flDelay > flMaxDelay)
- {
- flMaxDelay = ev->m_flDelay;
- }
- ev = ev->m_pNext;
- }
-
- return(flMaxDelay);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Destructor.
-//-----------------------------------------------------------------------------
-CBaseEntityOutput::~CBaseEntityOutput()
-{
- CEventAction *ev = m_ActionList;
- while (ev != NULL)
- {
- CEventAction *pNext = ev->m_pNext;
- delete ev;
- ev = pNext;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Fires the event, causing a sequence of action to occur in other ents.
-// Input : pActivator - Entity that initiated this sequence of actions.
-// pCaller - Entity that is actually causing the event.
-//-----------------------------------------------------------------------------
-void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
-{
- //
- // Iterate through all eventactions and fire them off.
- //
- CEventAction *ev = m_ActionList;
- CEventAction *prev = NULL;
-
- while (ev != NULL)
- {
- if (ev->m_iParameter == NULL_STRING)
- {
- //
- // Post the event with the default parameter.
- //
- g_EventQueue.AddEvent( STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), Value, ev->m_flDelay + fDelay, pActivator, pCaller, ev->m_iIDStamp );
- }
- else
- {
- //
- // Post the event with a parameter override.
- //
- variant_t ValueOverride;
- ValueOverride.SetString( ev->m_iParameter );
- g_EventQueue.AddEvent( STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), ValueOverride, ev->m_flDelay, pActivator, pCaller, ev->m_iIDStamp );
- }
-
- if ( ev->m_flDelay )
- {
- char szBuffer[256];
- Q_snprintf( szBuffer,
- sizeof(szBuffer),
- "(%0.2f) output: (%s,%s) -> (%s,%s,%.1f)(%s)\n",
- engine->GetServerTime(),
- pCaller ? STRING(pCaller->m_iClassname) : "NULL",
- pCaller ? STRING(pCaller->GetEntityName()) : "NULL",
- STRING(ev->m_iTarget),
- STRING(ev->m_iTargetInput),
- ev->m_flDelay,
- STRING(ev->m_iParameter) );
-
- DevMsg( 2, "%s", szBuffer );
- ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
- }
- else
- {
- char szBuffer[256];
- Q_snprintf( szBuffer,
- sizeof(szBuffer),
- "(%0.2f) output: (%s,%s) -> (%s,%s)(%s)\n",
- engine->GetServerTime(),
- pCaller ? STRING(pCaller->m_iClassname) : "NULL",
- pCaller ? STRING(pCaller->GetEntityName()) : "NULL", STRING(ev->m_iTarget),
- STRING(ev->m_iTargetInput),
- STRING(ev->m_iParameter) );
-
- DevMsg( 2, "%s", szBuffer );
- ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
- }
-
- if ( pCaller && pCaller->m_debugOverlays & OVERLAY_MESSAGE_BIT)
- {
- pCaller->DrawOutputOverlay(ev);
- }
-
- //
- // Remove the event action from the list if it was set to be fired a finite
- // number of times (and has been).
- //
- bool bRemove = false;
- if (ev->m_nTimesToFire != EVENT_FIRE_ALWAYS)
- {
- ev->m_nTimesToFire--;
- if (ev->m_nTimesToFire == 0)
- {
- char szBuffer[256];
- Q_snprintf( szBuffer, sizeof(szBuffer), "Removing from action list: (%s,%s) -> (%s,%s)\n", pCaller ? STRING(pCaller->m_iClassname) : "NULL", pCaller ? STRING(pCaller->GetEntityName()) : "NULL", STRING(ev->m_iTarget), STRING(ev->m_iTargetInput));
- DevMsg( 2, "%s", szBuffer );
- ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
- bRemove = true;
- }
- }
-
- if (!bRemove)
- {
- prev = ev;
- ev = ev->m_pNext;
- }
- else
- {
- if (prev != NULL)
- {
- prev->m_pNext = ev->m_pNext;
- }
- else
- {
- m_ActionList = ev->m_pNext;
- }
-
- CEventAction *next = ev->m_pNext;
- delete ev;
- ev = next;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Parameterless firing of an event
-// Input : pActivator -
-// pCaller -
-//-----------------------------------------------------------------------------
-void COutputEvent::FireOutput(CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
-{
- variant_t Val;
- Val.Set( FIELD_VOID, NULL );
- CBaseEntityOutput::FireOutput(Val, pActivator, pCaller, fDelay);
-}
-
-
-void CBaseEntityOutput::ParseEventAction( const char *EventData )
-{
- AddEventAction( new CEventAction( EventData ) );
-}
-
-void CBaseEntityOutput::AddEventAction( CEventAction *pEventAction )
-{
- pEventAction->m_pNext = m_ActionList;
- m_ActionList = pEventAction;
-}
-
-
-// save data description for the event queue
-BEGIN_SIMPLE_DATADESC( CBaseEntityOutput )
-
- DEFINE_CUSTOM_FIELD( m_Value, variantFuncs ),
-
- // This is saved manually by CBaseEntityOutput::Save
- // DEFINE_FIELD( m_ActionList, CEventAction ),
-END_DATADESC()
-
-
-int CBaseEntityOutput::Save( ISave &save )
-{
- // save that value out to disk, so we know how many to restore
- if ( !save.WriteFields( "Value", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
- return 0;
-
- for ( CEventAction *ev = m_ActionList; ev != NULL; ev = ev->m_pNext )
- {
- if ( !save.WriteFields( "EntityOutput", ev, NULL, ev->m_DataMap.dataDesc, ev->m_DataMap.dataNumFields ) )
- return 0;
- }
-
- return 1;
-}
-
-int CBaseEntityOutput::Restore( IRestore &restore, int elementCount )
-{
- // load the number of items saved
- if ( !restore.ReadFields( "Value", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
- return 0;
-
- m_ActionList = NULL;
-
- // read in all the fields
- CEventAction *lastEv = NULL;
- for ( int i = 0; i < elementCount; i++ )
- {
- CEventAction *ev = new CEventAction(NULL);
-
- if ( !restore.ReadFields( "EntityOutput", ev, NULL, ev->m_DataMap.dataDesc, ev->m_DataMap.dataNumFields ) )
- return 0;
-
- // add it to the list in the same order it was saved in
- if ( lastEv )
- {
- lastEv->m_pNext = ev;
- }
- else
- {
- m_ActionList = ev;
- }
- ev->m_pNext = NULL;
- lastEv = ev;
- }
-
- return 1;
-}
-
-int CBaseEntityOutput::NumberOfElements( void )
-{
- int count = 0;
- for ( CEventAction *ev = m_ActionList; ev != NULL; ev = ev->m_pNext )
- {
- count++;
- }
- return count;
-}
-
-/// Delete every single action in the action list.
-void CBaseEntityOutput::DeleteAllElements( void )
-{
- // walk front to back, deleting as we go. We needn't fix up pointers because
- // EVERYTHING will die.
-
- CEventAction *pNext = m_ActionList;
- // wipe out the head
- m_ActionList = NULL;
- while (pNext)
- {
- register CEventAction *strikeThis = pNext;
- pNext = pNext->m_pNext;
- delete strikeThis;
- }
-
-}
-
-/// EVENTS save/restore parsing wrapper
-
-class CEventsSaveDataOps : public ISaveRestoreOps
-{
- virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
- {
- AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CEventsSaveDataOps does not support arrays");
-
- CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
- const int fieldSize = fieldInfo.pTypeDesc->fieldSize;
- for ( int i = 0; i < fieldSize; i++, ev++ )
- {
- // save out the number of fields
- int numElements = ev->NumberOfElements();
- pSave->WriteInt( &numElements, 1 );
-
- // save the event data
- ev->Save( *pSave );
- }
- }
-
- virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
- {
- AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CEventsSaveDataOps does not support arrays");
-
- CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
- const int fieldSize = fieldInfo.pTypeDesc->fieldSize;
- for ( int i = 0; i < fieldSize; i++, ev++ )
- {
- int nElements = pRestore->ReadInt();
-
- Assert( nElements < 100 );
-
- ev->Restore( *pRestore, nElements );
- }
- }
-
- virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
- {
- AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CEventsSaveDataOps does not support arrays");
-
- // check all the elements of the array (usually only 1)
- CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
- const int fieldSize = fieldInfo.pTypeDesc->fieldSize;
- for ( int i = 0; i < fieldSize; i++, ev++ )
- {
- // It's not empty if it has events or if it has a non-void variant value
- if (( ev->NumberOfElements() != 0 ) || ( ev->ValueFieldType() != FIELD_VOID ))
- return 0;
- }
-
- // variant has no data
- return 1;
- }
-
- virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
- {
- // Don't no how to. This is okay, since objects of this type
- // are always born clean before restore, and not reused
- }
-
- virtual bool Parse( const SaveRestoreFieldInfo_t &fieldInfo, char const* szValue )
- {
- CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
- ev->ParseEventAction( szValue );
- return true;
- }
-};
-
-CEventsSaveDataOps g_EventsSaveDataOps;
-ISaveRestoreOps *eventFuncs = &g_EventsSaveDataOps;
-
-//-----------------------------------------------------------------------------
-// CMultiInputVar implementation
-//
-// Purpose: holds a list of inputs and their ID tags
-// used for entities that hold inputs from a set of other entities
-//-----------------------------------------------------------------------------
-
-
-//-----------------------------------------------------------------------------
-// Purpose: destructor, frees the data list
-//-----------------------------------------------------------------------------
-CMultiInputVar::~CMultiInputVar()
-{
- if ( m_InputList )
- {
- while ( m_InputList->next != NULL )
- {
- inputitem_t *input = m_InputList->next;
- m_InputList->next = input->next;
- delete input;
- }
- delete m_InputList;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Updates the data set with a new value
-// Input : newVal - the new value to add to or update in the list
-// outputID - the source of the value
-//-----------------------------------------------------------------------------
-void CMultiInputVar::AddValue( variant_t newVal, int outputID )
-{
- // see if it's already in the list
- inputitem_t *inp;
- for ( inp = m_InputList; inp != NULL; inp = inp->next )
- {
- // already in list, so just update this link
- if ( inp->outputID == outputID )
- {
- inp->value = newVal;
- return;
- }
- }
-
- // add to start of list
- inp = new inputitem_t;
- inp->value = newVal;
- inp->outputID = outputID;
- if ( !m_InputList )
- {
- m_InputList = inp;
- inp->next = NULL;
- }
- else
- {
- inp->next = m_InputList;
- m_InputList = inp;
- }
-}
-
-
-#include "tier0/memdbgoff.h"
-
-//-----------------------------------------------------------------------------
-// Purpose: allocates memory from the entitylist pool
-//-----------------------------------------------------------------------------
-void *CMultiInputVar::inputitem_t::operator new( size_t stAllocateBlock )
-{
- return g_EntityListPool.Alloc( stAllocateBlock );
-}
-
-void *CMultiInputVar::inputitem_t::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
-{
- return g_EntityListPool.Alloc( stAllocateBlock );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: frees memory from the entitylist pool
-//-----------------------------------------------------------------------------
-void CMultiInputVar::inputitem_t::operator delete( void *pMem )
-{
- g_EntityListPool.Free( pMem );
-}
-
-#include "tier0/memdbgon.h"
-
-
-//-----------------------------------------------------------------------------
-// CEventQueue implementation
-//
-// Purpose: holds and executes a global prioritized queue of entity actions
-//-----------------------------------------------------------------------------
-DEFINE_FIXEDSIZE_ALLOCATOR( EventQueuePrioritizedEvent_t, 128, CUtlMemoryPool::GROW_SLOW );
-
-CEventQueue g_EventQueue;
-
-CEventQueue::CEventQueue()
-{
- m_Events.m_flFireTime = -FLT_MAX;
- m_Events.m_pNext = NULL;
-
- Init();
-}
-
-CEventQueue::~CEventQueue()
-{
- Clear();
-}
-
-// Robin: Left here for backwards compatability.
-class CEventQueueSaveLoadProxy : public CLogicalEntity
-{
- DECLARE_CLASS( CEventQueueSaveLoadProxy, CLogicalEntity );
-
- int Save( ISave &save )
- {
- if ( !BaseClass::Save(save) )
- return 0;
-
- // save out the message queue
- return g_EventQueue.Save( save );
- }
-
-
- int Restore( IRestore &restore )
- {
- if ( !BaseClass::Restore(restore) )
- return 0;
-
- // restore the event queue
- int iReturn = g_EventQueue.Restore( restore );
-
- // Now remove myself, because the CEventQueue_SaveRestoreBlockHandler
- // will handle future saves.
- UTIL_Remove( this );
-
- return iReturn;
- }
-};
-
-LINK_ENTITY_TO_CLASS(event_queue_saveload_proxy, CEventQueueSaveLoadProxy);
-
-//-----------------------------------------------------------------------------
-// EVENT QUEUE SAVE / RESTORE
-//-----------------------------------------------------------------------------
-static short EVENTQUEUE_SAVE_RESTORE_VERSION = 1;
-
-class CEventQueue_SaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
-{
-public:
- const char *GetBlockName()
- {
- return "EventQueue";
- }
-
- //---------------------------------
-
- void Save( ISave *pSave )
- {
- g_EventQueue.Save( *pSave );
- }
-
- //---------------------------------
-
- void WriteSaveHeaders( ISave *pSave )
- {
- pSave->WriteShort( &EVENTQUEUE_SAVE_RESTORE_VERSION );
- }
-
- //---------------------------------
-
- void ReadRestoreHeaders( IRestore *pRestore )
- {
- // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
- short version;
- pRestore->ReadShort( &version );
- m_fDoLoad = ( version == EVENTQUEUE_SAVE_RESTORE_VERSION );
- }
-
- //---------------------------------
-
- void Restore( IRestore *pRestore, bool createPlayers )
- {
- if ( m_fDoLoad )
- {
- g_EventQueue.Restore( *pRestore );
- }
- }
-
-private:
- bool m_fDoLoad;
-};
-
-//-----------------------------------------------------------------------------
-
-CEventQueue_SaveRestoreBlockHandler g_EventQueue_SaveRestoreBlockHandler;
-
-//-------------------------------------
-
-ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler()
-{
- return &g_EventQueue_SaveRestoreBlockHandler;
-}
-
-
-void CEventQueue::Init( void )
-{
- Clear();
-}
-
-void CEventQueue::Clear( void )
-{
- // delete all the events in the queue
- EventQueuePrioritizedEvent_t *pe = m_Events.m_pNext;
-
- while ( pe != NULL )
- {
- EventQueuePrioritizedEvent_t *next = pe->m_pNext;
- delete pe;
- pe = next;
- }
-
- m_Events.m_pNext = NULL;
-}
-
-void CEventQueue::Dump( void )
-{
- EventQueuePrioritizedEvent_t *pe = m_Events.m_pNext;
-
- Msg( "Dumping event queue. Current time is: %.2f\n", engine->GetServerTime() );
-
- while ( pe != NULL )
- {
- EventQueuePrioritizedEvent_t *next = pe->m_pNext;
-
- Msg(" (%.2f) Target: '%s', Input: '%s', Parameter '%s'. Activator: '%s', Caller '%s'. \n",
- pe->m_flFireTime,
- STRING(pe->m_iTarget),
- STRING(pe->m_iTargetInput),
- pe->m_VariantValue.String(),
- pe->m_pActivator ? pe->m_pActivator->GetDebugName() : "None",
- pe->m_pCaller ? pe->m_pCaller->GetDebugName() : "None" );
-
- pe = next;
- }
-
- Msg("Finished dump.\n");
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: adds the action into the correct spot in the priority queue, targeting entity via string name
-//-----------------------------------------------------------------------------
-void CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID )
-{
- // build the new event
- EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t;
- newEvent->m_flFireTime = engine->GetServerTime() + fireDelay; // priority key in the priority queue
- newEvent->m_iTarget = MAKE_STRING( target );
- newEvent->m_pEntTarget = NULL;
- newEvent->m_iTargetInput = MAKE_STRING( targetInput );
- newEvent->m_pActivator = pActivator;
- newEvent->m_pCaller = pCaller;
- newEvent->m_VariantValue = Value;
- newEvent->m_iOutputID = outputID;
-
- AddEvent( newEvent );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: adds the action into the correct spot in the priority queue, targeting entity via pointer
-//-----------------------------------------------------------------------------
-void CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID )
-{
- // build the new event
- EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t;
- newEvent->m_flFireTime = engine->GetServerTime() + fireDelay; // primary priority key in the priority queue
- newEvent->m_iTarget = NULL_STRING;
- newEvent->m_pEntTarget = target;
- newEvent->m_iTargetInput = MAKE_STRING( targetInput );
- newEvent->m_pActivator = pActivator;
- newEvent->m_pCaller = pCaller;
- newEvent->m_VariantValue = Value;
- newEvent->m_iOutputID = outputID;
-
- AddEvent( newEvent );
-}
-
-void CEventQueue::AddEvent( CBaseEntity *target, const char *action, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID )
-{
- variant_t Value;
- Value.Set( FIELD_VOID, NULL );
- AddEvent( target, action, Value, fireDelay, pActivator, pCaller, outputID );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: private function, adds an event into the list
-// Input : *newEvent - the (already built) event to add
-//-----------------------------------------------------------------------------
-void CEventQueue::AddEvent( EventQueuePrioritizedEvent_t *newEvent )
-{
- // loop through the actions looking for a place to insert
- EventQueuePrioritizedEvent_t *pe;
- for ( pe = &m_Events; pe->m_pNext != NULL; pe = pe->m_pNext )
- {
- if ( pe->m_pNext->m_flFireTime > newEvent->m_flFireTime )
- {
- break;
- }
- }
-
- Assert( pe );
-
- // insert
- newEvent->m_pNext = pe->m_pNext;
- newEvent->m_pPrev = pe;
- pe->m_pNext = newEvent;
- if ( newEvent->m_pNext )
- {
- newEvent->m_pNext->m_pPrev = newEvent;
- }
-}
-
-void CEventQueue::RemoveEvent( EventQueuePrioritizedEvent_t *pe )
-{
- Assert( pe->m_pPrev );
- pe->m_pPrev->m_pNext = pe->m_pNext;
- if ( pe->m_pNext )
- {
- pe->m_pNext->m_pPrev = pe->m_pPrev;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: fires off any events in the queue who's fire time is (or before) the present time
-//-----------------------------------------------------------------------------
-void CEventQueue::ServiceEvents( void )
-{
- if (!CBaseEntity::Debug_ShouldStep())
- {
- return;
- }
-
- EventQueuePrioritizedEvent_t *pe = m_Events.m_pNext;
-
- while ( pe != NULL && pe->m_flFireTime <= engine->GetServerTime() )
- {
- MDLCACHE_CRITICAL_SECTION();
-
- bool targetFound = false;
-
- // find the targets
- if ( pe->m_iTarget != NULL_STRING )
- {
- // In the context the event, the searching entity is also the caller
- CBaseEntity *pSearchingEntity = pe->m_pCaller;
- CBaseEntity *target = NULL;
- while ( 1 )
- {
- target = gEntList.FindEntityByName( target, pe->m_iTarget, pSearchingEntity, pe->m_pActivator, pe->m_pCaller );
- if ( !target )
- break;
-
- // pump the action into the target
- target->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID );
- targetFound = true;
- }
- }
-
- // direct pointer
- if ( pe->m_pEntTarget != NULL )
- {
- pe->m_pEntTarget->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID );
- targetFound = true;
- }
-
- if ( !targetFound )
- {
- // See if we can find a target if we treat the target as a classname
- if ( pe->m_iTarget != NULL_STRING )
- {
- CBaseEntity *target = NULL;
- while ( 1 )
- {
- target = gEntList.FindEntityByClassname( target, STRING(pe->m_iTarget) );
- if ( !target )
- break;
-
- // pump the action into the target
- target->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID );
- targetFound = true;
- }
- }
- }
-
- if ( !targetFound )
- {
- const char *pClass ="", *pName = "";
-
- // might be NULL
- if ( pe->m_pCaller )
- {
- pClass = STRING(pe->m_pCaller->m_iClassname);
- pName = STRING(pe->m_pCaller->GetEntityName());
- }
-
- char szBuffer[256];
- Q_snprintf( szBuffer, sizeof(szBuffer), "unhandled input: (%s) -> (%s), from (%s,%s); target entity not found\n", STRING(pe->m_iTargetInput), STRING(pe->m_iTarget), pClass, pName );
- DevMsg( 2, "%s", szBuffer );
- ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
- }
-
- // remove the event from the list (remembering that the queue may have been added to)
- RemoveEvent( pe );
- delete pe;
-
- //
- // If we are in debug mode, exit the loop if we have fired the correct number of events.
- //
- if (CBaseEntity::Debug_IsPaused())
- {
- if (!CBaseEntity::Debug_Step())
- {
- break;
- }
- }
-
- // restart the list (to catch any new items have probably been added to the queue)
- pe = m_Events.m_pNext;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Dumps the contents of the Entity I/O event queue to the console.
-//-----------------------------------------------------------------------------
-void CC_DumpEventQueue()
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- g_EventQueue.Dump();
-}
-static ConCommand dumpeventqueue( "dumpeventqueue", CC_DumpEventQueue, "Dump the contents of the Entity I/O event queue to the console." );
-
-//-----------------------------------------------------------------------------
-// Purpose: Removes all pending events from the I/O queue that were added by the
-// given caller.
-//
-// TODO: This is only as reliable as callers are in passing the correct
-// caller pointer when they fire the outputs. Make more foolproof.
-//-----------------------------------------------------------------------------
-void CEventQueue::CancelEvents( CBaseEntity *pCaller )
-{
- if (!pCaller)
- return;
-
- EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext;
-
- while (pCur != NULL)
- {
- bool bDelete = false;
- if (pCur->m_pCaller == pCaller)
- {
- // Pointers match; make sure everything else matches.
- if (!stricmp(STRING(pCur->m_pCaller->GetEntityName()), STRING(pCaller->GetEntityName())) &&
- !stricmp(pCur->m_pCaller->GetClassname(), pCaller->GetClassname()))
- {
- // Found a matching event; delete it from the queue.
- bDelete = true;
- }
- }
-
- EventQueuePrioritizedEvent_t *pCurSave = pCur;
- pCur = pCur->m_pNext;
-
- if (bDelete)
- {
- RemoveEvent( pCurSave );
- delete pCurSave;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Removes all pending events of the specified type from the I/O queue of the specified target
-//
-// TODO: This is only as reliable as callers are in passing the correct
-// caller pointer when they fire the outputs. Make more foolproof.
-//-----------------------------------------------------------------------------
-void CEventQueue::CancelEventOn( CBaseEntity *pTarget, const char *sInputName )
-{
- if (!pTarget)
- return;
-
- EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext;
-
- while (pCur != NULL)
- {
- bool bDelete = false;
- if (pCur->m_pEntTarget == pTarget)
- {
- if ( !Q_strncmp( STRING(pCur->m_iTargetInput), sInputName, strlen(sInputName) ) )
- {
- // Found a matching event; delete it from the queue.
- bDelete = true;
- }
- }
-
- EventQueuePrioritizedEvent_t *pCurSave = pCur;
- pCur = pCur->m_pNext;
-
- if (bDelete)
- {
- RemoveEvent( pCurSave );
- delete pCurSave;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return true if the target has any pending inputs.
-// Input : *pTarget -
-// *sInputName - NULL for any input, or a specified one
-//-----------------------------------------------------------------------------
-bool CEventQueue::HasEventPending( CBaseEntity *pTarget, const char *sInputName )
-{
- if (!pTarget)
- return false;
-
- EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext;
-
- while (pCur != NULL)
- {
- if (pCur->m_pEntTarget == pTarget)
- {
- if ( !sInputName )
- return true;
-
- if ( !Q_strncmp( STRING(pCur->m_iTargetInput), sInputName, strlen(sInputName) ) )
- return true;
- }
-
- pCur = pCur->m_pNext;
- }
-
- return false;
-}
-
-void ServiceEventQueue( void )
-{
- VPROF("ServiceEventQueue()");
-
- g_EventQueue.ServiceEvents();
-}
-
-
-
-// save data description for the event queue
-BEGIN_SIMPLE_DATADESC( CEventQueue )
- // These are saved explicitly in CEventQueue::Save below
- // DEFINE_FIELD( m_Events, EventQueuePrioritizedEvent_t ),
-
- DEFINE_FIELD( m_iListCount, FIELD_INTEGER ), // this value is only used during save/restore
-END_DATADESC()
-
-
-// save data for a single event in the queue
-BEGIN_SIMPLE_DATADESC( EventQueuePrioritizedEvent_t )
- DEFINE_FIELD( m_flFireTime, FIELD_TIME ),
- DEFINE_FIELD( m_iTarget, FIELD_STRING ),
- DEFINE_FIELD( m_iTargetInput, FIELD_STRING ),
- DEFINE_FIELD( m_pActivator, FIELD_EHANDLE ),
- DEFINE_FIELD( m_pCaller, FIELD_EHANDLE ),
- DEFINE_FIELD( m_pEntTarget, FIELD_EHANDLE ),
- DEFINE_FIELD( m_iOutputID, FIELD_INTEGER ),
- DEFINE_CUSTOM_FIELD( m_VariantValue, variantFuncs ),
-
-// DEFINE_FIELD( m_pNext, FIELD_??? ),
-// DEFINE_FIELD( m_pPrev, FIELD_??? ),
-END_DATADESC()
-
-
-int CEventQueue::Save( ISave &save )
-{
- // count the number of items in the queue
- EventQueuePrioritizedEvent_t *pe;
-
- m_iListCount = 0;
- for ( pe = m_Events.m_pNext; pe != NULL; pe = pe->m_pNext )
- {
- m_iListCount++;
- }
-
- // save that value out to disk, so we know how many to restore
- if ( !save.WriteFields( "EventQueue", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
- return 0;
-
- // cycle through all the events, saving them all
- for ( pe = m_Events.m_pNext; pe != NULL; pe = pe->m_pNext )
- {
- if ( !save.WriteFields( "PEvent", pe, NULL, pe->m_DataMap.dataDesc, pe->m_DataMap.dataNumFields ) )
- return 0;
- }
-
- return 1;
-}
-
-
-int CEventQueue::Restore( IRestore &restore )
-{
- // clear the event queue
- Clear();
-
- // rebuild the event queue by restoring all the queue items
- EventQueuePrioritizedEvent_t tmpEvent;
-
- // load the number of items saved
- if ( !restore.ReadFields( "EventQueue", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
- return 0;
-
- for ( int i = 0; i < m_iListCount; i++ )
- {
- if ( !restore.ReadFields( "PEvent", &tmpEvent, NULL, tmpEvent.m_DataMap.dataDesc, tmpEvent.m_DataMap.dataNumFields ) )
- return 0;
-
- // add the restored event into the list
- if ( tmpEvent.m_pEntTarget )
- {
- AddEvent( tmpEvent.m_pEntTarget,
- STRING(tmpEvent.m_iTargetInput),
- tmpEvent.m_VariantValue,
- tmpEvent.m_flFireTime - engine->GetServerTime(),
- tmpEvent.m_pActivator,
- tmpEvent.m_pCaller,
- tmpEvent.m_iOutputID );
- }
- else
- {
- AddEvent( STRING(tmpEvent.m_iTarget),
- STRING(tmpEvent.m_iTargetInput),
- tmpEvent.m_VariantValue,
- tmpEvent.m_flFireTime - engine->GetServerTime(),
- tmpEvent.m_pActivator,
- tmpEvent.m_pCaller,
- tmpEvent.m_iOutputID );
- }
- }
-
- return 1;
-}
-
-////////////////////////// variant_t implementation //////////////////////////
-
-// BUGBUG: Add support for function pointer save/restore to variants
-// BUGBUG: Must pass datamap_t to read/write fields
-void variant_t::Set( fieldtype_t ftype, void *data )
-{
- fieldType = ftype;
-
- switch ( ftype )
- {
- case FIELD_BOOLEAN: bVal = *((bool *)data); break;
- case FIELD_CHARACTER: iVal = *((char *)data); break;
- case FIELD_SHORT: iVal = *((short *)data); break;
- case FIELD_INTEGER: iVal = *((int *)data); break;
- case FIELD_STRING: iszVal = *((string_t *)data); break;
- case FIELD_FLOAT: flVal = *((float *)data); break;
- case FIELD_COLOR32: rgbaVal = *((color32 *)data); break;
-
- case FIELD_VECTOR:
- case FIELD_POSITION_VECTOR:
- {
- vecVal[0] = ((float *)data)[0];
- vecVal[1] = ((float *)data)[1];
- vecVal[2] = ((float *)data)[2];
- break;
- }
-
- case FIELD_EHANDLE: eVal = *((EHANDLE *)data); break;
- case FIELD_CLASSPTR: eVal = *((CBaseEntity **)data); break;
- case FIELD_VOID:
- default:
- iVal = 0; fieldType = FIELD_VOID;
- break;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Copies the value in the variant into a block of memory
-// Input : *data - the block to write into
-//-----------------------------------------------------------------------------
-void variant_t::SetOther( void *data )
-{
- switch ( fieldType )
- {
- case FIELD_BOOLEAN: *((bool *)data) = bVal != 0; break;
- case FIELD_CHARACTER: *((char *)data) = iVal; break;
- case FIELD_SHORT: *((short *)data) = iVal; break;
- case FIELD_INTEGER: *((int *)data) = iVal; break;
- case FIELD_STRING: *((string_t *)data) = iszVal; break;
- case FIELD_FLOAT: *((float *)data) = flVal; break;
- case FIELD_COLOR32: *((color32 *)data) = rgbaVal; break;
-
- case FIELD_VECTOR:
- case FIELD_POSITION_VECTOR:
- {
- ((float *)data)[0] = vecVal[0];
- ((float *)data)[1] = vecVal[1];
- ((float *)data)[2] = vecVal[2];
- break;
- }
-
- case FIELD_EHANDLE: *((EHANDLE *)data) = eVal; break;
- case FIELD_CLASSPTR: *((CBaseEntity **)data) = eVal; break;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Converts the variant to a new type. This function defines which I/O
-// types can be automatically converted between. Connections that require
-// an unsupported conversion will cause an error message at runtime.
-// Input : newType - the type to convert to
-// Output : Returns true on success, false if the conversion is not legal
-//-----------------------------------------------------------------------------
-bool variant_t::Convert( fieldtype_t newType )
-{
- if ( newType == fieldType )
- {
- return true;
- }
-
- //
- // Converting to a null value is easy.
- //
- if ( newType == FIELD_VOID )
- {
- Set( FIELD_VOID, NULL );
- return true;
- }
-
- //
- // FIELD_INPUT accepts the variant type directly.
- //
- if ( newType == FIELD_INPUT )
- {
- return true;
- }
-
- switch ( fieldType )
- {
- case FIELD_INTEGER:
- {
- switch ( newType )
- {
- case FIELD_FLOAT:
- {
- SetFloat( (float) iVal );
- return true;
- }
-
- case FIELD_BOOLEAN:
- {
- SetBool( iVal != 0 );
- return true;
- }
- }
- break;
- }
-
- case FIELD_FLOAT:
- {
- switch ( newType )
- {
- case FIELD_INTEGER:
- {
- SetInt( (int) flVal );
- return true;
- }
-
- case FIELD_BOOLEAN:
- {
- SetBool( flVal != 0 );
- return true;
- }
- }
- break;
- }
-
- //
- // Everyone must convert from FIELD_STRING if possible, since
- // parameter overrides are always passed as strings.
- //
- case FIELD_STRING:
- {
- switch ( newType )
- {
- case FIELD_INTEGER:
- {
- if (iszVal != NULL_STRING)
- {
- SetInt(atoi(STRING(iszVal)));
- }
- else
- {
- SetInt(0);
- }
- return true;
- }
-
- case FIELD_FLOAT:
- {
- if (iszVal != NULL_STRING)
- {
- SetFloat(atof(STRING(iszVal)));
- }
- else
- {
- SetFloat(0);
- }
- return true;
- }
-
- case FIELD_BOOLEAN:
- {
- if (iszVal != NULL_STRING)
- {
- SetBool( atoi(STRING(iszVal)) != 0 );
- }
- else
- {
- SetBool(false);
- }
- return true;
- }
-
- case FIELD_VECTOR:
- {
- Vector tmpVec = vec3_origin;
- if (sscanf(STRING(iszVal), "[%f %f %f]", &tmpVec[0], &tmpVec[1], &tmpVec[2]) == 0)
- {
- // Try sucking out 3 floats with no []s
- sscanf(STRING(iszVal), "%f %f %f", &tmpVec[0], &tmpVec[1], &tmpVec[2]);
- }
- SetVector3D( tmpVec );
- return true;
- }
-
- case FIELD_COLOR32:
- {
- int nRed = 0;
- int nGreen = 0;
- int nBlue = 0;
- int nAlpha = 255;
-
- sscanf(STRING(iszVal), "%d %d %d %d", &nRed, &nGreen, &nBlue, &nAlpha);
- SetColor32( nRed, nGreen, nBlue, nAlpha );
- return true;
- }
-
- case FIELD_EHANDLE:
- {
- // convert the string to an entity by locating it by classname
- CBaseEntity *ent = NULL;
- if ( iszVal != NULL_STRING )
- {
- // FIXME: do we need to pass an activator in here?
- ent = gEntList.FindEntityByName( NULL, iszVal );
- }
- SetEntity( ent );
- return true;
- }
- }
-
- break;
- }
-
- case FIELD_EHANDLE:
- {
- switch ( newType )
- {
- case FIELD_STRING:
- {
- // take the entities targetname as the string
- string_t iszStr = NULL_STRING;
- if ( eVal != NULL )
- {
- SetString( eVal->GetEntityName() );
- }
- return true;
- }
- }
- break;
- }
- }
-
- // invalid conversion
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: All types must be able to display as strings for debugging purposes.
-// Output : Returns a pointer to the string that represents this value.
-//
-// NOTE: The returned pointer should not be stored by the caller as
-// subsequent calls to this function will overwrite the contents
-// of the buffer!
-//-----------------------------------------------------------------------------
-const char *variant_t::ToString( void ) const
-{
- COMPILE_TIME_ASSERT( sizeof(string_t) == sizeof(int) );
-
- static char szBuf[512];
-
- switch (fieldType)
- {
- case FIELD_STRING:
- {
- return(STRING(iszVal));
- }
-
- case FIELD_BOOLEAN:
- {
- if (bVal == 0)
- {
- Q_strncpy(szBuf, "false",sizeof(szBuf));
- }
- else
- {
- Q_strncpy(szBuf, "true",sizeof(szBuf));
- }
- return(szBuf);
- }
-
- case FIELD_INTEGER:
- {
- Q_snprintf( szBuf, sizeof( szBuf ), "%i", iVal );
- return(szBuf);
- }
-
- case FIELD_FLOAT:
- {
- Q_snprintf(szBuf,sizeof(szBuf), "%g", flVal);
- return(szBuf);
- }
-
- case FIELD_COLOR32:
- {
- Q_snprintf(szBuf,sizeof(szBuf), "%d %d %d %d", (int)rgbaVal.r, (int)rgbaVal.g, (int)rgbaVal.b, (int)rgbaVal.a);
- return(szBuf);
- }
-
- case FIELD_VECTOR:
- {
- Q_snprintf(szBuf,sizeof(szBuf), "[%g %g %g]", (double)vecVal[0], (double)vecVal[1], (double)vecVal[2]);
- return(szBuf);
- }
-
- case FIELD_VOID:
- {
- szBuf[0] = '\0';
- return(szBuf);
- }
-
- case FIELD_EHANDLE:
- {
- const char *pszName = (Entity()) ? STRING(Entity()->GetEntityName()) : "<<null entity>>";
- Q_strncpy( szBuf, pszName, 512 );
- return (szBuf);
- }
- }
-
- return("No conversion to string");
-}
-
-#define classNameTypedef variant_t // to satisfy DEFINE... macros
-
-typedescription_t variant_t::m_SaveBool[] =
-{
- DEFINE_FIELD( bVal, FIELD_BOOLEAN ),
-};
-typedescription_t variant_t::m_SaveInt[] =
-{
- DEFINE_FIELD( iVal, FIELD_INTEGER ),
-};
-typedescription_t variant_t::m_SaveFloat[] =
-{
- DEFINE_FIELD( flVal, FIELD_FLOAT ),
-};
-typedescription_t variant_t::m_SaveEHandle[] =
-{
- DEFINE_FIELD( eVal, FIELD_EHANDLE ),
-};
-typedescription_t variant_t::m_SaveString[] =
-{
- DEFINE_FIELD( iszVal, FIELD_STRING ),
-};
-typedescription_t variant_t::m_SaveColor[] =
-{
- DEFINE_FIELD( rgbaVal, FIELD_COLOR32 ),
-};
-
-#undef classNameTypedef
-
-//
-// Struct for saving and restoring vector variants, since they are
-// stored as float[3] and we want to take advantage of position vector
-// fixup across level transitions.
-//
-#define classNameTypedef variant_savevector_t // to satisfy DEFINE... macros
-
-struct variant_savevector_t
-{
- Vector vecSave;
-};
-typedescription_t variant_t::m_SaveVector[] =
-{
- // Just here to shut up ClassCheck
-// DEFINE_ARRAY( vecVal, FIELD_FLOAT, 3 ),
-
- DEFINE_FIELD( vecSave, FIELD_VECTOR ),
-};
-typedescription_t variant_t::m_SavePositionVector[] =
-{
- DEFINE_FIELD( vecSave, FIELD_POSITION_VECTOR ),
-};
-#undef classNameTypedef
-
-#define classNameTypedef variant_savevmatrix_t // to satisfy DEFINE... macros
-struct variant_savevmatrix_t
-{
- VMatrix matSave;
-};
-typedescription_t variant_t::m_SaveVMatrix[] =
-{
- DEFINE_FIELD( matSave, FIELD_VMATRIX ),
-};
-typedescription_t variant_t::m_SaveVMatrixWorldspace[] =
-{
- DEFINE_FIELD( matSave, FIELD_VMATRIX_WORLDSPACE ),
-};
-#undef classNameTypedef
-
-#define classNameTypedef variant_savevmatrix3x4_t // to satisfy DEFINE... macros
-struct variant_savevmatrix3x4_t
-{
- matrix3x4_t matSave;
-};
-typedescription_t variant_t::m_SaveMatrix3x4Worldspace[] =
-{
- DEFINE_FIELD( matSave, FIELD_MATRIX3X4_WORLDSPACE ),
-};
-#undef classNameTypedef
-
-class CVariantSaveDataOps : public CDefSaveRestoreOps
-{
- // saves the entire array of variables
- virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
- {
- variant_t *var = (variant_t*)fieldInfo.pField;
-
- int type = var->FieldType();
- pSave->WriteInt( &type, 1 );
-
- switch ( var->FieldType() )
- {
- case FIELD_VOID:
- break;
- case FIELD_BOOLEAN:
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveBool, 1 );
- break;
- case FIELD_INTEGER:
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveInt, 1 );
- break;
- case FIELD_FLOAT:
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveFloat, 1 );
- break;
- case FIELD_EHANDLE:
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveEHandle, 1 );
- break;
- case FIELD_STRING:
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveString, 1 );
- break;
- case FIELD_COLOR32:
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveColor, 1 );
- break;
- case FIELD_VECTOR:
- {
- variant_savevector_t Temp;
- var->Vector3D(Temp.vecSave);
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SaveVector, 1 );
- break;
- }
-
- case FIELD_POSITION_VECTOR:
- {
- variant_savevector_t Temp;
- var->Vector3D(Temp.vecSave);
- pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SavePositionVector, 1 );
- break;
- }
-
- default:
- Warning( "Bad type %d in saved variant_t\n", var->FieldType() );
- Assert(0);
- }
- }
-
- // restores a single instance of the variable
- virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
- {
- variant_t *var = (variant_t*)fieldInfo.pField;
-
- *var = variant_t();
-
- var->fieldType = (_fieldtypes)pRestore->ReadInt();
-
- switch ( var->fieldType )
- {
- case FIELD_VOID:
- break;
- case FIELD_BOOLEAN:
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveBool, 1 );
- break;
- case FIELD_INTEGER:
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveInt, 1 );
- break;
- case FIELD_FLOAT:
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveFloat, 1 );
- break;
- case FIELD_EHANDLE:
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveEHandle, 1 );
- break;
- case FIELD_STRING:
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveString, 1 );
- break;
- case FIELD_COLOR32:
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveColor, 1 );
- break;
- case FIELD_VECTOR:
- {
- variant_savevector_t Temp;
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SaveVector, 1 );
- var->SetVector3D(Temp.vecSave);
- break;
- }
- case FIELD_POSITION_VECTOR:
- {
- variant_savevector_t Temp;
- pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SavePositionVector, 1 );
- var->SetPositionVector3D(Temp.vecSave);
- break;
- }
- default:
- Warning( "Bad type %d in saved variant_t\n", var->FieldType() );
- Assert(0);
- break;
- }
- }
-
-
- virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
- {
- // check all the elements of the array (usually only 1)
- variant_t *var = (variant_t*)fieldInfo.pField;
- for ( int i = 0; i < fieldInfo.pTypeDesc->fieldSize; i++, var++ )
- {
- if ( var->FieldType() != FIELD_VOID )
- return 0;
- }
-
- // variant has no data
- return 1;
- }
-
- virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
- {
- // Don't no how to. This is okay, since objects of this type
- // are always born clean before restore, and not reused
- }
-};
-
-CVariantSaveDataOps g_VariantSaveDataOps;
-ISaveRestoreOps *variantFuncs = &g_VariantSaveDataOps;
-
-/////////////////////// entitylist /////////////////////
-
-CUtlMemoryPool g_EntListMemPool( sizeof(entitem_t), 256, CUtlMemoryPool::GROW_NONE, "g_EntListMemPool" );
-
-#include "tier0/memdbgoff.h"
-
-void *entitem_t::operator new( size_t stAllocateBlock )
-{
- return g_EntListMemPool.Alloc( stAllocateBlock );
-}
-
-void *entitem_t::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
-{
- return g_EntListMemPool.Alloc( stAllocateBlock );
-}
-
-void entitem_t::operator delete( void *pMem )
-{
- g_EntListMemPool.Free( pMem );
-}
-
-#include "tier0/memdbgon.h"
-
-
-CEntityList::CEntityList()
-{
- m_pItemList = NULL;
- m_iNumItems = 0;
-}
-
-CEntityList::~CEntityList()
-{
- // remove all items from the list
- entitem_t *next, *e = m_pItemList;
- while ( e != NULL )
- {
- next = e->pNext;
- delete e;
- e = next;
- }
- m_pItemList = NULL;
-}
-
-void CEntityList::AddEntity( CBaseEntity *pEnt )
-{
- // check if it's already in the list; if not, add it
- entitem_t *e = m_pItemList;
- while ( e != NULL )
- {
- if ( e->hEnt == pEnt )
- {
- // it's already in the list
- return;
- }
-
- if ( e->pNext == NULL )
- {
- // we've hit the end of the list, so tack it on
- e->pNext = new entitem_t;
- e->pNext->hEnt = pEnt;
- e->pNext->pNext = NULL;
- m_iNumItems++;
- return;
- }
-
- e = e->pNext;
- }
-
- // empty list
- m_pItemList = new entitem_t;
- m_pItemList->hEnt = pEnt;
- m_pItemList->pNext = NULL;
- m_iNumItems = 1;
-}
-
-void CEntityList::DeleteEntity( CBaseEntity *pEnt )
-{
- // find the entry in the list and delete it
- entitem_t *prev = NULL, *e = m_pItemList;
- while ( e != NULL )
- {
- // delete the link if it's the matching entity OR if the link is NULL
- if ( e->hEnt == pEnt || e->hEnt == NULL )
- {
- if ( prev )
- {
- prev->pNext = e->pNext;
- }
- else
- {
- m_pItemList = e->pNext;
- }
-
- delete e;
- m_iNumItems--;
-
- // REVISIT: Is this correct? Is this just here to clean out dead EHANDLEs?
- // restart the loop
- e = m_pItemList;
- prev = NULL;
- continue;
- }
-
- prev = e;
- e = e->pNext;
- }
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+/*
+Entity Data Descriptions
+
+Each entity has an array which defines it's data in way that is useful for
+entity communication, parsing initial values from the map, and save/restore.
+
+each entity has to have the following line in it's class declaration:
+
+ DECLARE_DATADESC();
+
+this line defines that it has an m_DataDesc[] array, and declares functions through
+which various subsystems can iterate the data.
+
+In it's implementation, each entity has to have:
+
+ typedescription_t CBaseEntity::m_DataDesc[] = { ... }
+
+in which all it's data is defined (see below), followed by:
+
+
+which implements the functions necessary for iterating through an entities data desc.
+
+There are several types of data:
+
+ FIELD : this is a variable which gets saved & loaded from disk
+ KEY : this variable can be read in from the map file
+ GLOBAL : a global field is actually local; it is saved/restored, but is actually
+ unique to the entity on each level.
+ CUSTOM : the save/restore parsing functions are described by the user.
+ ARRAY : an array of values
+ OUTPUT : a variable or event that can be connected to other entities (see below)
+ INPUTFUNC : maps a string input to a function pointer. Outputs connected to this input
+ will call the notify function when fired.
+ INPUT : maps a string input to a member variable. Outputs connected to this input
+ will update the input data value when fired.
+ INPUTNOTIFY : maps a string input to a member variable/function pointer combo. Outputs
+ connected to this input will update the data value and call the notify
+ function when fired.
+
+some of these can overlap. all the data descriptions usable are:
+
+ DEFINE_FIELD( name, fieldtype )
+ DEFINE_KEYFIELD( name, fieldtype, mapname )
+ DEFINE_KEYFIELD_NOTSAVED( name, fieldtype, mapname )
+ DEFINE_ARRAY( name, fieldtype, count )
+ DEFINE_GLOBAL_FIELD(name, fieldtype )
+ DEFINE_CUSTOM_FIELD(name, datafuncs, mapname )
+ DEFINE_GLOBAL_KEYFIELD(name, fieldtype, mapname )
+
+where:
+ type is the name of the class (eg. CBaseEntity)
+ name is the name of the variable in the class (eg. m_iHealth)
+ fieldtype is the type of data (FIELD_STRING, FIELD_INTEGER, etc)
+ mapname is the string by which this variable is associated with map file data
+ count is the number of items in the array
+ datafuncs is a struct containing function pointers for a custom-defined save/restore/parse
+
+OUTPUTS:
+
+ DEFINE_OUTPUT( outputvar, outputname )
+
+ This maps the string 'outputname' to the COutput-derived member variable outputvar. In the VMF
+ file these outputs can be hooked up to inputs (see above). Whenever the internal state
+ of an entity changes it will often fire off outputs so that map makers can hook up behaviors.
+ e.g. A door entity would have OnDoorOpen, OnDoorClose, OnTouched, etc outputs.
+*/
+
+
+#include "cbase.h"
+#include "entitylist.h"
+#include "mapentities_shared.h"
+#include "isaverestore.h"
+#include "eventqueue.h"
+#include "entityinput.h"
+#include "entityoutput.h"
+#include "mempool.h"
+#include "tier1/strtools.h"
+#include "datacache/imdlcache.h"
+#include "env_debughistory.h"
+
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern ISaveRestoreOps *variantFuncs; // function pointer set for save/restoring variants
+
+BEGIN_SIMPLE_DATADESC( CEventAction )
+ DEFINE_FIELD( m_iTarget, FIELD_STRING ),
+ DEFINE_FIELD( m_iTargetInput, FIELD_STRING ),
+ DEFINE_FIELD( m_iParameter, FIELD_STRING ),
+ DEFINE_FIELD( m_flDelay, FIELD_FLOAT ),
+ DEFINE_FIELD( m_nTimesToFire, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iIDStamp, FIELD_INTEGER ),
+
+ // This is dealt with by the Restore method
+ // DEFINE_FIELD( m_pNext, CEventAction ),
+END_DATADESC()
+
+
+// ID Stamp used to uniquely identify every output
+int CEventAction::s_iNextIDStamp = 0;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates an event action and assigns it an unique ID stamp.
+// Input : ActionData - the map file data block descibing the event action.
+//-----------------------------------------------------------------------------
+CEventAction::CEventAction( const char *ActionData )
+{
+ m_pNext = NULL;
+ m_iIDStamp = ++s_iNextIDStamp;
+
+ m_flDelay = 0;
+ m_iTarget = NULL_STRING;
+ m_iParameter = NULL_STRING;
+ m_iTargetInput = NULL_STRING;
+ m_nTimesToFire = EVENT_FIRE_ALWAYS;
+
+ if (ActionData == NULL)
+ return;
+
+ char szToken[256];
+
+ //
+ // Parse the target name.
+ //
+ const char *psz = nexttoken(szToken, ActionData, ',');
+ if (szToken[0] != '\0')
+ {
+ m_iTarget = AllocPooledString(szToken);
+ }
+
+ //
+ // Parse the input name.
+ //
+ psz = nexttoken(szToken, psz, ',');
+ if (szToken[0] != '\0')
+ {
+ m_iTargetInput = AllocPooledString(szToken);
+ }
+ else
+ {
+ m_iTargetInput = AllocPooledString("Use");
+ }
+
+ //
+ // Parse the parameter override.
+ //
+ psz = nexttoken(szToken, psz, ',');
+ if (szToken[0] != '\0')
+ {
+ m_iParameter = AllocPooledString(szToken);
+ }
+
+ //
+ // Parse the delay.
+ //
+ psz = nexttoken(szToken, psz, ',');
+ if (szToken[0] != '\0')
+ {
+ m_flDelay = atof(szToken);
+ }
+
+ //
+ // Parse the number of times to fire.
+ //
+ nexttoken(szToken, psz, ',');
+ if (szToken[0] != '\0')
+ {
+ m_nTimesToFire = atoi(szToken);
+ if (m_nTimesToFire == 0)
+ {
+ m_nTimesToFire = EVENT_FIRE_ALWAYS;
+ }
+ }
+}
+
+
+// this memory pool stores blocks around the size of CEventAction/inputitem_t structs
+// can be used for other blocks; will error if to big a block is tried to be allocated
+CUtlMemoryPool g_EntityListPool( MAX(sizeof(CEventAction),sizeof(CMultiInputVar::inputitem_t)), 512, CUtlMemoryPool::GROW_FAST, "g_EntityListPool" );
+
+#include "tier0/memdbgoff.h"
+
+void *CEventAction::operator new( size_t stAllocateBlock )
+{
+ return g_EntityListPool.Alloc( stAllocateBlock );
+}
+
+void *CEventAction::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
+{
+ return g_EntityListPool.Alloc( stAllocateBlock );
+}
+
+void CEventAction::operator delete( void *pMem )
+{
+ g_EntityListPool.Free( pMem );
+}
+
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the highest-valued delay in our list of event actions.
+//-----------------------------------------------------------------------------
+float CBaseEntityOutput::GetMaxDelay(void)
+{
+ float flMaxDelay = 0;
+ CEventAction *ev = m_ActionList;
+
+ while (ev != NULL)
+ {
+ if (ev->m_flDelay > flMaxDelay)
+ {
+ flMaxDelay = ev->m_flDelay;
+ }
+ ev = ev->m_pNext;
+ }
+
+ return(flMaxDelay);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+CBaseEntityOutput::~CBaseEntityOutput()
+{
+ CEventAction *ev = m_ActionList;
+ while (ev != NULL)
+ {
+ CEventAction *pNext = ev->m_pNext;
+ delete ev;
+ ev = pNext;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fires the event, causing a sequence of action to occur in other ents.
+// Input : pActivator - Entity that initiated this sequence of actions.
+// pCaller - Entity that is actually causing the event.
+//-----------------------------------------------------------------------------
+void CBaseEntityOutput::FireOutput(variant_t Value, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
+{
+ //
+ // Iterate through all eventactions and fire them off.
+ //
+ CEventAction *ev = m_ActionList;
+ CEventAction *prev = NULL;
+
+ while (ev != NULL)
+ {
+ if (ev->m_iParameter == NULL_STRING)
+ {
+ //
+ // Post the event with the default parameter.
+ //
+ g_EventQueue.AddEvent( STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), Value, ev->m_flDelay + fDelay, pActivator, pCaller, ev->m_iIDStamp );
+ }
+ else
+ {
+ //
+ // Post the event with a parameter override.
+ //
+ variant_t ValueOverride;
+ ValueOverride.SetString( ev->m_iParameter );
+ g_EventQueue.AddEvent( STRING(ev->m_iTarget), STRING(ev->m_iTargetInput), ValueOverride, ev->m_flDelay, pActivator, pCaller, ev->m_iIDStamp );
+ }
+
+ if ( ev->m_flDelay )
+ {
+ char szBuffer[256];
+ Q_snprintf( szBuffer,
+ sizeof(szBuffer),
+ "(%0.2f) output: (%s,%s) -> (%s,%s,%.1f)(%s)\n",
+ engine->GetServerTime(),
+ pCaller ? STRING(pCaller->m_iClassname) : "NULL",
+ pCaller ? STRING(pCaller->GetEntityName()) : "NULL",
+ STRING(ev->m_iTarget),
+ STRING(ev->m_iTargetInput),
+ ev->m_flDelay,
+ STRING(ev->m_iParameter) );
+
+ DevMsg( 2, "%s", szBuffer );
+ ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
+ }
+ else
+ {
+ char szBuffer[256];
+ Q_snprintf( szBuffer,
+ sizeof(szBuffer),
+ "(%0.2f) output: (%s,%s) -> (%s,%s)(%s)\n",
+ engine->GetServerTime(),
+ pCaller ? STRING(pCaller->m_iClassname) : "NULL",
+ pCaller ? STRING(pCaller->GetEntityName()) : "NULL", STRING(ev->m_iTarget),
+ STRING(ev->m_iTargetInput),
+ STRING(ev->m_iParameter) );
+
+ DevMsg( 2, "%s", szBuffer );
+ ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
+ }
+
+ if ( pCaller && pCaller->m_debugOverlays & OVERLAY_MESSAGE_BIT)
+ {
+ pCaller->DrawOutputOverlay(ev);
+ }
+
+ //
+ // Remove the event action from the list if it was set to be fired a finite
+ // number of times (and has been).
+ //
+ bool bRemove = false;
+ if (ev->m_nTimesToFire != EVENT_FIRE_ALWAYS)
+ {
+ ev->m_nTimesToFire--;
+ if (ev->m_nTimesToFire == 0)
+ {
+ char szBuffer[256];
+ Q_snprintf( szBuffer, sizeof(szBuffer), "Removing from action list: (%s,%s) -> (%s,%s)\n", pCaller ? STRING(pCaller->m_iClassname) : "NULL", pCaller ? STRING(pCaller->GetEntityName()) : "NULL", STRING(ev->m_iTarget), STRING(ev->m_iTargetInput));
+ DevMsg( 2, "%s", szBuffer );
+ ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
+ bRemove = true;
+ }
+ }
+
+ if (!bRemove)
+ {
+ prev = ev;
+ ev = ev->m_pNext;
+ }
+ else
+ {
+ if (prev != NULL)
+ {
+ prev->m_pNext = ev->m_pNext;
+ }
+ else
+ {
+ m_ActionList = ev->m_pNext;
+ }
+
+ CEventAction *next = ev->m_pNext;
+ delete ev;
+ ev = next;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parameterless firing of an event
+// Input : pActivator -
+// pCaller -
+//-----------------------------------------------------------------------------
+void COutputEvent::FireOutput(CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
+{
+ variant_t Val;
+ Val.Set( FIELD_VOID, NULL );
+ CBaseEntityOutput::FireOutput(Val, pActivator, pCaller, fDelay);
+}
+
+
+void CBaseEntityOutput::ParseEventAction( const char *EventData )
+{
+ AddEventAction( new CEventAction( EventData ) );
+}
+
+void CBaseEntityOutput::AddEventAction( CEventAction *pEventAction )
+{
+ pEventAction->m_pNext = m_ActionList;
+ m_ActionList = pEventAction;
+}
+
+
+// save data description for the event queue
+BEGIN_SIMPLE_DATADESC( CBaseEntityOutput )
+
+ DEFINE_CUSTOM_FIELD( m_Value, variantFuncs ),
+
+ // This is saved manually by CBaseEntityOutput::Save
+ // DEFINE_FIELD( m_ActionList, CEventAction ),
+END_DATADESC()
+
+
+int CBaseEntityOutput::Save( ISave &save )
+{
+ // save that value out to disk, so we know how many to restore
+ if ( !save.WriteFields( "Value", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
+ return 0;
+
+ for ( CEventAction *ev = m_ActionList; ev != NULL; ev = ev->m_pNext )
+ {
+ if ( !save.WriteFields( "EntityOutput", ev, NULL, ev->m_DataMap.dataDesc, ev->m_DataMap.dataNumFields ) )
+ return 0;
+ }
+
+ return 1;
+}
+
+int CBaseEntityOutput::Restore( IRestore &restore, int elementCount )
+{
+ // load the number of items saved
+ if ( !restore.ReadFields( "Value", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
+ return 0;
+
+ m_ActionList = NULL;
+
+ // read in all the fields
+ CEventAction *lastEv = NULL;
+ for ( int i = 0; i < elementCount; i++ )
+ {
+ CEventAction *ev = new CEventAction(NULL);
+
+ if ( !restore.ReadFields( "EntityOutput", ev, NULL, ev->m_DataMap.dataDesc, ev->m_DataMap.dataNumFields ) )
+ return 0;
+
+ // add it to the list in the same order it was saved in
+ if ( lastEv )
+ {
+ lastEv->m_pNext = ev;
+ }
+ else
+ {
+ m_ActionList = ev;
+ }
+ ev->m_pNext = NULL;
+ lastEv = ev;
+ }
+
+ return 1;
+}
+
+int CBaseEntityOutput::NumberOfElements( void )
+{
+ int count = 0;
+ for ( CEventAction *ev = m_ActionList; ev != NULL; ev = ev->m_pNext )
+ {
+ count++;
+ }
+ return count;
+}
+
+/// Delete every single action in the action list.
+void CBaseEntityOutput::DeleteAllElements( void )
+{
+ // walk front to back, deleting as we go. We needn't fix up pointers because
+ // EVERYTHING will die.
+
+ CEventAction *pNext = m_ActionList;
+ // wipe out the head
+ m_ActionList = NULL;
+ while (pNext)
+ {
+ register CEventAction *strikeThis = pNext;
+ pNext = pNext->m_pNext;
+ delete strikeThis;
+ }
+
+}
+
+/// EVENTS save/restore parsing wrapper
+
+class CEventsSaveDataOps : public ISaveRestoreOps
+{
+ virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
+ {
+ AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CEventsSaveDataOps does not support arrays");
+
+ CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
+ const int fieldSize = fieldInfo.pTypeDesc->fieldSize;
+ for ( int i = 0; i < fieldSize; i++, ev++ )
+ {
+ // save out the number of fields
+ int numElements = ev->NumberOfElements();
+ pSave->WriteInt( &numElements, 1 );
+
+ // save the event data
+ ev->Save( *pSave );
+ }
+ }
+
+ virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
+ {
+ AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CEventsSaveDataOps does not support arrays");
+
+ CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
+ const int fieldSize = fieldInfo.pTypeDesc->fieldSize;
+ for ( int i = 0; i < fieldSize; i++, ev++ )
+ {
+ int nElements = pRestore->ReadInt();
+
+ Assert( nElements < 100 );
+
+ ev->Restore( *pRestore, nElements );
+ }
+ }
+
+ virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ AssertMsg( fieldInfo.pTypeDesc->fieldSize == 1, "CEventsSaveDataOps does not support arrays");
+
+ // check all the elements of the array (usually only 1)
+ CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
+ const int fieldSize = fieldInfo.pTypeDesc->fieldSize;
+ for ( int i = 0; i < fieldSize; i++, ev++ )
+ {
+ // It's not empty if it has events or if it has a non-void variant value
+ if (( ev->NumberOfElements() != 0 ) || ( ev->ValueFieldType() != FIELD_VOID ))
+ return 0;
+ }
+
+ // variant has no data
+ return 1;
+ }
+
+ virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ // Don't no how to. This is okay, since objects of this type
+ // are always born clean before restore, and not reused
+ }
+
+ virtual bool Parse( const SaveRestoreFieldInfo_t &fieldInfo, char const* szValue )
+ {
+ CBaseEntityOutput *ev = (CBaseEntityOutput*)fieldInfo.pField;
+ ev->ParseEventAction( szValue );
+ return true;
+ }
+};
+
+CEventsSaveDataOps g_EventsSaveDataOps;
+ISaveRestoreOps *eventFuncs = &g_EventsSaveDataOps;
+
+//-----------------------------------------------------------------------------
+// CMultiInputVar implementation
+//
+// Purpose: holds a list of inputs and their ID tags
+// used for entities that hold inputs from a set of other entities
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose: destructor, frees the data list
+//-----------------------------------------------------------------------------
+CMultiInputVar::~CMultiInputVar()
+{
+ if ( m_InputList )
+ {
+ while ( m_InputList->next != NULL )
+ {
+ inputitem_t *input = m_InputList->next;
+ m_InputList->next = input->next;
+ delete input;
+ }
+ delete m_InputList;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the data set with a new value
+// Input : newVal - the new value to add to or update in the list
+// outputID - the source of the value
+//-----------------------------------------------------------------------------
+void CMultiInputVar::AddValue( variant_t newVal, int outputID )
+{
+ // see if it's already in the list
+ inputitem_t *inp;
+ for ( inp = m_InputList; inp != NULL; inp = inp->next )
+ {
+ // already in list, so just update this link
+ if ( inp->outputID == outputID )
+ {
+ inp->value = newVal;
+ return;
+ }
+ }
+
+ // add to start of list
+ inp = new inputitem_t;
+ inp->value = newVal;
+ inp->outputID = outputID;
+ if ( !m_InputList )
+ {
+ m_InputList = inp;
+ inp->next = NULL;
+ }
+ else
+ {
+ inp->next = m_InputList;
+ m_InputList = inp;
+ }
+}
+
+
+#include "tier0/memdbgoff.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: allocates memory from the entitylist pool
+//-----------------------------------------------------------------------------
+void *CMultiInputVar::inputitem_t::operator new( size_t stAllocateBlock )
+{
+ return g_EntityListPool.Alloc( stAllocateBlock );
+}
+
+void *CMultiInputVar::inputitem_t::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
+{
+ return g_EntityListPool.Alloc( stAllocateBlock );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: frees memory from the entitylist pool
+//-----------------------------------------------------------------------------
+void CMultiInputVar::inputitem_t::operator delete( void *pMem )
+{
+ g_EntityListPool.Free( pMem );
+}
+
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// CEventQueue implementation
+//
+// Purpose: holds and executes a global prioritized queue of entity actions
+//-----------------------------------------------------------------------------
+DEFINE_FIXEDSIZE_ALLOCATOR( EventQueuePrioritizedEvent_t, 128, CUtlMemoryPool::GROW_SLOW );
+
+CEventQueue g_EventQueue;
+
+CEventQueue::CEventQueue()
+{
+ m_Events.m_flFireTime = -FLT_MAX;
+ m_Events.m_pNext = NULL;
+
+ Init();
+}
+
+CEventQueue::~CEventQueue()
+{
+ Clear();
+}
+
+// Robin: Left here for backwards compatability.
+class CEventQueueSaveLoadProxy : public CLogicalEntity
+{
+ DECLARE_CLASS( CEventQueueSaveLoadProxy, CLogicalEntity );
+
+ int Save( ISave &save )
+ {
+ if ( !BaseClass::Save(save) )
+ return 0;
+
+ // save out the message queue
+ return g_EventQueue.Save( save );
+ }
+
+
+ int Restore( IRestore &restore )
+ {
+ if ( !BaseClass::Restore(restore) )
+ return 0;
+
+ // restore the event queue
+ int iReturn = g_EventQueue.Restore( restore );
+
+ // Now remove myself, because the CEventQueue_SaveRestoreBlockHandler
+ // will handle future saves.
+ UTIL_Remove( this );
+
+ return iReturn;
+ }
+};
+
+LINK_ENTITY_TO_CLASS(event_queue_saveload_proxy, CEventQueueSaveLoadProxy);
+
+//-----------------------------------------------------------------------------
+// EVENT QUEUE SAVE / RESTORE
+//-----------------------------------------------------------------------------
+static short EVENTQUEUE_SAVE_RESTORE_VERSION = 1;
+
+class CEventQueue_SaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
+{
+public:
+ const char *GetBlockName()
+ {
+ return "EventQueue";
+ }
+
+ //---------------------------------
+
+ void Save( ISave *pSave )
+ {
+ g_EventQueue.Save( *pSave );
+ }
+
+ //---------------------------------
+
+ void WriteSaveHeaders( ISave *pSave )
+ {
+ pSave->WriteShort( &EVENTQUEUE_SAVE_RESTORE_VERSION );
+ }
+
+ //---------------------------------
+
+ void ReadRestoreHeaders( IRestore *pRestore )
+ {
+ // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
+ short version;
+ pRestore->ReadShort( &version );
+ m_fDoLoad = ( version == EVENTQUEUE_SAVE_RESTORE_VERSION );
+ }
+
+ //---------------------------------
+
+ void Restore( IRestore *pRestore, bool createPlayers )
+ {
+ if ( m_fDoLoad )
+ {
+ g_EventQueue.Restore( *pRestore );
+ }
+ }
+
+private:
+ bool m_fDoLoad;
+};
+
+//-----------------------------------------------------------------------------
+
+CEventQueue_SaveRestoreBlockHandler g_EventQueue_SaveRestoreBlockHandler;
+
+//-------------------------------------
+
+ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler()
+{
+ return &g_EventQueue_SaveRestoreBlockHandler;
+}
+
+
+void CEventQueue::Init( void )
+{
+ Clear();
+}
+
+void CEventQueue::Clear( void )
+{
+ // delete all the events in the queue
+ EventQueuePrioritizedEvent_t *pe = m_Events.m_pNext;
+
+ while ( pe != NULL )
+ {
+ EventQueuePrioritizedEvent_t *next = pe->m_pNext;
+ delete pe;
+ pe = next;
+ }
+
+ m_Events.m_pNext = NULL;
+}
+
+void CEventQueue::Dump( void )
+{
+ EventQueuePrioritizedEvent_t *pe = m_Events.m_pNext;
+
+ Msg( "Dumping event queue. Current time is: %.2f\n", engine->GetServerTime() );
+
+ while ( pe != NULL )
+ {
+ EventQueuePrioritizedEvent_t *next = pe->m_pNext;
+
+ Msg(" (%.2f) Target: '%s', Input: '%s', Parameter '%s'. Activator: '%s', Caller '%s'. \n",
+ pe->m_flFireTime,
+ STRING(pe->m_iTarget),
+ STRING(pe->m_iTargetInput),
+ pe->m_VariantValue.String(),
+ pe->m_pActivator ? pe->m_pActivator->GetDebugName() : "None",
+ pe->m_pCaller ? pe->m_pCaller->GetDebugName() : "None" );
+
+ pe = next;
+ }
+
+ Msg("Finished dump.\n");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: adds the action into the correct spot in the priority queue, targeting entity via string name
+//-----------------------------------------------------------------------------
+void CEventQueue::AddEvent( const char *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID )
+{
+ // build the new event
+ EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t;
+ newEvent->m_flFireTime = engine->GetServerTime() + fireDelay; // priority key in the priority queue
+ newEvent->m_iTarget = MAKE_STRING( target );
+ newEvent->m_pEntTarget = NULL;
+ newEvent->m_iTargetInput = MAKE_STRING( targetInput );
+ newEvent->m_pActivator = pActivator;
+ newEvent->m_pCaller = pCaller;
+ newEvent->m_VariantValue = Value;
+ newEvent->m_iOutputID = outputID;
+
+ AddEvent( newEvent );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds the action into the correct spot in the priority queue, targeting entity via pointer
+//-----------------------------------------------------------------------------
+void CEventQueue::AddEvent( CBaseEntity *target, const char *targetInput, variant_t Value, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID )
+{
+ // build the new event
+ EventQueuePrioritizedEvent_t *newEvent = new EventQueuePrioritizedEvent_t;
+ newEvent->m_flFireTime = engine->GetServerTime() + fireDelay; // primary priority key in the priority queue
+ newEvent->m_iTarget = NULL_STRING;
+ newEvent->m_pEntTarget = target;
+ newEvent->m_iTargetInput = MAKE_STRING( targetInput );
+ newEvent->m_pActivator = pActivator;
+ newEvent->m_pCaller = pCaller;
+ newEvent->m_VariantValue = Value;
+ newEvent->m_iOutputID = outputID;
+
+ AddEvent( newEvent );
+}
+
+void CEventQueue::AddEvent( CBaseEntity *target, const char *action, float fireDelay, CBaseEntity *pActivator, CBaseEntity *pCaller, int outputID )
+{
+ variant_t Value;
+ Value.Set( FIELD_VOID, NULL );
+ AddEvent( target, action, Value, fireDelay, pActivator, pCaller, outputID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: private function, adds an event into the list
+// Input : *newEvent - the (already built) event to add
+//-----------------------------------------------------------------------------
+void CEventQueue::AddEvent( EventQueuePrioritizedEvent_t *newEvent )
+{
+ // loop through the actions looking for a place to insert
+ EventQueuePrioritizedEvent_t *pe;
+ for ( pe = &m_Events; pe->m_pNext != NULL; pe = pe->m_pNext )
+ {
+ if ( pe->m_pNext->m_flFireTime > newEvent->m_flFireTime )
+ {
+ break;
+ }
+ }
+
+ Assert( pe );
+
+ // insert
+ newEvent->m_pNext = pe->m_pNext;
+ newEvent->m_pPrev = pe;
+ pe->m_pNext = newEvent;
+ if ( newEvent->m_pNext )
+ {
+ newEvent->m_pNext->m_pPrev = newEvent;
+ }
+}
+
+void CEventQueue::RemoveEvent( EventQueuePrioritizedEvent_t *pe )
+{
+ Assert( pe->m_pPrev );
+ pe->m_pPrev->m_pNext = pe->m_pNext;
+ if ( pe->m_pNext )
+ {
+ pe->m_pNext->m_pPrev = pe->m_pPrev;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: fires off any events in the queue who's fire time is (or before) the present time
+//-----------------------------------------------------------------------------
+void CEventQueue::ServiceEvents( void )
+{
+ if (!CBaseEntity::Debug_ShouldStep())
+ {
+ return;
+ }
+
+ EventQueuePrioritizedEvent_t *pe = m_Events.m_pNext;
+
+ while ( pe != NULL && pe->m_flFireTime <= engine->GetServerTime() )
+ {
+ MDLCACHE_CRITICAL_SECTION();
+
+ bool targetFound = false;
+
+ // find the targets
+ if ( pe->m_iTarget != NULL_STRING )
+ {
+ // In the context the event, the searching entity is also the caller
+ CBaseEntity *pSearchingEntity = pe->m_pCaller;
+ CBaseEntity *target = NULL;
+ while ( 1 )
+ {
+ target = gEntList.FindEntityByName( target, pe->m_iTarget, pSearchingEntity, pe->m_pActivator, pe->m_pCaller );
+ if ( !target )
+ break;
+
+ // pump the action into the target
+ target->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID );
+ targetFound = true;
+ }
+ }
+
+ // direct pointer
+ if ( pe->m_pEntTarget != NULL )
+ {
+ pe->m_pEntTarget->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID );
+ targetFound = true;
+ }
+
+ if ( !targetFound )
+ {
+ // See if we can find a target if we treat the target as a classname
+ if ( pe->m_iTarget != NULL_STRING )
+ {
+ CBaseEntity *target = NULL;
+ while ( 1 )
+ {
+ target = gEntList.FindEntityByClassname( target, STRING(pe->m_iTarget) );
+ if ( !target )
+ break;
+
+ // pump the action into the target
+ target->AcceptInput( STRING(pe->m_iTargetInput), pe->m_pActivator, pe->m_pCaller, pe->m_VariantValue, pe->m_iOutputID );
+ targetFound = true;
+ }
+ }
+ }
+
+ if ( !targetFound )
+ {
+ const char *pClass ="", *pName = "";
+
+ // might be NULL
+ if ( pe->m_pCaller )
+ {
+ pClass = STRING(pe->m_pCaller->m_iClassname);
+ pName = STRING(pe->m_pCaller->GetEntityName());
+ }
+
+ char szBuffer[256];
+ Q_snprintf( szBuffer, sizeof(szBuffer), "unhandled input: (%s) -> (%s), from (%s,%s); target entity not found\n", STRING(pe->m_iTargetInput), STRING(pe->m_iTarget), pClass, pName );
+ DevMsg( 2, "%s", szBuffer );
+ ADD_DEBUG_HISTORY( HISTORY_ENTITY_IO, szBuffer );
+ }
+
+ // remove the event from the list (remembering that the queue may have been added to)
+ RemoveEvent( pe );
+ delete pe;
+
+ //
+ // If we are in debug mode, exit the loop if we have fired the correct number of events.
+ //
+ if (CBaseEntity::Debug_IsPaused())
+ {
+ if (!CBaseEntity::Debug_Step())
+ {
+ break;
+ }
+ }
+
+ // restart the list (to catch any new items have probably been added to the queue)
+ pe = m_Events.m_pNext;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dumps the contents of the Entity I/O event queue to the console.
+//-----------------------------------------------------------------------------
+void CC_DumpEventQueue()
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ g_EventQueue.Dump();
+}
+static ConCommand dumpeventqueue( "dumpeventqueue", CC_DumpEventQueue, "Dump the contents of the Entity I/O event queue to the console." );
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes all pending events from the I/O queue that were added by the
+// given caller.
+//
+// TODO: This is only as reliable as callers are in passing the correct
+// caller pointer when they fire the outputs. Make more foolproof.
+//-----------------------------------------------------------------------------
+void CEventQueue::CancelEvents( CBaseEntity *pCaller )
+{
+ if (!pCaller)
+ return;
+
+ EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext;
+
+ while (pCur != NULL)
+ {
+ bool bDelete = false;
+ if (pCur->m_pCaller == pCaller)
+ {
+ // Pointers match; make sure everything else matches.
+ if (!stricmp(STRING(pCur->m_pCaller->GetEntityName()), STRING(pCaller->GetEntityName())) &&
+ !stricmp(pCur->m_pCaller->GetClassname(), pCaller->GetClassname()))
+ {
+ // Found a matching event; delete it from the queue.
+ bDelete = true;
+ }
+ }
+
+ EventQueuePrioritizedEvent_t *pCurSave = pCur;
+ pCur = pCur->m_pNext;
+
+ if (bDelete)
+ {
+ RemoveEvent( pCurSave );
+ delete pCurSave;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes all pending events of the specified type from the I/O queue of the specified target
+//
+// TODO: This is only as reliable as callers are in passing the correct
+// caller pointer when they fire the outputs. Make more foolproof.
+//-----------------------------------------------------------------------------
+void CEventQueue::CancelEventOn( CBaseEntity *pTarget, const char *sInputName )
+{
+ if (!pTarget)
+ return;
+
+ EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext;
+
+ while (pCur != NULL)
+ {
+ bool bDelete = false;
+ if (pCur->m_pEntTarget == pTarget)
+ {
+ if ( !Q_strncmp( STRING(pCur->m_iTargetInput), sInputName, strlen(sInputName) ) )
+ {
+ // Found a matching event; delete it from the queue.
+ bDelete = true;
+ }
+ }
+
+ EventQueuePrioritizedEvent_t *pCurSave = pCur;
+ pCur = pCur->m_pNext;
+
+ if (bDelete)
+ {
+ RemoveEvent( pCurSave );
+ delete pCurSave;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the target has any pending inputs.
+// Input : *pTarget -
+// *sInputName - NULL for any input, or a specified one
+//-----------------------------------------------------------------------------
+bool CEventQueue::HasEventPending( CBaseEntity *pTarget, const char *sInputName )
+{
+ if (!pTarget)
+ return false;
+
+ EventQueuePrioritizedEvent_t *pCur = m_Events.m_pNext;
+
+ while (pCur != NULL)
+ {
+ if (pCur->m_pEntTarget == pTarget)
+ {
+ if ( !sInputName )
+ return true;
+
+ if ( !Q_strncmp( STRING(pCur->m_iTargetInput), sInputName, strlen(sInputName) ) )
+ return true;
+ }
+
+ pCur = pCur->m_pNext;
+ }
+
+ return false;
+}
+
+void ServiceEventQueue( void )
+{
+ VPROF("ServiceEventQueue()");
+
+ g_EventQueue.ServiceEvents();
+}
+
+
+
+// save data description for the event queue
+BEGIN_SIMPLE_DATADESC( CEventQueue )
+ // These are saved explicitly in CEventQueue::Save below
+ // DEFINE_FIELD( m_Events, EventQueuePrioritizedEvent_t ),
+
+ DEFINE_FIELD( m_iListCount, FIELD_INTEGER ), // this value is only used during save/restore
+END_DATADESC()
+
+
+// save data for a single event in the queue
+BEGIN_SIMPLE_DATADESC( EventQueuePrioritizedEvent_t )
+ DEFINE_FIELD( m_flFireTime, FIELD_TIME ),
+ DEFINE_FIELD( m_iTarget, FIELD_STRING ),
+ DEFINE_FIELD( m_iTargetInput, FIELD_STRING ),
+ DEFINE_FIELD( m_pActivator, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_pCaller, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_pEntTarget, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_iOutputID, FIELD_INTEGER ),
+ DEFINE_CUSTOM_FIELD( m_VariantValue, variantFuncs ),
+
+// DEFINE_FIELD( m_pNext, FIELD_??? ),
+// DEFINE_FIELD( m_pPrev, FIELD_??? ),
+END_DATADESC()
+
+
+int CEventQueue::Save( ISave &save )
+{
+ // count the number of items in the queue
+ EventQueuePrioritizedEvent_t *pe;
+
+ m_iListCount = 0;
+ for ( pe = m_Events.m_pNext; pe != NULL; pe = pe->m_pNext )
+ {
+ m_iListCount++;
+ }
+
+ // save that value out to disk, so we know how many to restore
+ if ( !save.WriteFields( "EventQueue", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
+ return 0;
+
+ // cycle through all the events, saving them all
+ for ( pe = m_Events.m_pNext; pe != NULL; pe = pe->m_pNext )
+ {
+ if ( !save.WriteFields( "PEvent", pe, NULL, pe->m_DataMap.dataDesc, pe->m_DataMap.dataNumFields ) )
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int CEventQueue::Restore( IRestore &restore )
+{
+ // clear the event queue
+ Clear();
+
+ // rebuild the event queue by restoring all the queue items
+ EventQueuePrioritizedEvent_t tmpEvent;
+
+ // load the number of items saved
+ if ( !restore.ReadFields( "EventQueue", this, NULL, m_DataMap.dataDesc, m_DataMap.dataNumFields ) )
+ return 0;
+
+ for ( int i = 0; i < m_iListCount; i++ )
+ {
+ if ( !restore.ReadFields( "PEvent", &tmpEvent, NULL, tmpEvent.m_DataMap.dataDesc, tmpEvent.m_DataMap.dataNumFields ) )
+ return 0;
+
+ // add the restored event into the list
+ if ( tmpEvent.m_pEntTarget )
+ {
+ AddEvent( tmpEvent.m_pEntTarget,
+ STRING(tmpEvent.m_iTargetInput),
+ tmpEvent.m_VariantValue,
+ tmpEvent.m_flFireTime - engine->GetServerTime(),
+ tmpEvent.m_pActivator,
+ tmpEvent.m_pCaller,
+ tmpEvent.m_iOutputID );
+ }
+ else
+ {
+ AddEvent( STRING(tmpEvent.m_iTarget),
+ STRING(tmpEvent.m_iTargetInput),
+ tmpEvent.m_VariantValue,
+ tmpEvent.m_flFireTime - engine->GetServerTime(),
+ tmpEvent.m_pActivator,
+ tmpEvent.m_pCaller,
+ tmpEvent.m_iOutputID );
+ }
+ }
+
+ return 1;
+}
+
+////////////////////////// variant_t implementation //////////////////////////
+
+// BUGBUG: Add support for function pointer save/restore to variants
+// BUGBUG: Must pass datamap_t to read/write fields
+void variant_t::Set( fieldtype_t ftype, void *data )
+{
+ fieldType = ftype;
+
+ switch ( ftype )
+ {
+ case FIELD_BOOLEAN: bVal = *((bool *)data); break;
+ case FIELD_CHARACTER: iVal = *((char *)data); break;
+ case FIELD_SHORT: iVal = *((short *)data); break;
+ case FIELD_INTEGER: iVal = *((int *)data); break;
+ case FIELD_STRING: iszVal = *((string_t *)data); break;
+ case FIELD_FLOAT: flVal = *((float *)data); break;
+ case FIELD_COLOR32: rgbaVal = *((color32 *)data); break;
+
+ case FIELD_VECTOR:
+ case FIELD_POSITION_VECTOR:
+ {
+ vecVal[0] = ((float *)data)[0];
+ vecVal[1] = ((float *)data)[1];
+ vecVal[2] = ((float *)data)[2];
+ break;
+ }
+
+ case FIELD_EHANDLE: eVal = *((EHANDLE *)data); break;
+ case FIELD_CLASSPTR: eVal = *((CBaseEntity **)data); break;
+ case FIELD_VOID:
+ default:
+ iVal = 0; fieldType = FIELD_VOID;
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies the value in the variant into a block of memory
+// Input : *data - the block to write into
+//-----------------------------------------------------------------------------
+void variant_t::SetOther( void *data )
+{
+ switch ( fieldType )
+ {
+ case FIELD_BOOLEAN: *((bool *)data) = bVal != 0; break;
+ case FIELD_CHARACTER: *((char *)data) = iVal; break;
+ case FIELD_SHORT: *((short *)data) = iVal; break;
+ case FIELD_INTEGER: *((int *)data) = iVal; break;
+ case FIELD_STRING: *((string_t *)data) = iszVal; break;
+ case FIELD_FLOAT: *((float *)data) = flVal; break;
+ case FIELD_COLOR32: *((color32 *)data) = rgbaVal; break;
+
+ case FIELD_VECTOR:
+ case FIELD_POSITION_VECTOR:
+ {
+ ((float *)data)[0] = vecVal[0];
+ ((float *)data)[1] = vecVal[1];
+ ((float *)data)[2] = vecVal[2];
+ break;
+ }
+
+ case FIELD_EHANDLE: *((EHANDLE *)data) = eVal; break;
+ case FIELD_CLASSPTR: *((CBaseEntity **)data) = eVal; break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Converts the variant to a new type. This function defines which I/O
+// types can be automatically converted between. Connections that require
+// an unsupported conversion will cause an error message at runtime.
+// Input : newType - the type to convert to
+// Output : Returns true on success, false if the conversion is not legal
+//-----------------------------------------------------------------------------
+bool variant_t::Convert( fieldtype_t newType )
+{
+ if ( newType == fieldType )
+ {
+ return true;
+ }
+
+ //
+ // Converting to a null value is easy.
+ //
+ if ( newType == FIELD_VOID )
+ {
+ Set( FIELD_VOID, NULL );
+ return true;
+ }
+
+ //
+ // FIELD_INPUT accepts the variant type directly.
+ //
+ if ( newType == FIELD_INPUT )
+ {
+ return true;
+ }
+
+ switch ( fieldType )
+ {
+ case FIELD_INTEGER:
+ {
+ switch ( newType )
+ {
+ case FIELD_FLOAT:
+ {
+ SetFloat( (float) iVal );
+ return true;
+ }
+
+ case FIELD_BOOLEAN:
+ {
+ SetBool( iVal != 0 );
+ return true;
+ }
+ }
+ break;
+ }
+
+ case FIELD_FLOAT:
+ {
+ switch ( newType )
+ {
+ case FIELD_INTEGER:
+ {
+ SetInt( (int) flVal );
+ return true;
+ }
+
+ case FIELD_BOOLEAN:
+ {
+ SetBool( flVal != 0 );
+ return true;
+ }
+ }
+ break;
+ }
+
+ //
+ // Everyone must convert from FIELD_STRING if possible, since
+ // parameter overrides are always passed as strings.
+ //
+ case FIELD_STRING:
+ {
+ switch ( newType )
+ {
+ case FIELD_INTEGER:
+ {
+ if (iszVal != NULL_STRING)
+ {
+ SetInt(atoi(STRING(iszVal)));
+ }
+ else
+ {
+ SetInt(0);
+ }
+ return true;
+ }
+
+ case FIELD_FLOAT:
+ {
+ if (iszVal != NULL_STRING)
+ {
+ SetFloat(atof(STRING(iszVal)));
+ }
+ else
+ {
+ SetFloat(0);
+ }
+ return true;
+ }
+
+ case FIELD_BOOLEAN:
+ {
+ if (iszVal != NULL_STRING)
+ {
+ SetBool( atoi(STRING(iszVal)) != 0 );
+ }
+ else
+ {
+ SetBool(false);
+ }
+ return true;
+ }
+
+ case FIELD_VECTOR:
+ {
+ Vector tmpVec = vec3_origin;
+ if (sscanf(STRING(iszVal), "[%f %f %f]", &tmpVec[0], &tmpVec[1], &tmpVec[2]) == 0)
+ {
+ // Try sucking out 3 floats with no []s
+ sscanf(STRING(iszVal), "%f %f %f", &tmpVec[0], &tmpVec[1], &tmpVec[2]);
+ }
+ SetVector3D( tmpVec );
+ return true;
+ }
+
+ case FIELD_COLOR32:
+ {
+ int nRed = 0;
+ int nGreen = 0;
+ int nBlue = 0;
+ int nAlpha = 255;
+
+ sscanf(STRING(iszVal), "%d %d %d %d", &nRed, &nGreen, &nBlue, &nAlpha);
+ SetColor32( nRed, nGreen, nBlue, nAlpha );
+ return true;
+ }
+
+ case FIELD_EHANDLE:
+ {
+ // convert the string to an entity by locating it by classname
+ CBaseEntity *ent = NULL;
+ if ( iszVal != NULL_STRING )
+ {
+ // FIXME: do we need to pass an activator in here?
+ ent = gEntList.FindEntityByName( NULL, iszVal );
+ }
+ SetEntity( ent );
+ return true;
+ }
+ }
+
+ break;
+ }
+
+ case FIELD_EHANDLE:
+ {
+ switch ( newType )
+ {
+ case FIELD_STRING:
+ {
+ // take the entities targetname as the string
+ string_t iszStr = NULL_STRING;
+ if ( eVal != NULL )
+ {
+ SetString( eVal->GetEntityName() );
+ }
+ return true;
+ }
+ }
+ break;
+ }
+ }
+
+ // invalid conversion
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: All types must be able to display as strings for debugging purposes.
+// Output : Returns a pointer to the string that represents this value.
+//
+// NOTE: The returned pointer should not be stored by the caller as
+// subsequent calls to this function will overwrite the contents
+// of the buffer!
+//-----------------------------------------------------------------------------
+const char *variant_t::ToString( void ) const
+{
+ COMPILE_TIME_ASSERT( sizeof(string_t) == sizeof(int) );
+
+ static char szBuf[512];
+
+ switch (fieldType)
+ {
+ case FIELD_STRING:
+ {
+ return(STRING(iszVal));
+ }
+
+ case FIELD_BOOLEAN:
+ {
+ if (bVal == 0)
+ {
+ Q_strncpy(szBuf, "false",sizeof(szBuf));
+ }
+ else
+ {
+ Q_strncpy(szBuf, "true",sizeof(szBuf));
+ }
+ return(szBuf);
+ }
+
+ case FIELD_INTEGER:
+ {
+ Q_snprintf( szBuf, sizeof( szBuf ), "%i", iVal );
+ return(szBuf);
+ }
+
+ case FIELD_FLOAT:
+ {
+ Q_snprintf(szBuf,sizeof(szBuf), "%g", flVal);
+ return(szBuf);
+ }
+
+ case FIELD_COLOR32:
+ {
+ Q_snprintf(szBuf,sizeof(szBuf), "%d %d %d %d", (int)rgbaVal.r, (int)rgbaVal.g, (int)rgbaVal.b, (int)rgbaVal.a);
+ return(szBuf);
+ }
+
+ case FIELD_VECTOR:
+ {
+ Q_snprintf(szBuf,sizeof(szBuf), "[%g %g %g]", (double)vecVal[0], (double)vecVal[1], (double)vecVal[2]);
+ return(szBuf);
+ }
+
+ case FIELD_VOID:
+ {
+ szBuf[0] = '\0';
+ return(szBuf);
+ }
+
+ case FIELD_EHANDLE:
+ {
+ const char *pszName = (Entity()) ? STRING(Entity()->GetEntityName()) : "<<null entity>>";
+ Q_strncpy( szBuf, pszName, 512 );
+ return (szBuf);
+ }
+ }
+
+ return("No conversion to string");
+}
+
+#define classNameTypedef variant_t // to satisfy DEFINE... macros
+
+typedescription_t variant_t::m_SaveBool[] =
+{
+ DEFINE_FIELD( bVal, FIELD_BOOLEAN ),
+};
+typedescription_t variant_t::m_SaveInt[] =
+{
+ DEFINE_FIELD( iVal, FIELD_INTEGER ),
+};
+typedescription_t variant_t::m_SaveFloat[] =
+{
+ DEFINE_FIELD( flVal, FIELD_FLOAT ),
+};
+typedescription_t variant_t::m_SaveEHandle[] =
+{
+ DEFINE_FIELD( eVal, FIELD_EHANDLE ),
+};
+typedescription_t variant_t::m_SaveString[] =
+{
+ DEFINE_FIELD( iszVal, FIELD_STRING ),
+};
+typedescription_t variant_t::m_SaveColor[] =
+{
+ DEFINE_FIELD( rgbaVal, FIELD_COLOR32 ),
+};
+
+#undef classNameTypedef
+
+//
+// Struct for saving and restoring vector variants, since they are
+// stored as float[3] and we want to take advantage of position vector
+// fixup across level transitions.
+//
+#define classNameTypedef variant_savevector_t // to satisfy DEFINE... macros
+
+struct variant_savevector_t
+{
+ Vector vecSave;
+};
+typedescription_t variant_t::m_SaveVector[] =
+{
+ // Just here to shut up ClassCheck
+// DEFINE_ARRAY( vecVal, FIELD_FLOAT, 3 ),
+
+ DEFINE_FIELD( vecSave, FIELD_VECTOR ),
+};
+typedescription_t variant_t::m_SavePositionVector[] =
+{
+ DEFINE_FIELD( vecSave, FIELD_POSITION_VECTOR ),
+};
+#undef classNameTypedef
+
+#define classNameTypedef variant_savevmatrix_t // to satisfy DEFINE... macros
+struct variant_savevmatrix_t
+{
+ VMatrix matSave;
+};
+typedescription_t variant_t::m_SaveVMatrix[] =
+{
+ DEFINE_FIELD( matSave, FIELD_VMATRIX ),
+};
+typedescription_t variant_t::m_SaveVMatrixWorldspace[] =
+{
+ DEFINE_FIELD( matSave, FIELD_VMATRIX_WORLDSPACE ),
+};
+#undef classNameTypedef
+
+#define classNameTypedef variant_savevmatrix3x4_t // to satisfy DEFINE... macros
+struct variant_savevmatrix3x4_t
+{
+ matrix3x4_t matSave;
+};
+typedescription_t variant_t::m_SaveMatrix3x4Worldspace[] =
+{
+ DEFINE_FIELD( matSave, FIELD_MATRIX3X4_WORLDSPACE ),
+};
+#undef classNameTypedef
+
+class CVariantSaveDataOps : public CDefSaveRestoreOps
+{
+ // saves the entire array of variables
+ virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
+ {
+ variant_t *var = (variant_t*)fieldInfo.pField;
+
+ int type = var->FieldType();
+ pSave->WriteInt( &type, 1 );
+
+ switch ( var->FieldType() )
+ {
+ case FIELD_VOID:
+ break;
+ case FIELD_BOOLEAN:
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveBool, 1 );
+ break;
+ case FIELD_INTEGER:
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveInt, 1 );
+ break;
+ case FIELD_FLOAT:
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveFloat, 1 );
+ break;
+ case FIELD_EHANDLE:
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveEHandle, 1 );
+ break;
+ case FIELD_STRING:
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveString, 1 );
+ break;
+ case FIELD_COLOR32:
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveColor, 1 );
+ break;
+ case FIELD_VECTOR:
+ {
+ variant_savevector_t Temp;
+ var->Vector3D(Temp.vecSave);
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SaveVector, 1 );
+ break;
+ }
+
+ case FIELD_POSITION_VECTOR:
+ {
+ variant_savevector_t Temp;
+ var->Vector3D(Temp.vecSave);
+ pSave->WriteFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SavePositionVector, 1 );
+ break;
+ }
+
+ default:
+ Warning( "Bad type %d in saved variant_t\n", var->FieldType() );
+ Assert(0);
+ }
+ }
+
+ // restores a single instance of the variable
+ virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
+ {
+ variant_t *var = (variant_t*)fieldInfo.pField;
+
+ *var = variant_t();
+
+ var->fieldType = (_fieldtypes)pRestore->ReadInt();
+
+ switch ( var->fieldType )
+ {
+ case FIELD_VOID:
+ break;
+ case FIELD_BOOLEAN:
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveBool, 1 );
+ break;
+ case FIELD_INTEGER:
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveInt, 1 );
+ break;
+ case FIELD_FLOAT:
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveFloat, 1 );
+ break;
+ case FIELD_EHANDLE:
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveEHandle, 1 );
+ break;
+ case FIELD_STRING:
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveString, 1 );
+ break;
+ case FIELD_COLOR32:
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, var, NULL, variant_t::m_SaveColor, 1 );
+ break;
+ case FIELD_VECTOR:
+ {
+ variant_savevector_t Temp;
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SaveVector, 1 );
+ var->SetVector3D(Temp.vecSave);
+ break;
+ }
+ case FIELD_POSITION_VECTOR:
+ {
+ variant_savevector_t Temp;
+ pRestore->ReadFields( fieldInfo.pTypeDesc->fieldName, &Temp, NULL, variant_t::m_SavePositionVector, 1 );
+ var->SetPositionVector3D(Temp.vecSave);
+ break;
+ }
+ default:
+ Warning( "Bad type %d in saved variant_t\n", var->FieldType() );
+ Assert(0);
+ break;
+ }
+ }
+
+
+ virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ // check all the elements of the array (usually only 1)
+ variant_t *var = (variant_t*)fieldInfo.pField;
+ for ( int i = 0; i < fieldInfo.pTypeDesc->fieldSize; i++, var++ )
+ {
+ if ( var->FieldType() != FIELD_VOID )
+ return 0;
+ }
+
+ // variant has no data
+ return 1;
+ }
+
+ virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ // Don't no how to. This is okay, since objects of this type
+ // are always born clean before restore, and not reused
+ }
+};
+
+CVariantSaveDataOps g_VariantSaveDataOps;
+ISaveRestoreOps *variantFuncs = &g_VariantSaveDataOps;
+
+/////////////////////// entitylist /////////////////////
+
+CUtlMemoryPool g_EntListMemPool( sizeof(entitem_t), 256, CUtlMemoryPool::GROW_NONE, "g_EntListMemPool" );
+
+#include "tier0/memdbgoff.h"
+
+void *entitem_t::operator new( size_t stAllocateBlock )
+{
+ return g_EntListMemPool.Alloc( stAllocateBlock );
+}
+
+void *entitem_t::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
+{
+ return g_EntListMemPool.Alloc( stAllocateBlock );
+}
+
+void entitem_t::operator delete( void *pMem )
+{
+ g_EntListMemPool.Free( pMem );
+}
+
+#include "tier0/memdbgon.h"
+
+
+CEntityList::CEntityList()
+{
+ m_pItemList = NULL;
+ m_iNumItems = 0;
+}
+
+CEntityList::~CEntityList()
+{
+ // remove all items from the list
+ entitem_t *next, *e = m_pItemList;
+ while ( e != NULL )
+ {
+ next = e->pNext;
+ delete e;
+ e = next;
+ }
+ m_pItemList = NULL;
+}
+
+void CEntityList::AddEntity( CBaseEntity *pEnt )
+{
+ // check if it's already in the list; if not, add it
+ entitem_t *e = m_pItemList;
+ while ( e != NULL )
+ {
+ if ( e->hEnt == pEnt )
+ {
+ // it's already in the list
+ return;
+ }
+
+ if ( e->pNext == NULL )
+ {
+ // we've hit the end of the list, so tack it on
+ e->pNext = new entitem_t;
+ e->pNext->hEnt = pEnt;
+ e->pNext->pNext = NULL;
+ m_iNumItems++;
+ return;
+ }
+
+ e = e->pNext;
+ }
+
+ // empty list
+ m_pItemList = new entitem_t;
+ m_pItemList->hEnt = pEnt;
+ m_pItemList->pNext = NULL;
+ m_iNumItems = 1;
+}
+
+void CEntityList::DeleteEntity( CBaseEntity *pEnt )
+{
+ // find the entry in the list and delete it
+ entitem_t *prev = NULL, *e = m_pItemList;
+ while ( e != NULL )
+ {
+ // delete the link if it's the matching entity OR if the link is NULL
+ if ( e->hEnt == pEnt || e->hEnt == NULL )
+ {
+ if ( prev )
+ {
+ prev->pNext = e->pNext;
+ }
+ else
+ {
+ m_pItemList = e->pNext;
+ }
+
+ delete e;
+ m_iNumItems--;
+
+ // REVISIT: Is this correct? Is this just here to clean out dead EHANDLEs?
+ // restart the loop
+ e = m_pItemList;
+ prev = NULL;
+ continue;
+ }
+
+ prev = e;
+ e = e->pNext;
+ }
+}
+