abstract entity types can have typenames, exposed inheritance and output-type information through entityTypeReg

This commit is contained in:
Darren Ranalli 2003-10-17 22:40:23 +00:00
parent 61008f86b7
commit 14efabc778
5 changed files with 212 additions and 134 deletions

View File

@ -24,8 +24,9 @@ class AttribDesc:
def __str__(self): def __str__(self):
return self.name return self.name
def __repr__(self): def __repr__(self):
return "AttribDesc(%s, %s, %s)" % ( return "AttribDesc(%s, %s, %s, %s)" % (
repr(self.name), repr(self.name),
repr(self.default), repr(self.default),
repr(self.datatype) repr(self.datatype),
repr(self.params),
) )

View File

@ -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))

View File

@ -3,124 +3,82 @@
import DirectNotifyGlobal import DirectNotifyGlobal
import types import types
import AttribDesc import AttribDesc
import EntityTypes import EntityTypeDesc
from PythonUtil import mostDerivedLast from PythonUtil import mostDerivedLast
class EntityTypeRegistry: class EntityTypeRegistry:
notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeRegistry') notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeRegistry')
def __init__(self, entityTypeModule=EntityTypes): def __init__(self, entityTypeModule):
"""pass in a module that contains EntityType classes""" """pass in a module that contains EntityTypeDesc classes"""
# get a list of the entity type classes in the type module # get a list of the EntityTypeDesc classes in the type module
classes = [] classes = []
for key, value in entityTypeModule.__dict__.items(): for key, value in entityTypeModule.__dict__.items():
if type(value) is types.ClassType: if type(value) is types.ClassType:
if issubclass(value, EntityTypes.Entity): if issubclass(value, EntityTypeDesc.EntityTypeDesc):
classes.append(value) 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) mostDerivedLast(classes)
# scrub through the class heirarchy and compile complete
# attribute descriptor lists for each concrete Entity type class
typeName2class = {}
for c in classes: for c in classes:
# if this is a concrete Entity type, add it to the dict
if c.__dict__.has_key('type'): if c.__dict__.has_key('type'):
if typeName2class.has_key(c.type): if self.entTypeName2typeDesc.has_key(c.type):
EntityTypeRegistry.notify.debug( # a more-derived class is replacing a less-derived class
"replacing %s with %s for type '%s'" % # to implement a particular entity type
(typeName2class[c.type], c, c.type)) EntityTypeRegistry.notify.info(
typeName2class[c.type] = c "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 # create mapping of entity typename (abstract or concrete) to list
self.typeName2attribNames = {} # of entity typenames are concrete and are of that type or derive
# maps entity typename to dict of attrib name -> attrib descriptor # from that type
self.typeName2attribDescDict = {} self.typeName2derivedTypeNames = {}
for typename, typeDesc in self.entTypeName2typeDesc.items():
for typeName, c in typeName2class.items(): typenames = []
self.typeName2attribNames[typeName] = [] for tn, td in self.entTypeName2typeDesc.items():
self.typeName2attribDescDict[typeName] = {} if td.isConcrete():
if issubclass(td.__class__, typeDesc.__class__):
typenames.append(tn)
self.typeName2derivedTypeNames[typename] = typenames
# ordered list of attrib descriptors def getTypeDesc(self, entTypeName):
attribDescs = c._attribDescs """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: def getTypeNamesFromOutputType(self, outputType):
attribName = desc.getName() """return Entity typenames for Entity types with particular output"""
self.typeName2attribNames[typeName].append(attribName) return self.output2typeNames.get(outputType, [])
self.typeName2attribDescDict[typeName][attribName] = desc
def getAttribNames(self, entityTypeName): def getDerivedTypeNames(self, entTypeName):
""" returns ordered list of attribute names for entity type """ """return Entity typenames that are of or derive from an entity type,
assert entityTypeName in self.typeName2attribNames which may be concrete or abstract"""
return self.typeName2attribNames[entityTypeName] assert entTypeName in self.typeName2derivedTypeNames,\
"unknown entity type '%s'" % entTypeName
def getAttribDescDict(self, entityTypeName): return self.typeName2derivedTypeNames[entTypeName]
""" returns dict of attribName -> attribDescriptor """
assert entityTypeName in self.typeName2attribDescDict
return self.typeName2attribDescDict[entityTypeName]
def __hash__(self): def __hash__(self):
return hash(str(self)) return hash(repr(self))
def __str__(self): def __repr__(self):
return str(self.typeName2attribNames)+str(self.typeName2attribDescDict) # this is used to produce a hash value
return str(self.entTypeName2typeDesc)
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

View File

@ -1,28 +1,17 @@
"""EntityTypes module: contains classes that describe Entity types""" """EntityTypes module: contains classes that describe Entity types"""
from EntityTypeDesc import EntityTypeDesc
from SpecImports import * from SpecImports import *
class Entity: class Entity(EntityTypeDesc):
abstract = 1
type = 'entity'
attribs = ( attribs = (
('type', None), ('type', None),
('name', 'unnamed'), ('name', 'unnamed'),
('comment', ''), ('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): class LevelMgr(Entity):
type = 'levelMgr' type = 'levelMgr'
attribs = ( attribs = (
@ -60,7 +49,7 @@ class Nodepath(Entity):
('hpr', Vec3(0,0,0), 'hpr'), ('hpr', Vec3(0,0,0), 'hpr'),
) )
class Zone(Entity, Nodepath): class Zone(Nodepath):
type = 'zone' type = 'zone'
delAttribs = ( delAttribs = (
'parent', 'parent',
@ -86,6 +75,7 @@ class CutScene(Entity):
) )
class BarrelBase(Nodepath): class BarrelBase(Nodepath):
abstract = 1
delAttribs = ( delAttribs = (
'hpr', 'hpr',
) )
@ -103,7 +93,8 @@ class GagBarrel(BarrelBase):
('gagTrack', 0, 'choice', {'choiceSet':range(7)}), ('gagTrack', 0, 'choice', {'choiceSet':range(7)}),
) )
class Switch(Entity, Nodepath): class Switch(Nodepath):
abstract = 1
output = 'bool' output = 'bool'
attribs = ( attribs = (
('scale', Vec3(1), 'scale'), ('scale', Vec3(1), 'scale'),
@ -131,17 +122,6 @@ class ConveyorBelt(Nodepath):
('floorName', 'platformcollision'), ('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): class Door(Entity):
type = 'door' type = 'door'
output = 'bool' output = 'bool'
@ -177,6 +157,31 @@ class Grid(Nodepath):
('numRow', 3, 'int'), ('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): class Lift(Nodepath):
type = 'lift' type = 'lift'
attribs = ( attribs = (

View File

@ -116,7 +116,8 @@ class LevelSpec:
# create a new entity spec entry w/ default values # create a new entity spec entry w/ default values
globalEnts[entId] = {} globalEnts[entId] = {}
spec = globalEnts[entId] spec = globalEnts[entId]
attribDescs = self.entTypeReg.getAttribDescDict(entType) attribDescs = self.entTypeReg.getTypeDesc(entType
).getAttribDescDict()
for name, desc in attribDescs.items(): for name, desc in attribDescs.items():
spec[name] = desc.getDefaultValue() spec[name] = desc.getDefaultValue()
spec['type'] = entType spec['type'] = entType
@ -323,8 +324,9 @@ class LevelSpec:
assert spec.has_key('type') assert spec.has_key('type')
entType = spec['type'] entType = spec['type']
attribNames = self.entTypeReg.getAttribNames(entType) typeDesc = self.entTypeReg.getTypeDesc(entType)
attribDescs = self.entTypeReg.getAttribDescDict(entType) attribNames = typeDesc.getAttribNames()
attribDescs = typeDesc.getAttribDescDict()
# are there any unknown attribs in the spec? # are there any unknown attribs in the spec?
for attrib in spec.keys(): for attrib in spec.keys():