mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 19:08:55 -04:00
199 lines
7.8 KiB
Python
Executable File
199 lines
7.8 KiB
Python
Executable File
"""Entity.py: contains the Entity class"""
|
|
|
|
from DirectObject import *
|
|
from PythonUtil import lineInfo
|
|
import string
|
|
import DirectNotifyGlobal
|
|
|
|
class Entity(DirectObject):
|
|
"""Entity is the base class for all objects that exist in a Level
|
|
and can be edited with the LevelEditor."""
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('Entity')
|
|
|
|
__attribs__ = (
|
|
'type',
|
|
'name',
|
|
'comment',
|
|
)
|
|
|
|
def __init__(self, level=None, entId=None):
|
|
self.initializeEntity(level, entId)
|
|
|
|
def initializeEntity(self, level, entId):
|
|
"""Distributed entities don't know their level or entId values
|
|
until they've been generated, so they call this after they've
|
|
been generated. At that point, the entity is good to go."""
|
|
self.level = level
|
|
self.entId = entId
|
|
if (self.level is not None) and (self.entId is not None):
|
|
self.level.initializeEntity(self)
|
|
|
|
def __str__(self):
|
|
return 'ent%s(%s)' % (self.entId, self.level.getEntityType(self.entId))
|
|
|
|
def destroy(self):
|
|
self.level.onEntityDestroy(self.entId)
|
|
del self.level
|
|
|
|
def privGetSetter(self, attrib):
|
|
setFuncName = 'set%s%s' % (string.upper(attrib[0]), attrib[1:])
|
|
if hasattr(self, setFuncName):
|
|
return getattr(self, setFuncName)
|
|
return None
|
|
|
|
def callSetters(self, *attribs):
|
|
"""call this with a list of attribs, and any that exist on the
|
|
entity and have setters will be passed to their setter"""
|
|
self.privCallSetters(0, *attribs)
|
|
|
|
def callSettersAndDelete(self, *attribs):
|
|
"""same as callSetters, but also removes attribs from entity"""
|
|
self.privCallSetters(1, *attribs)
|
|
|
|
def privCallSetters(self, doDelete, *attribs):
|
|
"""common implementation of callSetters and callSettersAndDelete"""
|
|
for attrib in attribs:
|
|
if hasattr(self, attrib):
|
|
setter = self.privGetSetter(attrib)
|
|
if setter is not None:
|
|
value = getattr(self, attrib)
|
|
if doDelete:
|
|
delattr(self, attrib)
|
|
setter(value)
|
|
|
|
# this will be called with each item of our spec data on initialization
|
|
def setAttribInit(self, attrib, value):
|
|
if hasattr(self, attrib):
|
|
Entity.notify.warning('%s already has member %s in %s' %
|
|
(self, attrib, lineInfo()[2]))
|
|
# TODO: we should probably put this crep in a dictionary
|
|
# rather than dump it into the entity's namespace
|
|
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
|
|
# otherwise set attrib directly and call notify func
|
|
setter = self.privGetSetter(attrib)
|
|
if setter is not None:
|
|
# call the setter
|
|
setter(value)
|
|
else:
|
|
# set the attrib directly
|
|
self.__dict__[attrib] = value
|
|
# and call the notify func
|
|
self.attribChanged(attrib, value)
|
|
|
|
def attribChanged(self, attrib, value):
|
|
"""This is called when a parameter is tweaked and no setter
|
|
is called; i.e. the value is set directly on the object.
|
|
Some Entities might want to completely reset every time anything
|
|
is tweaked; this is the place to do it, just override this func
|
|
in your derived class
|
|
"""
|
|
pass
|
|
|
|
"""
|
|
def getAttribInfo(self):
|
|
return self.attribs
|
|
"""
|
|
|
|
def debugPrint(self, message):
|
|
"""for debugging"""
|
|
return self.notify.debug(
|
|
str(self.__dict__.get('entId', '?'))+' '+message)
|