Game Development Community

dev|Pro Game Development Curriculum

Duplicate Objects in Script

by Brett Fattori · 10/19/2004 (6:55 pm) · 8 comments

SimObject allows you to clone itself, but only if the classes of the two objects match. This small functional update will allow you to clone one object into another, regardless of class.

MAKE SURE YOU BACK YOUR CODE UP!

In console/simBase.cc add the following right after the ConsoleMethod dump():
// BF :: Copy all fields from an object and write to a
//       stream for passing to script
ConsoleMethod(SimObject,getAllFields, const char*, 2, 2, "obj.getAllFields()")
{
   argc; argv;

   U8 streamBuf[2048];
   MemStream stream(2048, streamBuf, false, true);

   object->writeFields(stream, 1);

   // Dump stream to script return buf
   char *retBuf = (char *)streamBuf;
   U32 returnLeng = stream.getPosition();
   retBuf[returnLeng] = '[[6288b07b40cae]]';

   return (const char *)retBuf;
}

Now, from within script, you can make a copy of an object like so:
// Create the object string
       %fooObj = "new " @ %barObj.getClassName() @ "(foo) {" @ %barObj.getAllFields() @ "};";

       // Build the object
       %fooObj = "%fooObj = " @ %fooObj;
       eval( %fooObj );

When the script runs, you will be left with an exact duplicate of the barObj, but it is an entirely new object referred to as %fooObj. What's more, if you added it to the MissionGroup simGroup, with:
%missionGrp = nameToId("MissionGroup");
   %missionGrp.add(%fooObj);
You could then look it up as:
%fooObj = nameToId("MissionGroup/foo");

Enjoy!

- Brett

#1
10/19/2004 (7:18 pm)
I've made it another way, maybe someone find it's useful too:

Add to simBase.cc
SimObject* SimObject::duplicate(SimGroup *parentGroup)
{
   ConsoleObject *obj = ConsoleObject::create(this->getClassName());
   SimObject *newObject = dynamic_cast<SimObject *>(obj);
   if (!newObject)
      return NULL;

   newObject->assignFieldsFrom(this);
   newObject->setModStaticFields(true);
   newObject->setModDynamicFields(true);
   
   if (newObject->isProperlyAdded() == false && !newObject->registerObject())
   {
      delete newObject;
      return NULL;
   }

   const char *name = getName();
   if(name && name[0] && getClassRep())
   {
      Namespace *parent = getClassRep()->getNameSpace();
      if(Con::linkNamespaces(parent->mName, name))
         newObject->mNameSpace = Con::lookupNamespace(name);
   }

   if (!parentGroup)
      parentGroup = this->getGroup();

   parentGroup->addObject(newObject);
   return newObject;
}

ConsoleMethod(SimObject, duplicate, S32, 2, 2, "obj.duplicate()")
{
   argc; argv;
   SimObject *newObj = object->duplicate();

   if (newObj)
      return newObj->getId();

   return -1;
}

SimObject* SimSet::duplicate(SimGroup *parentGroup)
{
   SimSet *newObject = dynamic_cast<SimSet*>(Parent::duplicate(parentGroup));
   if (!newObject)
      return NULL;

   // Duplicate childs
   for(U32 i = 0; i < size(); i++)
      (*this)[i]->duplicate((SimGroup*)newObject);

   return (SimObject*)newObject;
}

Insert at simBase.h
~@ line 709
   virtual void write(Stream &stream, U32 tabStop, U32 flags = 0);

   [b]// Create an copy of the object (like saving to file and executing it after)
   virtual SimObject* duplicate(SimGroup *parentGroup = NULL);[/b]

   ~@ line 1077
   void write(Stream &stream, U32 tabStop, U32 flags = 0);
   [b]SimObject* duplicate(SimGroup *parentGroup = NULL);[/b]

Then in the script:
%source = new Foo();
%copy = %source.duplicate();

It's a recursive function, so SimSets and SimGroups can be duplicated too (all the object inside it also), it works by creating a new object from the same class, copying all the fields (static and dynamic), linking the namespace and adding to the same parent group.
#2
10/19/2004 (10:55 pm)
Marcelo's approach is probably a bit more portable - very cool to see two different ways of doing the same thing, though.
#3
10/20/2004 (5:09 am)
Sure thing... That's the benefit of the community. Just surprised this never showed up before now.

I guess sometimes it takes someone raising their hand and speaking up for others to speak up too. Way to rain on my parade Marcelo! ;-)

- Brett

EDIT: My approach allows for someone to duplicate an object that has no similarities. Thus you could copy all of the fields from a player object into an item object. Or, as I'm using it, from ScriptObject's into an actual instance of another object. Maybe a blending of the two approaches would be best.
#4
10/20/2004 (6:33 am)
Hehe, I have so many modifications in the engine that I don't have time to post resources of it. :)

Maybe you can make SimObject::assignFieldsFrom acessible by script, this way you can copy the fields without using the language parser.
#5
10/20/2004 (9:25 am)
I thought of using that assignFieldsFrom, but it meant that I couldn't duplicate an object unless it was the same class of object. With the method above, I can have a collection of ScriptObjects and then copy all their fields into Markers in the mission group. Thus there's no limitation on similarity in classes. It was just a design decision.

- Brett
#6
08/17/2005 (1:28 am)
brett:

Thank you for posting this, my eyes were bleeding trying to get some values into an Item from script without using "datablocks".

you might mention that this needs to be added at the top of simBase.cc:

#include "core/memstream.h"

also, if you want to copy a simobject to an Item, you will need to create a datablock and use this:

datablock itemdata(simObjData){
category = "yourCategory";
};

%itemObj = "new Item(){datablock = simObjData;"@%simObj.getAllFields()@"\n};";
#7
09/06/2005 (8:41 am)
I used Marcelo's approach, and it compiled and ran well. However, when I try to duplicate an InteriorInstance, the duplicated interior does appear in the world, but it is all black. I'm guessing it needs to be relighted or something, but that would be expensive. Is there a way that the duplicated instance can share the lightmap from the object which it was copied from?
#8
11/18/2005 (6:53 am)
Hi,

I've tried to implement both suggestions, but get this error when the camera is moved in the direction of copied object:

Fatal:(c:\torquw\sdk\engine\interior\interior.cc @ 2078) Bad texture handle in rebuildVertexColors

Does anyone know how to fix this?

Thanks in advance :)
/Immdtu