浏览代码

added basic entity attribute descriptors

Darren Ranalli 22 年之前
父节点
当前提交
d00c3a0980

+ 9 - 0
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')

+ 97 - 8
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(

+ 7 - 0
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)
         

+ 4 - 0
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,

+ 4 - 0
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)