//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/simSet.h"
#include "core/stringTable.h"
#include "console/console.h"
#include "console/engineAPI.h"
#include "core/stream/fileStream.h"
#include "sim/actionMap.h"
#include "core/fileObject.h"
#include "console/consoleInternal.h"
#include "console/engineAPI.h"
#include "platform/profiler.h"
#include "console/typeValidators.h"
#include "core/frameAllocator.h"
#include "math/mMathFn.h"
IMPLEMENT_CONOBJECT_CHILDREN( SimSet );
IMPLEMENT_CONOBJECT( SimGroup );
ConsoleDocClass( SimSet,
"@brief A collection of SimObjects.\n\n"
"It is often necessary to keep track of an arbitrary set of SimObjects. "
"For instance, Torque's networking code needs to not only keep track of "
"the set of objects which need to be ghosted, but also the set of objects "
"which must always be ghosted. It does this by working with two "
"sets. The first of these is the RootGroup (which is actually a SimGroup) "
"and the second is the GhostAlwaysSet, which contains objects which must "
"always be ghosted to the client.\n\n"
"Some general notes on SimSets:\n\n"
"- Membership is not exclusive. A SimObject may be a member of multiple "
"SimSets.\n\n"
"- A SimSet does not destroy subobjects when it is destroyed.\n\n"
"- A SimSet may hold an arbitrary number of objects.\n\n"
"@ingroup Console\n"
"@ingroup Scripting"
);
ConsoleDocClass( SimGroup,
"@brief A collection of SimObjects that are owned by the group.\n\n"
"A SimGroup is a stricter form of SimSet. SimObjects may only be a member "
"of a single SimGroup at a time. The SimGroup will automatically enforce "
"the single-group-membership rule (ie. adding an object to a SimGroup will "
"cause it to be removed from its current SimGroup, if any).\n\n"
"Deleting a SimGroup will also delete all SimObjects in the SimGroup.\n\n"
"@tsexample\n"
"// Create a SimGroup for particle emitters\n"
"new SimGroup(Emitters)\n"
"{\n"
" canSaveDynamicFields = \"1\";\n\n"
" new ParticleEmitterNode(CrystalEmmiter) {\n"
" active = \"1\";\n"
" emitter = \"dustEmitter\";\n"
" velocity = \"1\";\n"
" dataBlock = \"GenericSmokeEmitterNode\";\n"
" position = \"-61.6276 2.1142 4.45027\";\n"
" rotation = \"1 0 0 0\";\n"
" scale = \"1 1 1\";\n"
" canSaveDynamicFields = \"1\";\n"
" };\n\n"
" new ParticleEmitterNode(Steam1) {\n"
" active = \"1\";\n"
" emitter = \"SlowSteamEmitter\";\n"
" velocity = \"1\";\n"
" dataBlock = \"GenericSmokeEmitterNode\";\n"
" position = \"-25.0458 1.55289 2.51308\";\n"
" rotation = \"1 0 0 0\";\n"
" scale = \"1 1 1\";\n"
" canSaveDynamicFields = \"1\";\n"
" };\n"
"};\n\n"
"@endtsexample\n\n"
"@ingroup Console\n"
"@ingroup Scripting"
);
IMPLEMENT_CALLBACK( SimSet, onObjectAdded, void, ( SimObject* object ), ( object ),
"Called when an object is added to the set.\n"
"@param object The object that was added." );
IMPLEMENT_CALLBACK( SimSet, onObjectRemoved, void, ( SimObject* object ), ( object ),
"Called when an object is removed from the set.\n"
"@param object The object that was removed." );
//=============================================================================
// SimSet.
//=============================================================================
// MARK: ---- SimSet ----
//-----------------------------------------------------------------------------
SimSet::SimSet()
{
VECTOR_SET_ASSOCIATION(mObjectList);
mMutex = Mutex::createMutex();
}
//-----------------------------------------------------------------------------
SimSet::~SimSet()
{
Mutex::destroyMutex( mMutex );
}
//-----------------------------------------------------------------------------
void SimSet::addObject( SimObject* obj )
{
// Prevent SimSet being added to itself.
if( obj == this )
return;
lock();
const bool added = mObjectList.pushBack( obj );
if( added )
deleteNotify( obj );
unlock();
if( added )
{
getSetModificationSignal().trigger( SetObjectAdded, this, obj );
if( obj->isProperlyAdded() )
onObjectAdded_callback( obj );
}
}
//-----------------------------------------------------------------------------
void SimSet::removeObject( SimObject* obj )
{
lock();
const bool removed = mObjectList.remove( obj );
if( removed )
clearNotify( obj );
unlock();
if( removed )
{
getSetModificationSignal().trigger( SetObjectRemoved, this, obj );
if( obj->isProperlyAdded() )
onObjectRemoved_callback( obj );
}
}
//-----------------------------------------------------------------------------
void SimSet::pushObject( SimObject* obj )
{
if( obj == this )
return;
lock();
bool added = mObjectList.pushBackForce( obj );
if( added )
deleteNotify( obj );
unlock();
if( added )
{
getSetModificationSignal().trigger( SetObjectAdded, this, obj );
if( obj->isProperlyAdded() )
onObjectAdded_callback( obj );
}
}
//-----------------------------------------------------------------------------
void SimSet::popObject()
{
if(mObjectList.empty() )
{
AssertWarn(false, "Stack underflow in SimSet::popObject");
return;
}
lock();
SimObject* object = mObjectList.last();
mObjectList.pop_back();
clearNotify( object );
unlock();
getSetModificationSignal().trigger( SetObjectRemoved, this, object );
if( object->isProperlyAdded() )
onObjectRemoved_callback( object );
}
//-----------------------------------------------------------------------------
void SimSet::scriptSort( const String &scriptCallbackFn )
{
lock();
mObjectList.scriptSort( scriptCallbackFn );
unlock();
}
//-----------------------------------------------------------------------------
void SimSet::callOnChildren( const String &method, S32 argc, ConsoleValue argv[], bool executeOnChildGroups )
{
// Prep the arguments for the console exec...
// Make sure and leave args[1] empty.
ConsoleValue args[21] = { };
args[0].setString(method.c_str());
for (S32 i = 0; i < argc; i++)
args[i + 2].setString(argv[i].getString());
for( iterator i = begin(); i != end(); i++ )
{
SimObject *childObj = static_cast(*i);
if( childObj->isMethod( method.c_str() ) )
Con::execute(childObj, argc + 2, args);
if( executeOnChildGroups )
{
SimSet* childSet = dynamic_cast(*i);
if ( childSet )
childSet->callOnChildren( method, argc, argv, executeOnChildGroups );
}
}
}
//-----------------------------------------------------------------------------
U32 SimSet::sizeRecursive()
{
U32 count = 0;
for ( iterator i = begin(); i != end(); i++ )
{
count++;
SimSet* childSet = dynamic_cast(*i);
if ( childSet )
count += childSet->sizeRecursive();
}
return count;
}
//-----------------------------------------------------------------------------
bool SimSet::reOrder( SimObject *obj, SimObject *target )
{
MutexHandle handle;
handle.lock(mMutex);
iterator itrS, itrD;
if ( (itrS = find(begin(),end(),obj)) == end() )
{
// object must be in list
return false;
}
if ( obj == target )
{
// don't reorder same object but don't indicate error
return true;
}
if ( !target )
{
// if no target, then put to back of list
// don't move if already last object
if ( itrS != (end()-1) )
{
// remove object from its current location and push to back of list
mObjectList.erase(itrS);
mObjectList.push_back(obj);
}
}
else
{
// if target, insert object in front of target
if ( (itrD = find(begin(),end(),target)) == end() )
// target must be in list
return false;
mObjectList.erase(itrS);
// once itrS has been erased, itrD won't be pointing at the
// same place anymore - re-find...
itrD = find(begin(),end(),target);
mObjectList.insert(itrD, obj);
}
return true;
}
//-----------------------------------------------------------------------------
void SimSet::onDeleteNotify(SimObject *object)
{
removeObject(object);
Parent::onDeleteNotify(object);
}
//-----------------------------------------------------------------------------
void SimSet::onRemove()
{
MutexHandle handle;
handle.lock( mMutex );
if( !mObjectList.empty() )
{
mObjectList.sortId();
// This backwards iterator loop doesn't work if the
// list is empty, check the size first.
for( SimObjectList::iterator ptr = mObjectList.end() - 1;
ptr >= mObjectList.begin(); ptr -- )
clearNotify( *ptr );
}
handle.unlock();
Parent::onRemove();
}
//-----------------------------------------------------------------------------
void SimSet::write(Stream &stream, U32 tabStop, U32 flags)
{
MutexHandle handle;
handle.lock(mMutex);
// export selected only?
if((flags & SelectedOnly) && !isSelected())
{
for(U32 i = 0; i < size(); i++)
(*this)[i]->write(stream, tabStop, flags);
return;
}
stream.writeTabs( tabStop );
char buffer[ 2048 ];
const U32 bufferWriteLen = dSprintf( buffer, sizeof( buffer ), "new %s(%s) {\r\n", getClassName(), getName() && !( flags & NoName ) ? getName() : "" );
stream.write( bufferWriteLen, buffer );
writeFields( stream, tabStop + 1 );
if(size())
{
stream.write(2, "\r\n");
for(U32 i = 0; i < size(); i++)
{
SimObject* child = ( *this )[ i ];
if( child->getCanSave() )
child->write(stream, tabStop + 1, flags);
}
}
stream.writeTabs(tabStop);
stream.write(4, "};\r\n");
}
//-----------------------------------------------------------------------------
void SimSet::clear()
{
lock();
while( !empty() )
popObject();
unlock();
getSetModificationSignal().trigger( SetCleared, this, NULL );
}
//-----------------------------------------------------------------------------
//UNSAFE
void SimSet::deleteAllObjects()
{
lock();
while( !empty() )
{
SimObject* object = mObjectList.last();
mObjectList.pop_back();
object->deleteObject();
}
unlock();
}
//-----------------------------------------------------------------------------
SimObject* SimSet::findObject( SimObject* object )
{
bool found = false;
lock();
for( SimSet::iterator iter = begin(); iter != end(); ++ iter )
if( *iter == object )
{
found = true;
break;
}
unlock();
if( found )
return object;
return NULL;
}
//-----------------------------------------------------------------------------
SimObject* SimSet::findObject( const char *namePath )
{
// find the end of the object name
S32 len;
for(len = 0; namePath[len] != 0 && namePath[len] != '/'; len++)
;
StringTableEntry stName = StringTable->lookupn(namePath, len);
if(!stName)
return NULL;
lock();
for(SimSet::iterator i = begin(); i != end(); i++)
{
if((*i)->getName() == stName)
{
unlock();
if(namePath[len] == 0)
return *i;
return (*i)->findObject(namePath + len + 1);
}
}
unlock();
return NULL;
}
//-----------------------------------------------------------------------------
SimObject* SimSet::findObjectByInternalName(StringTableEntry internalName, bool searchChildren)
{
iterator i;
for (i = begin(); i != end(); i++)
{
SimObject *childObj = static_cast(*i);
if(childObj->getInternalName() == internalName)
return childObj;
else if (searchChildren)
{
SimSet* childSet = dynamic_cast(*i);
if (childSet)
{
SimObject* found = childSet->findObjectByInternalName(internalName, searchChildren);
if (found) return found;
}
}
}
return NULL;
}
//-----------------------------------------------------------------------------
SimObject* SimSet::findObjectByLineNumber(const char* fileName, S32 declarationLine, bool searchChildren)
{
if (!fileName)
return NULL;
if (declarationLine < 0)
return NULL;
StringTableEntry fileEntry = StringTable->insert(fileName);
for (iterator i = begin(); i != end(); i++)
{
SimObject *childObj = static_cast(*i);
if(childObj->getFilename() == fileEntry && childObj->getDeclarationLine() == declarationLine)
return childObj;
else if (searchChildren)
{
SimSet* childSet = dynamic_cast(*i);
if (childSet)
{
SimObject* found = childSet->findObjectByLineNumber(fileName, declarationLine, searchChildren);
if (found)
return found;
}
}
}
return NULL;
}
//-----------------------------------------------------------------------------
SimObject* SimSet::getRandom()
{
if (size() > 0)
return mObjectList[mRandI(0, size() - 1)];
return NULL;
}
//-----------------------------------------------------------------------------
SimSet* SimSet::clone()
{
// Clone the set object.
SimObject* object = Parent::clone();
SimSet* set = dynamic_cast< SimSet* >( object );
if( !set )
{
object->deleteObject();
return NULL;
}
// Add all object in the set.
for( iterator iter = begin(); iter != end(); ++ iter )
set->addObject( *iter );
return set;
}
//-----------------------------------------------------------------------------
inline void SimSetIterator::Stack::push_back(SimSet* set)
{
increment();
last().set = set;
last().itr = set->begin();
}
//-----------------------------------------------------------------------------
SimSetIterator::SimSetIterator(SimSet* set)
{
VECTOR_SET_ASSOCIATION(stack);
if (!set->empty())
stack.push_back(set);
}
//-----------------------------------------------------------------------------
SimObject* SimSetIterator::operator++()
{
SimSet* set;
if ((set = dynamic_cast(*stack.last().itr)) != 0)
{
if (!set->empty())
{
stack.push_back(set);
return *stack.last().itr;
}
}
while (++stack.last().itr == stack.last().set->end())
{
stack.pop_back();
if (stack.empty())
return 0;
}
return *stack.last().itr;
}
//=============================================================================
// SimGroup.
//=============================================================================
// MARK: ---- SimGroup ----
//-----------------------------------------------------------------------------
SimGroup::~SimGroup()
{
for( iterator itr = begin(); itr != end(); itr ++ )
mNameDictionary.remove(*itr);
}
//-----------------------------------------------------------------------------
void SimGroup::_addObject( SimObject* obj, bool forcePushBack )
{
// Make sure we aren't adding ourself. This isn't the most robust check
// but it should be good enough to prevent some self-foot-shooting.
if( obj == this )
{
Con::errorf( "SimGroup::addObject - (%d) can't add self!", getIdString() );
return;
}
if( obj->getGroup() == this )
return;
lock();
obj->incRefCount();
if( obj->getGroup() )
obj->getGroup()->removeObject( obj );
if( forcePushBack ? mObjectList.pushBack( obj ) : mObjectList.pushBackForce( obj ) )
{
mNameDictionary.insert( obj );
obj->mGroup = this;
obj->onGroupAdd();
getSetModificationSignal().trigger( SetObjectAdded, this, obj );
if( obj->isProperlyAdded() )
onObjectAdded_callback( obj );
}
else
obj->decRefCount();
unlock();
// SimObjects will automatically remove them from their group
// when deleted so we don't hook up a delete notification.
}
//-----------------------------------------------------------------------------
void SimGroup::addObject( SimObject* obj )
{
_addObject( obj );
}
//-----------------------------------------------------------------------------
void SimGroup::removeObject( SimObject* obj )
{
lock();
_removeObjectNoLock( obj );
unlock();
}
//-----------------------------------------------------------------------------
void SimGroup::_removeObjectNoLock( SimObject* obj )
{
if( obj->mGroup == this )
{
obj->onGroupRemove();
mNameDictionary.remove( obj );
mObjectList.remove( obj );
obj->mGroup = 0;
getSetModificationSignal().trigger( SetObjectRemoved, this, obj );
if( obj->isProperlyAdded() )
onObjectRemoved_callback( obj );
obj->decRefCount();
}
}
//-----------------------------------------------------------------------------
void SimGroup::pushObject( SimObject* object )
{
_addObject( object, true );
}
//-----------------------------------------------------------------------------
void SimGroup::popObject()
{
MutexHandle handle;
handle.lock( mMutex );
if(mObjectList.empty() )
{
AssertWarn( false, "SimGroup::popObject - Stack underflow" );
return;
}
SimObject* object = mObjectList.last();
mObjectList.pop_back();
object->onGroupRemove();
object->mGroup = NULL;
clearNotify( object );
mNameDictionary.remove( object );
getSetModificationSignal().trigger( SetObjectAdded, this, object );
if( object->isProperlyAdded() )
onObjectRemoved_callback( object );
object->decRefCount();
}
//-----------------------------------------------------------------------------
void SimGroup::onRemove()
{
lock();
if( !mObjectList.empty() )
{
mObjectList.sortId();
clear();
}
SimObject::onRemove();
unlock();
}
//-----------------------------------------------------------------------------
void SimGroup::clear()
{
lock();
while( size() > 0 )
{
SimObject* object = mObjectList.last();
object->onGroupRemove();
mObjectList.pop_back();
mNameDictionary.remove( object );
object->mGroup = 0;
getSetModificationSignal().trigger( SetObjectRemoved, this, object );
if( object->isProperlyAdded() )
onObjectRemoved_callback( object );
if( engineAPI::gUseConsoleInterop )
object->deleteObject();
else
object->decRefCount();
}
unlock();
getSetModificationSignal().trigger( SetCleared, this, NULL );
}
//-----------------------------------------------------------------------------
SimObject *SimGroup::findObject(const char *namePath)
{
// find the end of the object name
S32 len;
for(len = 0; namePath[len] != 0 && namePath[len] != '/'; len++)
;
StringTableEntry stName = StringTable->lookupn(namePath, len);
if(!stName)
return NULL;
SimObject *root = mNameDictionary.find( stName );
if( !root )
return NULL;
if(namePath[len] == 0)
return root;
return root->findObject(namePath + len + 1);
}
//-----------------------------------------------------------------------------
SimGroup* SimGroup::clone()
{
// Skip SimSet::clone since we do not want to steal the child objects
// from this group.
SimObject* object = SimObject::clone();
SimGroup* group = dynamic_cast< SimGroup* >( object );
if( !group )
{
object->deleteObject();
return NULL;
}
return group;
}
//-----------------------------------------------------------------------------
SimGroup* SimGroup::deepClone()
{
// Clone the group object.
SimObject* object = Parent::deepClone();
SimGroup* group = dynamic_cast< SimGroup* >( object );
if( !group )
{
object->deleteObject();
return NULL;
}
// Clone all child objects.
for( iterator iter = begin(); iter != end(); ++ iter )
group->addObject( ( *iter )->deepClone() );
return group;
}
//-----------------------------------------------------------------------------
bool SimGroup::processArguments(S32, ConsoleValue *argv)
{
return true;
}
SimObject* SimGroup::getObject(const S32& index)
{
if (index < 0 || index >= size())
{
Con::errorf("Set::getObject - index out of range.");
return NULL;
}
return (*this)[index];
}
//-----------------------------------------------------------------------------
SimObject* SimGroupIterator::operator++()
{
SimGroup* set;
if ((set = dynamic_cast(*stack.last().itr)) != 0)
{
if (!set->empty())
{
stack.push_back(set);
return *stack.last().itr;
}
}
while (++stack.last().itr == stack.last().set->end())
{
stack.pop_back();
if (stack.empty())
return 0;
}
return *stack.last().itr;
}
//=============================================================================
// API.
//=============================================================================
// MARK: ---- API ----
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, listObjects, void, (),,
"Dump a list of all objects contained in the set to the console." )
{
object->lock();
SimSet::iterator itr;
for(itr = object->begin(); itr != object->end(); itr++)
{
SimObject *obj = *itr;
bool isSet = dynamic_cast(obj) != 0;
const char *name = obj->getName();
if(name)
Con::printf(" %d,\"%s\": %s %s", obj->getId(), name,
obj->getClassName(), isSet ? "(g)":"");
else
Con::printf(" %d: %s %s", obj->getId(), obj->getClassName(),
isSet ? "(g)" : "");
}
object->unlock();
}
//-----------------------------------------------------------------------------
DefineEngineStringlyVariadicMethod( SimSet, add, void, 3, 0,
"( SimObject objects... ) Add the given objects to the set.\n"
"@param objects The objects to add to the set." )
{
for(S32 i = 2; i < argc; i++)
{
SimObject *obj = Sim::findObject( argv[ i ] );
if(obj)
object->addObject( obj );
else
Con::printf("Set::add: Object \"%s\" doesn't exist to add to %s", (const char*)argv[ i ], object->getName() );
}
}
//-----------------------------------------------------------------------------
DefineEngineStringlyVariadicMethod( SimSet, remove, void, 3, 0,
"( SimObject objects... ) Remove the given objects from the set.\n"
"@param objects The objects to remove from the set." )
{
for(S32 i = 2; i < argc; i++)
{
SimObject *obj = Sim::findObject(argv[i]);
object->lock();
if(obj && object->find(object->begin(),object->end(),obj) != object->end())
object->removeObject(obj);
else
Con::printf("Set::remove: Object \"%s\" does not exist in set %s", (const char*)argv[i], object->getName());
object->unlock();
}
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, clear, void, (),,
"Remove all objects from the set." )
{
object->clear();
}
//-----------------------------------------------------------------------------
//UNSAFE; don't want this in the new API
DefineEngineMethod( SimSet, deleteAllObjects, void, (), , "() Delete all objects in the set." )
{
object->deleteAllObjects();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, getRandom, SimObject*, (),,
"Return a random object from the set.\n"
"@return A randomly selected object from the set or -1 if the set is empty." )
{
return object->getRandom();
}
//-----------------------------------------------------------------------------
DefineEngineStringlyVariadicMethod( SimSet, callOnChildren, void, 3, 0,
"( string method, string args... ) Call a method on all objects contained in the set.\n\n"
"@param method The name of the method to call.\n"
"@param args The arguments to the method.\n\n"
"@note This method recurses into all SimSets that are children to the set.\n\n"
"@see callOnChildrenNoRecurse" )
{
object->callOnChildren( (const char*)argv[2], argc - 3, argv + 3 );
}
//-----------------------------------------------------------------------------
DefineEngineStringlyVariadicMethod( SimSet, callOnChildrenNoRecurse, void, 3, 0,
"( string method, string args... ) Call a method on all objects contained in the set.\n\n"
"@param method The name of the method to call.\n"
"@param args The arguments to the method.\n\n"
"@note This method does not recurse into child SimSets.\n\n"
"@see callOnChildren" )
{
object->callOnChildren( (const char*)argv[2], argc - 3, argv + 3, false );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, reorderChild, void, ( SimObject* child1, SimObject* child2 ),,
"Make sure child1 is ordered right before child2 in the set.\n"
"@param child1 The first child. The object must already be contained in the set.\n"
"@param child2 The second child. The object must already be contained in the set." )
{
SimObject* pObject = child1;
SimObject* pTarget = child2;
if(pObject && pTarget)
{
object->reOrder(pObject,pTarget);
}
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, getCount, S32, (),,
"Get the number of objects contained in the set.\n"
"@return The number of objects contained in the set." )
{
return object->size();
}
//-----------------------------------------------------------------------------
DEFINE_CALLIN( fnSimSet_getCountRecursive, getCountRecursive, SimSet, U32, ( SimSet* set ),,,
"Get the number of direct and indirect child objects contained in the set.\n"
"@return The number of objects contained in the set as well as in other sets contained directly or indirectly in the set." )
{
return set->sizeRecursive();
}
DefineEngineMethod( SimSet, getFullCount, S32, (), , "() Get the number of direct and indirect child objects contained in the set.\n"
"@return The number of objects contained in the set as well as in other sets contained directly or indirectly in the set." )
{
return object->sizeRecursive();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, getObject, SimObject*, ( U32 index ),,
"Get the object at the given index.\n"
"@param index The object index.\n"
"@return The object at the given index or -1 if index is out of range." )
{
if( index < 0 || index >= object->size() )
{
Con::errorf( "Set::getObject - index out of range." );
return NULL;
}
return ( *object )[ index ];
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, getObjectIndex, S32, ( SimObject* obj ),,
"Return the index of the given object in this set.\n"
"@param obj The object for which to return the index. Must be contained in the set.\n"
"@return The index of the object or -1 if the object is not contained in the set." )
{
if( !obj )
return -1;
object->lock();
S32 count = 0;
for( SimSet::iterator i = object->begin(); i != object->end(); i++)
{
if( *i == obj )
{
object->unlock();
return count;
}
++count;
}
object->unlock();
return -1;
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, isMember, bool, ( SimObject* obj ),,
"Test whether the given object belongs to the set.\n"
"@param obj The object.\n"
"@return True if the object is contained in the set; false otherwise." )
{
if( !obj )
return false;
return ( object->find( object->begin(), object->end(), obj ) != object->end() );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, findObjectByInternalName, SimObject*, ( const char* internalName, bool searchChildren ), ( false ),
"Find an object in the set by its internal name.\n"
"@param internalName The internal name of the object to look for.\n"
"@param searchChildren If true, SimSets contained in the set will be recursively searched for the object.\n"
"@return The object with the given internal name or 0 if no match was found.\n" )
{
StringTableEntry pcName = StringTable->insert( internalName );
return object->findObjectByInternalName( pcName, searchChildren );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, bringToFront, void, ( SimObject* obj ),,
"Make the given object the first object in the set.\n"
"@param obj The object to bring to the frontmost position. Must be contained in the set." )
{
if( obj )
object->bringObjectToFront( obj );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, pushToBack, void, ( SimObject* obj ),,
"Make the given object the last object in the set.\n"
"@param obj The object to bring to the last position. Must be contained in the set." )
{
if( obj )
object->pushObjectToBack( obj );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, sort, void, ( const char * callbackFunction ), , "( string callbackFunction ) Sort the objects in the set using the given comparison function.\n"
"@param callbackFunction Name of a function that takes two object arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal." )
{
object->scriptSort( callbackFunction );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( SimSet, acceptsAsChild, bool, ( SimObject* obj ),,
"Test whether the given object may be added to the set.\n"
"@param obj The object to test for potential membership.\n"
"@return True if the object may be added to the set, false otherwise." )
{
if( !obj )
return false;
return object->acceptsAsChild( obj );
}