mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
abstract entity types can have typenames, exposed inheritance and output-type information through entityTypeReg
This commit is contained in:
parent
61008f86b7
commit
14efabc778
@ -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),
|
||||
)
|
||||
|
112
direct/src/level/EntityTypeDesc.py
Executable file
112
direct/src/level/EntityTypeDesc.py
Executable 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))
|
@ -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)
|
||||
|
@ -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 = (
|
||||
|
@ -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():
|
||||
|
Loading…
x
Reference in New Issue
Block a user