diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/cbase.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/cbase.cpp')
| -rw-r--r-- | sp/src/game/server/cbase.cpp | 3674 |
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; + } +} + |