diff --git a/direct/src/level/BasicEntities.py b/direct/src/level/BasicEntities.py index 4a9d1da87f..ef3e314121 100755 --- a/direct/src/level/BasicEntities.py +++ b/direct/src/level/BasicEntities.py @@ -9,6 +9,9 @@ class NodePathAttribs: """Derive from this class to give an entity the behavior of a NodePath, without necessarily deriving from NodePath. Derived class must implement getNodePath().""" + __attribs__ = ( + 'parent', 'pos', 'hpr', + ) def initNodePathAttribs(self, doReparent=1): """Call this after the entity has been initialized""" self.callSetters('pos','x','y','z', @@ -57,6 +60,9 @@ class NodePathEntity(Entity.Entity, privNodePathImpl): """This is an entity that represents a NodePath on the client. It may be instantiated directly or used as a base class for other entity types.""" + __attribs__ = ( + 'parent', 'pos', 'hpr', + ) def __init__(self, level, entId): privNodePathImpl.__init__(self, '') Entity.Entity.__init__(self, level, entId) @@ -75,6 +81,9 @@ class DistributedNodePathEntity(DistributedEntity.DistributedEntity, """This is a distributed version of NodePathEntity. It should not be instantiated directly; derive your client-side distEntity from this class instead of DistributedEntity.""" + __attribs__ = ( + 'parent', 'pos', 'hpr', + ) def __init__(self, cr): DistributedEntity.DistributedEntity.__init__(self, cr) privNodePathImpl.__init__(self, 'DistributedNodePathEntity') diff --git a/direct/src/level/Entity.py b/direct/src/level/Entity.py index ddb01b822e..6b20b0104d 100755 --- a/direct/src/level/Entity.py +++ b/direct/src/level/Entity.py @@ -10,14 +10,11 @@ class Entity(DirectObject): and can be edited with the LevelEditor.""" notify = DirectNotifyGlobal.directNotify.newCategory('Entity') - """ - # these are values that can be changed in the level editor - Attribs = ( - # Name, PythonType, CallSetterOnInitialization - ('name', str, 0), - ('comment', str, 0), + __attribs__ = ( + 'type', + 'name', + 'comment', ) - """ def __init__(self, level=None, entId=None): self.initializeEntity(level, entId) @@ -74,6 +71,99 @@ class Entity(DirectObject): self.__dict__[attrib] = value if __debug__: + def getAttribDescriptors(self): + # lazy compilation + if not self.__class__.__dict__.has_key('_attribDescs'): + self.compileAttribDescs() + return self.__class__.__dict__['_attribDescs'] + + def compileAttribDescs(self): + Entity.notify.debug('compiling attrib descriptors for %s' % + self.__class__.__name__) + # create a complete list of attribute descriptors, pulling in + # the attribs from the entire class heirarchy + def getClassList(obj, self=self): + """returns list, ordered from most-derived to base classes, + depth-first. Multiple inheritance base classes that do not + derive from Entity are listed before those that do. + """ + assert (type(obj) == types.ClassType) + classList = [obj] + + # no need to go below Entity + if obj == Entity: + return classList + + # explore the base classes + entityBases = [] + nonEntityBases = [] + for base in obj.__bases__: + l = getClassList(base) + if Entity in l: + entityBases.extend(l) + else: + nonEntityBases.extend(l) + # put bases that derive from Entity last + classList = classList + nonEntityBases + entityBases + return classList + + def getUniqueClassList(obj, self=self): + classList = getClassList(obj) + # remove duplicates, leaving the last instance + uniqueList = [] + for i in range(len(classList)): + if classList[i] not in classList[(i+1):]: + uniqueList.append(classList[i]) + return uniqueList + + classList = getUniqueClassList(self.__class__) + + # work backwards, through the class list, from Entity to the + # most-derived class, aggregating attribute descriptors. + allAttribs = [] + + def isDistObjAI(obj): + # util func: is this class a DistributedObjectAI? + lineage = getClassLineage(obj) + for item in lineage: + if type(item) == types.ClassType: + if item.__name__ == 'DistributedObjectAI': + return 1 + return 0 + + while len(classList): + cl = classList.pop() + Entity.notify.debug('looking for attribs for %s' % cl.__name__) + if cl.__dict__.has_key('__attribs__'): + cAttribs = cl.__attribs__ + elif isDistObjAI(cl): + # It's a distributed AI class. Check the client-side class + globals = {} + locals = {} + ccn = cl.__name__[:-2] # clientClassName + Entity.notify.debug('importing client class %s' % ccn) + try: + exec 'import %s' % ccn in globals, locals + except: + print 'could not import %s' % ccn + continue + exec 'cAttribs = %s.%s.__dict__.get("__attribs__")' % ( + ccn, ccn) in globals, locals + cAttribs = locals['cAttribs'] + if cAttribs is None: + continue + else: + continue + + for attrib in cAttribs: + if attrib not in allAttribs: + allAttribs.append(attrib) + + # we now have an ordered list of all of the attribute descriptors + # for this class. Cache it on the class object + Entity.notify.debug('all attribs: %s' % allAttribs) + self.__class__.__dict__['_attribDescs'] = allAttribs + # support for level editing def handleAttribChange(self, attrib, value): # call callback function if it exists @@ -102,7 +192,6 @@ class Entity(DirectObject): return self.attribs """ - if __debug__: def debugPrint(self, message): """for debugging""" return self.notify.debug( diff --git a/direct/src/level/LevelMgr.py b/direct/src/level/LevelMgr.py index fff39eeb1f..259928e1ea 100755 --- a/direct/src/level/LevelMgr.py +++ b/direct/src/level/LevelMgr.py @@ -5,6 +5,13 @@ import LevelMgrBase class LevelMgr(LevelMgrBase.LevelMgrBase): """This class manages editable client-side level attributes""" + + __attribs__ = ( + 'cogLevel', + 'cogTrack', + 'modelFilename', + ) + def __init__(self, level, entId): LevelMgrBase.LevelMgrBase.__init__(self, level, entId) diff --git a/direct/src/level/LogicGateAI.py b/direct/src/level/LogicGateAI.py index 761e7d68f3..b4c2fc8e2a 100755 --- a/direct/src/level/LogicGateAI.py +++ b/direct/src/level/LogicGateAI.py @@ -69,6 +69,10 @@ class LogicGateAI(Entity.Entity, PandaObject.PandaObject): if __debug__: notify = DirectNotifyGlobal.directNotify.newCategory( 'LogicGateAI') + __attribs__ = ( + 'input_input1_bool', 'input_input2_bool', + 'isInput1', 'isInput2', 'logicType', 'output', + ) logicTests={ "and": andTest, "or": orTest, diff --git a/direct/src/level/ZoneEntity.py b/direct/src/level/ZoneEntity.py index ca13636032..6f658f5ed3 100755 --- a/direct/src/level/ZoneEntity.py +++ b/direct/src/level/ZoneEntity.py @@ -4,6 +4,10 @@ import ZoneEntityBase import BasicEntities class ZoneEntity(ZoneEntityBase.ZoneEntityBase, BasicEntities.NodePathAttribs): + __attribs = ( + 'modelZoneNum', + ) + def __init__(self, level, entId): ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId)