mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-05 03:15:07 -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):
|
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),
|
||||||
)
|
)
|
||||||
|
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 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():
|
||||||
|
typenames = []
|
||||||
|
for tn, td in self.entTypeName2typeDesc.items():
|
||||||
|
if td.isConcrete():
|
||||||
|
if issubclass(td.__class__, typeDesc.__class__):
|
||||||
|
typenames.append(tn)
|
||||||
|
self.typeName2derivedTypeNames[typename] = typenames
|
||||||
|
|
||||||
for typeName, c in typeName2class.items():
|
def getTypeDesc(self, entTypeName):
|
||||||
self.typeName2attribNames[typeName] = []
|
"""returns EntityTypeDesc instance for concrete Entity type"""
|
||||||
self.typeName2attribDescDict[typeName] = {}
|
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]
|
||||||
|
|
||||||
# ordered list of attrib descriptors
|
def getTypeNamesFromOutputType(self, outputType):
|
||||||
attribDescs = c._attribDescs
|
"""return Entity typenames for Entity types with particular output"""
|
||||||
|
return self.output2typeNames.get(outputType, [])
|
||||||
|
|
||||||
for desc in attribDescs:
|
def getDerivedTypeNames(self, entTypeName):
|
||||||
attribName = desc.getName()
|
"""return Entity typenames that are of or derive from an entity type,
|
||||||
self.typeName2attribNames[typeName].append(attribName)
|
which may be concrete or abstract"""
|
||||||
self.typeName2attribDescDict[typeName][attribName] = desc
|
assert entTypeName in self.typeName2derivedTypeNames,\
|
||||||
|
"unknown entity type '%s'" % entTypeName
|
||||||
def getAttribNames(self, entityTypeName):
|
return self.typeName2derivedTypeNames[entTypeName]
|
||||||
""" 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 __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
|
|
||||||
|
@ -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 = (
|
||||||
|
@ -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():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user