diff --git a/direct/src/level/BasicEntities.py b/direct/src/level/BasicEntities.py index 188b2b8374..a6ba0fc88c 100755 --- a/direct/src/level/BasicEntities.py +++ b/direct/src/level/BasicEntities.py @@ -11,9 +11,9 @@ class privNodePathImpl(NodePath.NodePath): NodePath.NodePath.__init__(self, node) def initializeEntity(self): - self.callSetters(('pos','x','y','z', - 'hpr','h','p','r', - 'scale','sx','sx','sz')) + self.callSetters('pos','x','y','z', + 'hpr','h','p','r', + 'scale','sx','sx','sz') if hasattr(self, 'parent'): self.level.requestReparent(self, self.parent) diff --git a/direct/src/level/DistributedLevel.py b/direct/src/level/DistributedLevel.py index 72ee2cd52c..204b57cb11 100755 --- a/direct/src/level/DistributedLevel.py +++ b/direct/src/level/DistributedLevel.py @@ -24,8 +24,6 @@ class DistributedLevel(DistributedObject.DistributedObject, self.notify.debug('generate') DistributedObject.DistributedObject.generate(self) - self.setLevelId(self.doId) - # this dict stores entity reparents if the parent hasn't been # created yet self.parent2ChildIds = {} @@ -52,10 +50,21 @@ class DistributedLevel(DistributedObject.DistributedObject, self.scenarioIndex = scenarioIndex def initializeLevel(self, spec): - """ subclass should call this as soon as it's located its spec data """ - LevelBase.LevelBase.initializeLevel(self, spec, self.scenarioIndex) + """subclass should call this as soon as it's located its spec data. + Must be called after obj has been generated.""" + LevelBase.LevelBase.initializeLevel(self, self.doId, + spec, self.scenarioIndex) + self.localEntities = {} + + # get list of entity types we need to create + entTypes = self.entType2Ids.keys() + + # create the levelMgr + self.levelMgr = self.createEntity(self.entType2Ids['levelMgr'][0]) + entTypes.remove('levelMgr') + # load stuff - self.geom = loader.loadModel(self.spec['modelFilename']) + self.geom = loader.loadModel(self.modelFilename) def findNumberedNodes(baseString, model=self.geom, self=self): # finds nodes whose name follows the pattern 'baseString#' @@ -133,22 +142,30 @@ class DistributedLevel(DistributedObject.DistributedObject, self.initVisibility() - # create client-side Entities + # create the rest of the client-side Entities # TODO: only create client-side Entities for the # currently-visible zones? - self.localEntities = {} - for entId, spec in self.entId2Spec.iteritems(): - entity = self.entityCreator.createEntity(spec['type'], self, entId) - if entity is not None: - self.localEntities[entId] = entity + for entType in entTypes: + for entId in self.entType2Ids[entType]: + self.createEntity(entId) - # there should not be any pending reparents left + # there should not be any pending reparents left at this point assert len(self.parent2ChildIds) == 0 def makeEntityCreator(self): - """inheritors, override if desired""" + """Create the object that will be used to create Entities. + Inheritors, override if desired.""" return EntityCreator.EntityCreator() + def createEntity(self, entId): + assert not self.localEntities.has_key(entId) + spec = self.entId2Spec[entId] + self.notify.debug('creating %s %s' % (spec['type'], entId)) + entity = self.entityCreator.createEntity(spec['type'], self, entId) + if entity is not None: + self.localEntities[entId] = entity + return entity + def announceGenerate(self): self.notify.debug('announceGenerate') DistributedObject.DistributedObject.announceGenerate(self) diff --git a/direct/src/level/DistributedLevelAI.py b/direct/src/level/DistributedLevelAI.py index db73acd84a..34b78da883 100755 --- a/direct/src/level/DistributedLevelAI.py +++ b/direct/src/level/DistributedLevelAI.py @@ -19,12 +19,25 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, def initializeLevel(self, spec, uberZoneId): self.uberZoneId = uberZoneId + # We need a unique level ID to init the level system, and we need + # the level system to get the data for our required fields. + # Pre-allocate a doId. + self.preAllocateDoId() + # choose a scenario wc = WeightedChoice.WeightedChoice(spec['scenarios'], 1) scenario = wc.choose() scenarioIndex = spec['scenarios'].index(scenario) - LevelBase.LevelBase.initializeLevel(self, spec, scenarioIndex) + LevelBase.LevelBase.initializeLevel(self, self.doId, + spec, scenarioIndex) + self.aiEntities = {} + # get list of entity types we need to create + self.entTypes = self.entType2Ids.keys() + + # create the levelMgr + self.levelMgr = self.createEntity(self.entType2Ids['levelMgr'][0]) + self.entTypes.remove('levelMgr') # allocate the rest of the zones; add one for the uber-zone self.numZones = len(self.spec['zones']) + 1 @@ -39,9 +52,28 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, self.startTime, bits=32) def makeEntityCreator(self): - """inheritors, override if desired""" + """Create the object that will be used to create Entities. + Inheritors, override if desired.""" return EntityCreatorAI.EntityCreatorAI() + def createEntity(self, entId): + assert not self.aiEntities.has_key(entId) + spec = self.entId2Spec[entId] + self.notify.debug('creating %s %s' % (spec['type'], entId)) + zone = spec.get('zone') + # we might attempt to create non-distributed entities before + # we've been generated + if zone is not None: + zone = self.getZoneId(zone) + entity = self.entityCreator.createEntity( + spec['type'], self, entId, self.air, zone) + else: + entity = self.entityCreator.createEntity( + spec['type'], self, entId) + if entity is not None: + self.aiEntities[entId] = entity + return entity + # required-field getters def getZoneIds(self): return self.zoneIds @@ -56,17 +88,10 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, self.notify.debug('generate') DistributedObjectAI.DistributedObjectAI.generate(self) - self.setLevelId(self.doId) - - # create the Entities - self.aiEntities = {} - for entId, spec in self.entId2Spec.iteritems(): - self.notify.debug('creating %s %s' % (spec['type'], entId)) - entity = self.entityCreator.createEntity( - spec['type'], self.air, self.doId, entId, - self.getZoneId(spec['zone'])) - if entity is not None: - self.aiEntities[entId] = entity + # create the rest of the Entities + for entType in self.entTypes: + for entId in self.entType2Ids[entType]: + self.createEntity(entId) def delete(self): self.notify.debug('delete') diff --git a/direct/src/level/Entity.py b/direct/src/level/Entity.py index 0df39b2453..529b048888 100755 --- a/direct/src/level/Entity.py +++ b/direct/src/level/Entity.py @@ -7,25 +7,21 @@ class Entity: and can be edited with the LevelEditor.""" # these are values that can be changed in the level editor - # TODO: pick a good name for these values; - # parameters, tweakables, attributes, attribs, traits, - Tweakables = ( + Attribs = ( # Name, PythonType, CallSetterOnInitialization ('name', str, 0), ('comment', str, 0), ) - def __init__(self, level, entId, tweakables=None): + def __init__(self, level, entId, attribs=None): self.level = level self.entId = entId - self.tweakables = Entity.Tweakables + self.attribs = Entity.Attribs # add any additional tweakable values - if tweakables is not None: - self.tweakables.update(tweakables) + if attribs is not None: + self.attribs.update(attribs) - # TODO: funcs to populate the entity with its spec data, and system - # to call back when data changes def initializeEntity(self): """Call this once on initialization to set this entity's spec data""" @@ -40,16 +36,16 @@ class Entity: return getattr(self, setFuncName) return None - def callSetters(self, attribList): + 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""" - for attrib in attribList: + for attrib in attribs: if hasattr(self, attrib): setter = self.privGetSetter(attrib) if setter is not None: setter(getattr(self, attrib)) - def paramChanged(self): + def attribChanged(self): """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 @@ -58,8 +54,8 @@ class Entity: """ pass - def getTweakables(self): - return self.tweakables + def getAttribInfo(self): + return self.attribs def privTweak(self, name, value): self.__dict__[name] = value diff --git a/direct/src/level/EntityCreator.py b/direct/src/level/EntityCreator.py index 46936be68b..d7872305ed 100755 --- a/direct/src/level/EntityCreator.py +++ b/direct/src/level/EntityCreator.py @@ -2,6 +2,7 @@ import BasicEntities import DirectNotifyGlobal +import LevelMgr # some useful constructor functions # ctor functions must take (level, entId) @@ -17,12 +18,16 @@ class EntityCreator: def __init__(self): self.entType2Ctor = {} self.privRegisterTypes({ + 'levelMgr': LevelMgr.LevelMgr, 'logicGate': nothing, 'nodepath': BasicEntities.NodePathEntity, }) def privRegisterType(self, entType, ctor): - assert(not self.entType2Ctor.has_key(entType)) + if self.entType2Ctor.has_key(entType): + EntityCreator.notify.warning( + 'replacing %s ctor %s with %s' % + (entType, self.entType2Ctor[entType], ctor)) self.entType2Ctor[entType] = ctor def privRegisterTypes(self, type2ctor): diff --git a/direct/src/level/EntityCreatorAI.py b/direct/src/level/EntityCreatorAI.py index bb02ad80d4..304e4955fc 100755 --- a/direct/src/level/EntityCreatorAI.py +++ b/direct/src/level/EntityCreatorAI.py @@ -2,9 +2,13 @@ import DirectNotifyGlobal import LogicGateAI +import LevelMgrAI # some useful constructor functions -# ctor functions must take (air, level doId, entId, zoneId) +# ctor functions for distributed entities must take +# (air, level doId, entId, zoneId) +# ctor functions for non-distributed entities must take +# (level, entId) def createDistributedEntity(AIclass, air, levelDoId, entId, zoneId): """create a distributed entity and call generate""" ent = AIclass(air, levelDoId, entId) @@ -23,23 +27,33 @@ class EntityCreatorAI: def __init__(self): self.entType2Ctor = {} self.privRegisterTypes({ + 'levelMgr': LevelMgrAI.LevelMgrAI, 'logicGate': LogicGateAI.LogicGateAI, 'nodepath': nothing, }) def privRegisterType(self, entType, ctor): - assert(not self.entType2Ctor.has_key(entType)) + if self.entType2Ctor.has_key(entType): + EntityCreatorAI.notify.warning( + 'replacing %s ctor %s with %s' % + (entType, self.entType2Ctor[entType], ctor)) self.entType2Ctor[entType] = ctor def privRegisterTypes(self, type2ctor): for entType, ctor in type2ctor.items(): self.privRegisterType(entType, ctor) - def createEntity(self, entType, air, levelDoId, entId, zoneId): + def createEntity(self, entType, level, entId, air=None, zoneId=None): + """zoneId=None indicates a non-distributed entity""" if not self.entType2Ctor.has_key(entType): EntityCreatorAI.notify.warning( 'createEntity(entType=%s, levelDoId=%s, ' 'entId=%s, zoneId=%s) not found' % (entType, levelDoId, entId, zoneId)) return None - return self.entType2Ctor[entType](air, levelDoId, entId, zoneId) + + if zoneId is None: + return self.entType2Ctor[entType](level, entId) + else: + levelDoId = level.doId + return self.entType2Ctor[entType](air, levelDoId, entId, zoneId) diff --git a/direct/src/level/LevelBase.py b/direct/src/level/LevelBase.py index c9fc39f67e..64ed4ab1ff 100755 --- a/direct/src/level/LevelBase.py +++ b/direct/src/level/LevelBase.py @@ -14,12 +14,10 @@ class LevelBase: if levelId is not None: self.setLevelId(levelId) - def setLevelId(self, levelId): - self.levelId = levelId - - def initializeLevel(self, spec, scenarioIndex): + def initializeLevel(self, levelId, spec, scenarioIndex): """ subclass should call this as soon as it has located its spec data """ + self.levelId = levelId self.spec = spec self.scenarioIndex = scenarioIndex @@ -31,6 +29,23 @@ class LevelBase: entId2Spec.update(scenarioEntities) self.entId2Spec = entId2Spec + # create some handy tables + self.entType2Ids = {} + self.entZone2Ids = {} + self.nonZoneEntIds = [] + for entId, spec in self.entId2Spec.items(): + entType = spec['type'] + self.entType2Ids.setdefault(entType, []) + self.entType2Ids[entType].append(entId) + + entZone = spec.get('zone') + # note that entities with no Zone will be filed under 'None' + self.entZone2Ids.setdefault(entZone, []) + self.entZone2Ids[entZone].append(entId) + + # there should be one and only one levelMgr + assert len(self.entType2Ids['levelMgr']) == 1 + # this will be filled in as the entities are created and report in self.entities = {} diff --git a/direct/src/level/LogicGateAI.py b/direct/src/level/LogicGateAI.py index b022869745..5b88a9ebfb 100755 --- a/direct/src/level/LogicGateAI.py +++ b/direct/src/level/LogicGateAI.py @@ -78,15 +78,13 @@ class LogicGateAI(Entity.Entity, PandaObject.PandaObject): "xnor": xnorTest, } - def __init__(self, air, levelDoId, entId, zoneId=None): + def __init__(self, level, entId): """entId: """ assert(self.debugPrint( - "LogicGateAI(air=%s, levelDoId=%s, entId=%s, zoneId=%s)" - %("the air", levelDoId, entId, zoneId))) + "LogicGateAI(entId=%s)" + %(entId))) self.input1 = None self.input2 = None - self.levelDoId = levelDoId - level = air.doId2do[self.levelDoId] Entity.Entity.__init__(self, level, entId) self.initializeEntity() self.setLogicType(self.logicType)