2
0
Эх сурвалжийг харах

added levelMgr, changed AI entity creation, reorganized level initialization

Darren Ranalli 22 жил өмнө
parent
commit
80d1520092

+ 3 - 3
direct/src/level/BasicEntities.py

@@ -11,9 +11,9 @@ class privNodePathImpl(NodePath.NodePath):
         NodePath.NodePath.__init__(self, node)
         NodePath.NodePath.__init__(self, node)
 
 
     def initializeEntity(self):
     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'):
         if hasattr(self, 'parent'):
             self.level.requestReparent(self, self.parent)
             self.level.requestReparent(self, self.parent)

+ 30 - 13
direct/src/level/DistributedLevel.py

@@ -24,8 +24,6 @@ class DistributedLevel(DistributedObject.DistributedObject,
         self.notify.debug('generate')
         self.notify.debug('generate')
         DistributedObject.DistributedObject.generate(self)
         DistributedObject.DistributedObject.generate(self)
 
 
-        self.setLevelId(self.doId)
-
         # this dict stores entity reparents if the parent hasn't been
         # this dict stores entity reparents if the parent hasn't been
         # created yet
         # created yet
         self.parent2ChildIds = {}
         self.parent2ChildIds = {}
@@ -52,10 +50,21 @@ class DistributedLevel(DistributedObject.DistributedObject,
         self.scenarioIndex = scenarioIndex
         self.scenarioIndex = scenarioIndex
 
 
     def initializeLevel(self, spec):
     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
         # load stuff
-        self.geom = loader.loadModel(self.spec['modelFilename'])
+        self.geom = loader.loadModel(self.modelFilename)
 
 
         def findNumberedNodes(baseString, model=self.geom, self=self):
         def findNumberedNodes(baseString, model=self.geom, self=self):
             # finds nodes whose name follows the pattern 'baseString#'
             # finds nodes whose name follows the pattern 'baseString#'
@@ -133,22 +142,30 @@ class DistributedLevel(DistributedObject.DistributedObject,
 
 
         self.initVisibility()
         self.initVisibility()
 
 
-        # create client-side Entities
+        # create the rest of the client-side Entities
         # TODO: only create client-side Entities for the
         # TODO: only create client-side Entities for the
         # currently-visible zones?
         # 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
         assert len(self.parent2ChildIds) == 0
 
 
     def makeEntityCreator(self):
     def makeEntityCreator(self):
-        """inheritors, override if desired"""
+        """Create the object that will be used to create Entities.
+        Inheritors, override if desired."""
         return EntityCreator.EntityCreator()
         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):
     def announceGenerate(self):
         self.notify.debug('announceGenerate')
         self.notify.debug('announceGenerate')
         DistributedObject.DistributedObject.announceGenerate(self)
         DistributedObject.DistributedObject.announceGenerate(self)

+ 38 - 13
direct/src/level/DistributedLevelAI.py

@@ -19,12 +19,25 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
     def initializeLevel(self, spec, uberZoneId):
     def initializeLevel(self, spec, uberZoneId):
         self.uberZoneId = 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
         # choose a scenario
         wc = WeightedChoice.WeightedChoice(spec['scenarios'], 1)
         wc = WeightedChoice.WeightedChoice(spec['scenarios'], 1)
         scenario = wc.choose()
         scenario = wc.choose()
         scenarioIndex = spec['scenarios'].index(scenario)
         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
         # allocate the rest of the zones; add one for the uber-zone
         self.numZones = len(self.spec['zones']) + 1
         self.numZones = len(self.spec['zones']) + 1
@@ -39,9 +52,28 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
             self.startTime, bits=32)
             self.startTime, bits=32)
 
 
     def makeEntityCreator(self):
     def makeEntityCreator(self):
-        """inheritors, override if desired"""
+        """Create the object that will be used to create Entities.
+        Inheritors, override if desired."""
         return EntityCreatorAI.EntityCreatorAI()
         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
     # required-field getters
     def getZoneIds(self):
     def getZoneIds(self):
         return self.zoneIds
         return self.zoneIds
@@ -56,17 +88,10 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
         self.notify.debug('generate')
         self.notify.debug('generate')
         DistributedObjectAI.DistributedObjectAI.generate(self)
         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):
     def delete(self):
         self.notify.debug('delete')
         self.notify.debug('delete')

+ 10 - 14
direct/src/level/Entity.py

@@ -7,25 +7,21 @@ class Entity:
     and can be edited with the LevelEditor."""
     and can be edited with the LevelEditor."""
 
 
     # these are values that can be changed in the level editor
     # 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, PythonType, CallSetterOnInitialization
         ('name', str, 0),
         ('name', str, 0),
         ('comment', str, 0),
         ('comment', str, 0),
         )
         )
 
 
-    def __init__(self, level, entId, tweakables=None):
+    def __init__(self, level, entId, attribs=None):
         self.level = level
         self.level = level
         self.entId = entId
         self.entId = entId
 
 
-        self.tweakables = Entity.Tweakables
+        self.attribs = Entity.Attribs
         # add any additional tweakable values
         # 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):
     def initializeEntity(self):
         """Call this once on initialization to set this entity's
         """Call this once on initialization to set this entity's
         spec data"""
         spec data"""
@@ -40,16 +36,16 @@ class Entity:
             return getattr(self, setFuncName)
             return getattr(self, setFuncName)
         return None
         return None
 
 
-    def callSetters(self, attribList):
+    def callSetters(self, *attribs):
         """call this with a list of attribs, and any that exist on the
         """call this with a list of attribs, and any that exist on the
         entity and have setters will be passed to their setter"""
         entity and have setters will be passed to their setter"""
