From 14efabc778da75ad8f8750926ecd7fcb0e322fe7 Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Fri, 17 Oct 2003 22:40:23 +0000 Subject: [PATCH] abstract entity types can have typenames, exposed inheritance and output-type information through entityTypeReg --- direct/src/level/AttribDesc.py | 5 +- direct/src/level/EntityTypeDesc.py | 112 +++++++++++++++++ direct/src/level/EntityTypeRegistry.py | 160 +++++++++---------------- direct/src/level/EntityTypes.py | 61 +++++----- direct/src/level/LevelSpec.py | 8 +- 5 files changed, 212 insertions(+), 134 deletions(-) create mode 100755 direct/src/level/EntityTypeDesc.py diff --git a/direct/src/level/AttribDesc.py b/direct/src/level/AttribDesc.py index 6209632659..59adef9e8c 100755 --- a/direct/src/level/AttribDesc.py +++ b/direct/src/level/AttribDesc.py @@ -24,8 +24,9 @@ class AttribDesc: def __str__(self): return self.name def __repr__(self): - return "AttribDesc(%s, %s, %s)" % ( + return "AttribDesc(%s, %s, %s, %s)" % ( repr(self.name), repr(self.default), - repr(self.datatype) + repr(self.datatype), + repr(self.params), ) diff --git a/direct/src/level/EntityTypeDesc.py b/direct/src/level/EntityTypeDesc.py new file mode 100755 index 0000000000..5772ee2590 --- /dev/null +++ b/direct/src/level/EntityTypeDesc.py @@ -0,0 +1,112 @@ +"""EntityTypeDesc module: contains the EntityTypeDesc class""" + +import DirectNotifyGlobal +import AttribDesc +from PythonUtil import mostDerivedLast + +class EntityTypeDesc: + """This class is meta-data that describes an Entity type.""" + notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeDesc') + + output = None + + def __init__(self): + self.__class__.privCompileAttribDescs(self.__class__) + + self.attribNames = [] + self.attribDescDict = {} + + # ordered list of attrib descriptors + attribDescs = self.__class__._attribDescs + + # create ordered list of attrib names, dict of attrib name to attribDesc + for desc in attribDescs: + attribName = desc.getName() + self.attribNames.append(attribName) + self.attribDescDict[attribName] = desc + + def isConcrete(self): + return not self.__class__.__dict__.has_key('abstract') + + def getOutputType(self): + return self.output + + def getAttribNames(self): + """ returns ordered list of attribute names for this entity type """ + return self.attribNames + + def getAttribDescDict(self): + """ returns dict of attribName -> attribDescriptor """ + return self.attribDescDict + + def privCompileAttribDescs(entTypeClass): + """this compiles an ordered list of attribDescs for the Entity class + passed in. The attribute descriptors describe the properties of each + of the Entity type's attributes""" + # has someone already compiled the info? + if entTypeClass.__dict__.has_key('_attribDescs'): + return + + c = entTypeClass + EntityTypeDesc.notify.debug('compiling attrib descriptors for %s' % + c.__name__) + + # make sure all of our base classes have their complete list of + # attribDescs + for base in c.__bases__: + EntityTypeDesc.privCompileAttribDescs(base) + + # aggregate the attribute descriptors from our direct base classes + delAttribs = c.__dict__.get('delAttribs', []) + baseADs = [] + + bases = list(c.__bases__) + # make sure base-class attribs show up before derived-class attribs + mostDerivedLast(bases) + + for base in bases: + for desc in base._attribDescs: + # are we blocking this attribute? + if desc.getName() in delAttribs: + continue + + # make sure we haven't already picked up this attribute + # from an earlier base class + for d in baseADs: + if desc.getName() == d.getName(): + EntityTypeDesc.notify.warning( + '%s inherits attrib %s from multiple bases' % + (c.__name__, desc.getName())) + break + else: + baseADs.append(desc) + + # now that we have all of the descriptors from our base classes, + # add the descriptors from this class + attribDescs = [] + if c.__dict__.has_key('attribs'): + for attrib in c.attribs: + desc = AttribDesc.AttribDesc(*attrib) + + # if we picked up an attribute with the same name from a base + # class, this overrides it + for ad in baseADs: + if ad.getName() == desc.getName(): + baseADs.remove(ad) + # there ought to be no more than one desc with + # this name from the base classes + assert ad not in baseADs + break + + attribDescs.append(desc) + + c._attribDescs = baseADs + attribDescs + privCompileAttribDescs = staticmethod(privCompileAttribDescs) + + def __str__(self): + return str(self.__class__) + def __repr__(self): + # this is used to produce a hash value + return (str(self.__class__.__dict__.get('type',None))+ + str(self.output)+ + str(self.attribDescDict)) diff --git a/direct/src/level/EntityTypeRegistry.py b/direct/src/level/EntityTypeRegistry.py index 2dcf63cb40..f4067b9f0b 100755 --- a/direct/src/level/EntityTypeRegistry.py +++ b/direct/src/level/EntityTypeRegistry.py @@ -3,124 +3,82 @@ import DirectNotifyGlobal import types import AttribDesc -import EntityTypes +import EntityTypeDesc from PythonUtil import mostDerivedLast class EntityTypeRegistry: notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeRegistry') - def __init__(self, entityTypeModule=EntityTypes): - """pass in a module that contains EntityType classes""" - # get a list of the entity type classes in the type module + def __init__(self, entityTypeModule): + """pass in a module that contains EntityTypeDesc classes""" + # get a list of the EntityTypeDesc classes in the type module classes = [] for key, value in entityTypeModule.__dict__.items(): if type(value) is types.ClassType: - if issubclass(value, EntityTypes.Entity): + if issubclass(value, EntityTypeDesc.EntityTypeDesc): classes.append(value) - # put derived classes after their bases + self.entTypeName2typeDesc = {} + + # create an instance of each EntityType class with a typename + # make sure that derived classes come after bases mostDerivedLast(classes) - - # scrub through the class heirarchy and compile complete - # attribute descriptor lists for each concrete Entity type class - typeName2class = {} for c in classes: - # if this is a concrete Entity type, add it to the dict if c.__dict__.has_key('type'): - if typeName2class.has_key(c.type): - EntityTypeRegistry.notify.debug( - "replacing %s with %s for type '%s'" % - (typeName2class[c.type], c, c.type)) - typeName2class[c.type] = c + if self.entTypeName2typeDesc.has_key(c.type): + # a more-derived class is replacing a less-derived class + # to implement a particular entity type + EntityTypeRegistry.notify.info( + "replacing %s with %s for entity type '%s'" % + (self.entTypeName2typeDesc[c.type].__class__, + c, c.type)) + self.entTypeName2typeDesc[c.type] = c() - self.privCompileAttribDescs(c) + # create mapping of entity output types to list of concrete entity + # typenames with that output type + self.output2typeNames = {} + for typename, typeDesc in self.entTypeName2typeDesc.items(): + if typeDesc.isConcrete(): + if hasattr(typeDesc, 'output'): + outputType = typeDesc.output + self.output2typeNames.setdefault(outputType, []) + self.output2typeNames[outputType].append(typename) - # maps entity typename to ordered list of attrib names - self.typeName2attribNames = {} - # maps entity typename to dict of attrib name -> attrib descriptor - self.typeName2attribDescDict = {} - - for typeName, c in typeName2class.items(): - self.typeName2attribNames[typeName] = [] - self.typeName2attribDescDict[typeName] = {} + # create mapping of entity typename (abstract or concrete) to list + # of entity typenames are concrete and are of that type or derive + # from that type + self.typeName2derivedTypeNames = {} + for typename, typeDesc in self.entTypeName2typeDesc.items(): + typenames = [] + for tn, td in self.entTypeName2typeDesc.items(): + if td.isConcrete(): + if issubclass(td.__class__, typeDesc.__class__): + typenames.append(tn) + self.typeName2derivedTypeNames[typename] = typenames - # ordered list of attrib descriptors - attribDescs = c._attribDescs + def getTypeDesc(self, entTypeName): + """returns EntityTypeDesc instance for concrete Entity type""" + assert entTypeName in self.entTypeName2typeDesc,\ + "unknown entity type '%s'" % entTypeName + # the table has descriptors for abstract entity types, but I don't + # think there's any need for anyone outside this class to access them + assert self.entTypeName2typeDesc[entTypeName].isConcrete(),\ + "entity type '%s' is abstract" % entTypeName + return self.entTypeName2typeDesc[entTypeName] - for desc in attribDescs: - attribName = desc.getName() - self.typeName2attribNames[typeName].append(attribName) - self.typeName2attribDescDict[typeName][attribName] = desc + def getTypeNamesFromOutputType(self, outputType): + """return Entity typenames for Entity types with particular output""" + return self.output2typeNames.get(outputType, []) - def getAttribNames(self, entityTypeName): - """ returns ordered list of attribute names for entity type """ - assert entityTypeName in self.typeName2attribNames - return self.typeName2attribNames[entityTypeName] - - def getAttribDescDict(self, entityTypeName): - """ returns dict of attribName -> attribDescriptor """ - assert entityTypeName in self.typeName2attribDescDict - return self.typeName2attribDescDict[entityTypeName] + def getDerivedTypeNames(self, entTypeName): + """return Entity typenames that are of or derive from an entity type, + which may be concrete or abstract""" + assert entTypeName in self.typeName2derivedTypeNames,\ + "unknown entity type '%s'" % entTypeName + return self.typeName2derivedTypeNames[entTypeName] def __hash__(self): - return hash(str(self)) - def __str__(self): - return str(self.typeName2attribNames)+str(self.typeName2attribDescDict) - - def privCompileAttribDescs(self, entTypeClass): - # has someone already compiled the info? - if entTypeClass.__dict__.has_key('_attribDescs'): - return - - c = entTypeClass - EntityTypeRegistry.notify.debug('compiling attrib descriptors for %s' % - c.__name__) - - # make sure all of our base classes have their complete list of - # attribDescs - for base in c.__bases__: - self.privCompileAttribDescs(base) - - # aggregate the attribute descriptors from our direct base classes - delAttribs = c.__dict__.get('delAttribs', []) - baseADs = [] - - bases = list(c.__bases__) - # make sure that Entity comes first - if EntityTypes.Entity in bases: - bases.remove(EntityTypes.Entity) - bases = [EntityTypes.Entity] + bases - - for base in bases: - for desc in base._attribDescs: - # are we blocking this attribute? - if desc.getName() in delAttribs: - continue - - # make sure we haven't already picked up this attribute - # from an earlier base class - for d in baseADs: - if desc.getName() == d.getName(): - break - else: - baseADs.append(desc) - - # now that we have all of the descriptors from our base classes, - # add the descriptors from this class - attribDescs = [] - if c.__dict__.has_key('attribs'): - for attrib in c.attribs: - desc = AttribDesc.AttribDesc(*attrib) - - # if we picked up an attribute with the same name from a base - # class, this overrides it - for ad in baseADs: - if ad.getName() == desc.getName(): - baseADs.remove(ad) - # there ought to be no more than one desc with - # this name from the base classes - break - - attribDescs.append(desc) - - c._attribDescs = baseADs + attribDescs + return hash(repr(self)) + def __repr__(self): + # this is used to produce a hash value + return str(self.entTypeName2typeDesc) diff --git a/direct/src/level/EntityTypes.py b/direct/src/level/EntityTypes.py index f4abf4e412..35b074c1d9 100755 --- a/direct/src/level/EntityTypes.py +++ b/direct/src/level/EntityTypes.py @@ -1,28 +1,17 @@ """EntityTypes module: contains classes that describe Entity types""" +from EntityTypeDesc import EntityTypeDesc from SpecImports import * -class Entity: +class Entity(EntityTypeDesc): + abstract = 1 + type = 'entity' attribs = ( ('type', None), ('name', 'unnamed'), ('comment', ''), ) -class ActiveCell(Entity): - type = 'activeCell' - attribs = ( - ('row', 0, 'int'), - ('col', 0, 'int'), - ('gridId', None, 'entId', {'type':'grid'}) - ) - -class DirectionalCell(ActiveCell): - type = 'directionalCell' - attribs = ( - ('dir', [0,0], 'choice', {'choiceSet':['l','r','up','dn']}), - ) - class LevelMgr(Entity): type = 'levelMgr' attribs = ( @@ -60,7 +49,7 @@ class Nodepath(Entity): ('hpr', Vec3(0,0,0), 'hpr'), ) -class Zone(Entity, Nodepath): +class Zone(Nodepath): type = 'zone' delAttribs = ( 'parent', @@ -86,6 +75,7 @@ class CutScene(Entity): ) class BarrelBase(Nodepath): + abstract = 1 delAttribs = ( 'hpr', ) @@ -103,7 +93,8 @@ class GagBarrel(BarrelBase): ('gagTrack', 0, 'choice', {'choiceSet':range(7)}), ) -class Switch(Entity, Nodepath): +class Switch(Nodepath): + abstract = 1 output = 'bool' attribs = ( ('scale', Vec3(1), 'scale'), @@ -131,17 +122,6 @@ class ConveyorBelt(Nodepath): ('floorName', 'platformcollision'), ) -class Crate(Nodepath): - type = 'crate' - delAttribs = ( - 'hpr', - ) - attribs = ( - ('scale', Vec3(1), 'scale'), - ('gridId', None, 'entId', {'type':'grid'}), - ('pushable', 1, 'bool'), - ) - class Door(Entity): type = 'door' output = 'bool' @@ -177,6 +157,31 @@ class Grid(Nodepath): ('numRow', 3, 'int'), ) +class Crate(Nodepath): + type = 'crate' + delAttribs = ( + 'hpr', + ) + attribs = ( + ('scale', Vec3(1), 'scale'), + ('gridId', None, 'entId', {'type':'grid'}), + ('pushable', 1, 'bool'), + ) + +class ActiveCell(Entity): + type = 'activeCell' + attribs = ( + ('row', 0, 'int'), + ('col', 0, 'int'), + ('gridId', None, 'entId', {'type':'grid'}) + ) + +class DirectionalCell(ActiveCell): + type = 'directionalCell' + attribs = ( + ('dir', [0,0], 'choice', {'choiceSet':['l','r','up','dn']}), + ) + class Lift(Nodepath): type = 'lift' attribs = ( diff --git a/direct/src/level/LevelSpec.py b/direct/src/level/LevelSpec.py index 87baf61ff2..5f51a732a0 100755 --- a/direct/src/level/LevelSpec.py +++ b/direct/src/level/LevelSpec.py @@ -116,7 +116,8 @@ class LevelSpec: # create a new entity spec entry w/ default values globalEnts[entId] = {} spec = globalEnts[entId] - attribDescs = self.entTypeReg.getAttribDescDict(entType) + attribDescs = self.entTypeReg.getTypeDesc(entType + ).getAttribDescDict() for name, desc in attribDescs.items(): spec[name] = desc.getDefaultValue() spec['type'] = entType @@ -323,8 +324,9 @@ class LevelSpec: assert spec.has_key('type') entType = spec['type'] - attribNames = self.entTypeReg.getAttribNames(entType) - attribDescs = self.entTypeReg.getAttribDescDict(entType) + typeDesc = self.entTypeReg.getTypeDesc(entType) + attribNames = typeDesc.getAttribNames() + attribDescs = typeDesc.getAttribDescDict() # are there any unknown attribs in the spec? for attrib in spec.keys():