From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/server/cbase.cpp | 1807 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1807 insertions(+) create mode 100644 mp/src/game/server/cbase.cpp (limited to 'mp/src/game/server/cbase.cpp') diff --git a/mp/src/game/server/cbase.cpp b/mp/src/game/server/cbase.cpp new file mode 100644 index 00000000..f1f9b0d6 --- /dev/null +++ b/mp/src/game/server/cbase.cpp @@ -0,0 +1,1807 @@ +//========= 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", gpGlobals->curtime, 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", gpGlobals->curtime, 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", gpGlobals->curtime ); + + 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 = gpGlobals->curtime + 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 = gpGlobals->curtime + 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 <= gpGlobals->curtime ) + { + 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 - gpGlobals->curtime, 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 - gpGlobals->curtime, 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()) : "<>"; + 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; + } +} + -- cgit v1.2.3