-        for attrib in attribList:
+        for attrib in attribs:
             if hasattr(self, attrib):
             if hasattr(self, attrib):
                 setter = self.privGetSetter(attrib)
                 setter = self.privGetSetter(attrib)
                 if setter is not None:
                 if setter is not None:
                     setter(getattr(self, attrib))
                     setter(getattr(self, attrib))
 
 
-    def paramChanged(self):
+    def attribChanged(self):
         """This is called when a parameter is tweaked and no setter
         """This is called when a parameter is tweaked and no setter
         is called; i.e. the value is set directly on the object.
         is called; i.e. the value is set directly on the object.
         Some Entities might want to completely reset every time anything
         Some Entities might want to completely reset every time anything
@@ -58,8 +54,8 @@ class Entity:
         """
         """
         pass
         pass
 
 
-    def getTweakables(self):
-        return self.tweakables
+    def getAttribInfo(self):
+        return self.attribs
 
 
     def privTweak(self, name, value):
     def privTweak(self, name, value):
         self.__dict__[name] = value
         self.__dict__[name] = value

+ 6 - 1
direct/src/level/EntityCreator.py

@@ -2,6 +2,7 @@
 
 
 import BasicEntities
 import BasicEntities
 import DirectNotifyGlobal
 import DirectNotifyGlobal
+import LevelMgr
 
 
 # some useful constructor functions
 # some useful constructor functions
 # ctor functions must take (level, entId)
 # ctor functions must take (level, entId)
@@ -17,12 +18,16 @@ class EntityCreator:
     def __init__(self):
     def __init__(self):
         self.entType2Ctor = {}
         self.entType2Ctor = {}
         self.privRegisterTypes({
         self.privRegisterTypes({
+            'levelMgr': LevelMgr.LevelMgr,
             'logicGate': nothing,
             'logicGate': nothing,
             'nodepath': BasicEntities.NodePathEntity,
             'nodepath': BasicEntities.NodePathEntity,
             })
             })
 
 
     def privRegisterType(self, entType, ctor):
     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
         self.entType2Ctor[entType] = ctor
 
 
     def privRegisterTypes(self, type2ctor):
     def privRegisterTypes(self, type2ctor):

+ 18 - 4
direct/src/level/EntityCreatorAI.py

@@ -2,9 +2,13 @@
 
 
 import DirectNotifyGlobal
 import DirectNotifyGlobal
 import LogicGateAI
 import LogicGateAI
+import LevelMgrAI
 
 
 # some useful constructor functions
 # 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):
 def createDistributedEntity(AIclass, air, levelDoId, entId, zoneId):
     """create a distributed entity and call generate"""
     """create a distributed entity and call generate"""
     ent = AIclass(air, levelDoId, entId)
     ent = AIclass(air, levelDoId, entId)
@@ -23,23 +27,33 @@ class EntityCreatorAI:
     def __init__(self):
     def __init__(self):
         self.entType2Ctor = {}
         self.entType2Ctor = {}
         self.privRegisterTypes({
         self.privRegisterTypes({
+            'levelMgr': LevelMgrAI.LevelMgrAI,
             'logicGate': LogicGateAI.LogicGateAI,
             'logicGate': LogicGateAI.LogicGateAI,
             'nodepath': nothing,
             'nodepath': nothing,
             })
             })
 
 
     def privRegisterType(self, entType, ctor):
     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
         self.entType2Ctor[entType] = ctor
 
 
     def privRegisterTypes(self, type2ctor):
     def privRegisterTypes(self, type2ctor):
         for entType, ctor in type2ctor.items():
         for entType, ctor in type2ctor.items():
             self.privRegisterType(entType, ctor)
             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):
         if not self.entType2Ctor.has_key(entType):
             EntityCreatorAI.notify.warning(
             EntityCreatorAI.notify.warning(
                 'createEntity(entType=%s, levelDoId=%s, '
                 'createEntity(entType=%s, levelDoId=%s, '
                 'entId=%s, zoneId=%s) not found' %
                 'entId=%s, zoneId=%s) not found' %
                 (entType, levelDoId, entId, zoneId))
                 (entType, levelDoId, entId, zoneId))
             return None
             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)

+ 19 - 4
direct/src/level/LevelBase.py

@@ -14,12 +14,10 @@ class LevelBase:
         if levelId is not None:
         if levelId is not None:
             self.setLevelId(levelId)
             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
         """ subclass should call this as soon as it has located
         its spec data """
         its spec data """
+        self.levelId = levelId
         self.spec = spec
         self.spec = spec
         self.scenarioIndex = scenarioIndex
         self.scenarioIndex = scenarioIndex
 
 
@@ -31,6 +29,23 @@ class LevelBase:
         entId2Spec.update(scenarioEntities)
         entId2Spec.update(scenarioEntities)
         self.entId2Spec = entId2Spec
         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
         # this will be filled in as the entities are created and report in
         self.entities = {}
         self.entities = {}
 
 

+ 3 - 5
direct/src/level/LogicGateAI.py

@@ -78,15 +78,13 @@ class LogicGateAI(Entity.Entity, PandaObject.PandaObject):
         "xnor": xnorTest,
         "xnor": xnorTest,
     }
     }
 
 
-    def __init__(self, air, levelDoId, entId, zoneId=None):
+    def __init__(self, level, entId):
         """entId: """
         """entId: """
         assert(self.debugPrint(
         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.input1 = None
         self.input2 = None
         self.input2 = None
-        self.levelDoId = levelDoId
-        level = air.doId2do[self.levelDoId]
         Entity.Entity.__init__(self, level, entId)
         Entity.Entity.__init__(self, level, entId)
         self.initializeEntity()
         self.initializeEntity()
         self.setLogicType(self.logicType)
         self.setLogicType(self.logicType)