瀏覽代碼

code reorg; handling of dynamic zone entity creation and deletion

Darren Ranalli 22 年之前
父節點
當前提交
4b4c1e3a62

+ 15 - 18
direct/src/level/DistributedLevel.py

@@ -40,7 +40,7 @@ class DistributedLevel(DistributedObject.DistributedObject,
         # all our entities at that time.
         # all our entities at that time.
         toonbase.tcr.timeManager.synchronize('DistributedLevel.generate')
         toonbase.tcr.timeManager.synchronize('DistributedLevel.generate')
 
 
-    # required fields (these ought to be required fields, but
+    # "required" fields (these ought to be required fields, but
     # the AI level obj doesn't know the data values until it has been
     # the AI level obj doesn't know the data values until it has been
     # generated.)
     # generated.)
     def setZoneIds(self, zoneIds):
     def setZoneIds(self, zoneIds):
@@ -92,8 +92,6 @@ class DistributedLevel(DistributedObject.DistributedObject,
         # similar handlers in base classes
         # similar handlers in base classes
         if entType == 'levelMgr':
         if entType == 'levelMgr':
             self.__handleLevelMgrCreated()
             self.__handleLevelMgrCreated()
-        elif entType == 'zone':
-            self.__handleAllZonesCreated()
 
 
     def __handleLevelMgrCreated(self):
     def __handleLevelMgrCreated(self):
         # as soon as the levelMgr has been created, load up the model
         # as soon as the levelMgr has been created, load up the model
@@ -129,20 +127,9 @@ class DistributedLevel(DistributedObject.DistributedObject,
         self.zoneNums.sort()
         self.zoneNums.sort()
         self.notify.debug('zones: %s' % self.zoneNums)
         self.notify.debug('zones: %s' % self.zoneNums)
 
 
-        # hack in another doorway
-        dw = self.geom.attachNewNode('Doorway27')
-        dw.setPos(-49.4,86.7,19.26)
-        dw.setH(0)
-
-        # find the doorway nodes
-        self.doorwayNum2Node = findNumberedNodes('Doorway')
-
-    def __handleAllZonesCreated(self):
-        # fix up the floor collisions for walkable zones before
+        # fix up the floor collisions for walkable zones *before*
         # any entities get put under the model
         # any entities get put under the model
-        for zoneNum in self.zoneNums:
-            zoneNode = self.zoneNum2node[zoneNum]
-
+        for zoneNum,zoneNode in self.zoneNum2node.items():
             # if this is a walkable zone, fix up the model
             # if this is a walkable zone, fix up the model
             allColls = zoneNode.findAllMatches('**/+CollisionNode').asList()
             allColls = zoneNode.findAllMatches('**/+CollisionNode').asList()
             # which of them, if any, are floors?
             # which of them, if any, are floors?
@@ -170,6 +157,14 @@ class DistributedLevel(DistributedObject.DistributedObject,
                     self.toonEnterZone(zoneNum)
                     self.toonEnterZone(zoneNum)
                 self.accept('enter%s' % floorCollName, handleZoneEnter)
                 self.accept('enter%s' % floorCollName, handleZoneEnter)
 
 
+        # hack in another doorway
+        dw = self.geom.attachNewNode('Doorway27')
+        dw.setPos(-49.4,86.7,19.26)
+        dw.setH(0)
+
+        # find the doorway nodes
+        self.doorwayNum2Node = findNumberedNodes('Doorway')
+
     def announceGenerate(self):
     def announceGenerate(self):
         self.notify.debug('announceGenerate')
         self.notify.debug('announceGenerate')
         DistributedObject.DistributedObject.announceGenerate(self)
         DistributedObject.DistributedObject.announceGenerate(self)
@@ -270,7 +265,9 @@ class DistributedLevel(DistributedObject.DistributedObject,
 
 
         # if no viz, listen to all the zones
         # if no viz, listen to all the zones
         if not DistributedLevel.WantVisibility:
         if not DistributedLevel.WantVisibility:
-            self.setVisibility(self.zoneNums)
+            zoneNums = list(self.zoneNums)
+            zoneNums.remove(Level.Level.uberZoneNum)
+            self.setVisibility(zoneNums)
 
 
     def toonEnterZone(self, zoneNum):
     def toonEnterZone(self, zoneNum):
         self.notify.debug('toonEnterZone%s' % zoneNum)
         self.notify.debug('toonEnterZone%s' % zoneNum)
@@ -328,7 +325,7 @@ class DistributedLevel(DistributedObject.DistributedObject,
         # accepts list of visible zone numbers
         # accepts list of visible zone numbers
         # convert the zone numbers into their actual zoneIds
         # convert the zone numbers into their actual zoneIds
         # always include Toontown and factory uberZones
         # always include Toontown and factory uberZones
-        factoryUberZone = self.getZoneId(zoneNum=0)
+        factoryUberZone = self.getZoneId(zoneNum=Level.Level.UberZoneNum)
         visibleZoneIds = [ToontownGlobals.UberZone, factoryUberZone]
         visibleZoneIds = [ToontownGlobals.UberZone, factoryUberZone]
         for vz in vizList:
         for vz in vizList:
             visibleZoneIds.append(self.getZoneId(zoneNum=vz))
             visibleZoneIds.append(self.getZoneId(zoneNum=vz))

+ 7 - 22
direct/src/level/DistributedLevelAI.py

@@ -30,11 +30,6 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
     def delete(self):
     def delete(self):
         self.notify.debug('delete')
         self.notify.debug('delete')
         self.destroyLevel()
         self.destroyLevel()
-
-        # we do not allocate the uberZone for now, so don't deallocate it
-        for zoneId in self.zoneIds[1:]:
-            self.air.deallocateZone(zoneId)
-
         DistributedObjectAI.DistributedObjectAI.delete(self)
         DistributedObjectAI.DistributedObjectAI.delete(self)
 
 
     def initializeLevel(self, spec):
     def initializeLevel(self, spec):
@@ -48,9 +43,6 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
         scenario = wc.choose()
         scenario = wc.choose()
         scenarioIndex = spec['scenarios'].index(scenario)
         scenarioIndex = spec['scenarios'].index(scenario)
 
 
-        # this will hold the network zoneIds that we allocate
-        self.zoneIds = [self.uberZoneId]
-
         Level.Level.initializeLevel(self, self.doId,
         Level.Level.initializeLevel(self, self.doId,
                                     spec, scenarioIndex)
                                     spec, scenarioIndex)
 
 
@@ -59,30 +51,23 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
         Inheritors, override if desired."""
         Inheritors, override if desired."""
         return EntityCreatorAI.EntityCreatorAI(self.air, level=self)
         return EntityCreatorAI.EntityCreatorAI(self.air, level=self)
 
 
-    def allocateZoneId(self):
-        # set aside a zoneId for each zone; this is called by ZoneEntityAI
-        # there is error checking in air.allocateZone
-        self.zoneIds.append(self.air.allocateZone())
-
     def getEntityZoneId(self, entId):
     def getEntityZoneId(self, entId):
         """figure out what network zoneId an entity is in"""
         """figure out what network zoneId an entity is in"""
-        # TODO: where should the zone info come from? It could come
-        # from the 'parent' scene-graph info... but what about intangible
-        # distributed objects? I guess if they don't inherit from
-        # NodePathEntity et al, they'll just have an unused 'parent'
-        # attribute hanging around, which isn't the end of the world...
-
         # this func is called before the entity has been created; look
         # this func is called before the entity has been created; look
-        # into the spec data, since we can't get a handle on the object itself
+        # into the spec data, since we can't yet get a handle on the
+        # object itself at this point
         spec = self.entId2spec[entId]
         spec = self.entId2spec[entId]
         type = spec['type']
         type = spec['type']
         if type == 'zone':
         if type == 'zone':
             if not hasattr(self, 'zoneNum2zoneId'):
             if not hasattr(self, 'zoneNum2zoneId'):
-                # we haven't even created our zone entities yet;
+                # we haven't even started creating our zone entities yet;
                 # we have no idea yet which zoneNums map to which
                 # we have no idea yet which zoneNums map to which
                 # network zoneIds. just return None.
                 # network zoneIds. just return None.
                 return None
                 return None
-            return self.zoneNum2zoneId[spec['modelZoneNum']]
+            # If the entity *is* the zone, it will not yet be in the
+            # table; but since zone entities are currently not distributed,
+            # it's fine to return None.
+            return self.zoneNum2zoneId.get(spec['modelZoneNum'])
         if not spec.has_key('parent'):
         if not spec.has_key('parent'):
             return None
             return None
         return self.getEntityZoneId(spec['parent'])
         return self.getEntityZoneId(spec['parent'])

+ 13 - 11
direct/src/level/Entity.py

@@ -1,10 +1,11 @@
 """Entity.py: contains the Entity class"""
 """Entity.py: contains the Entity class"""
 
 
+from DirectObject import *
+from PythonUtil import lineInfo
 import string
 import string
 import DirectNotifyGlobal
 import DirectNotifyGlobal
-from PythonUtil import lineInfo
 
 
-class Entity:
+class Entity(DirectObject):
     """Entity is the base class for all objects that exist in a Level
     """Entity is the base class for all objects that exist in a Level
     and can be edited with the LevelEditor."""
     and can be edited with the LevelEditor."""
     notify = DirectNotifyGlobal.directNotify.newCategory('Entity')
     notify = DirectNotifyGlobal.directNotify.newCategory('Entity')
@@ -32,6 +33,7 @@ class Entity:
         return 'ent%s(%s)' % (self.entId, self.level.getEntityType(self.entId))
         return 'ent%s(%s)' % (self.entId, self.level.getEntityType(self.entId))
     
     
     def destroy(self):
     def destroy(self):
+        self.level.onEntityDestroy(self.entId)
         del self.level
         del self.level
         
         
     def privGetSetter(self, attrib):
     def privGetSetter(self, attrib):
@@ -40,6 +42,15 @@ class Entity:
             return getattr(self, setFuncName)
             return getattr(self, setFuncName)
         return None
         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):
     def privCallSetters(self, doDelete, *attribs):
         """common implementation of callSetters and callSettersAndDelete"""
         """common implementation of callSetters and callSettersAndDelete"""
         for attrib in attribs:
         for attrib in attribs:
@@ -51,15 +62,6 @@ class Entity:
                         delattr(self, attrib)
                         delattr(self, attrib)
                     setter(value)
                     setter(value)
 
 
-    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)
-
     # this will be called with each item of our spec data on initialization
     # this will be called with each item of our spec data on initialization
     def setAttribInit(self, attrib, value):
     def setAttribInit(self, attrib, value):
         if hasattr(self, attrib):
         if hasattr(self, attrib):

+ 21 - 34
direct/src/level/Level.py

@@ -30,6 +30,7 @@ class Level:
     entities and their interrelations, and creates and destroys entities"""
     entities and their interrelations, and creates and destroys entities"""
     notify = DirectNotifyGlobal.directNotify.newCategory('Level')
     notify = DirectNotifyGlobal.directNotify.newCategory('Level')
 
 
+    UberZoneNum = 0
     UberZoneEntId = 0
     UberZoneEntId = 0
 
 
     def __init__(self):
     def __init__(self):
@@ -61,10 +62,13 @@ class Level:
 
 
         # there should be one and only one levelMgr
         # there should be one and only one levelMgr
         assert len(self.entType2ids['levelMgr']) == 1
         assert len(self.entType2ids['levelMgr']) == 1
+        # make sure the uberzone is there
+        assert Level.UberZoneEntId in self.entType2ids['zone']
 
 
         # get an entity creator object
         # get an entity creator object
         self.entityCreator = self.createEntityCreator()
         self.entityCreator = self.createEntityCreator()
         # create all the entities
         # create all the entities
+        # TODO: we should leave this to a subclass or the level user
         self.createAllEntities(priorityTypes=['levelMgr','zone'])
         self.createAllEntities(priorityTypes=['levelMgr','zone'])
 
 
     def destroyLevel(self):
     def destroyLevel(self):
@@ -132,6 +136,13 @@ class Level:
         # below.
         # below.
         if entity is not None:
         if entity is not None:
             self.createdEntities.append(entity)
             self.createdEntities.append(entity)
+
+        # call the create handler
+        # we used to do this in initializeEntity, but that did not
+        # allow for additional initialization to be performed in
+        # derived entity __init__ funcs
+        self.onEntityCreate(entId)
+
         return entity
         return entity
 
 
     def initializeEntity(self, entity):
     def initializeEntity(self, entity):
@@ -150,11 +161,8 @@ class Level:
         # entity is initialized, add it to the list of entities
         # entity is initialized, add it to the list of entities
         self.entities[entity.entId] = entity
         self.entities[entity.entId] = entity
 
 
-        # call the create handler
-        self.onEntityCreate(entId)
-
     def getEntity(self, entId):
     def getEntity(self, entId):
-        return self.entities[entId]
+        return self.entities.get(entId)
 
 
     def getEntityType(self, entId):
     def getEntityType(self, entId):
         return self.entId2spec[entId]['type']
         return self.entId2spec[entId]['type']
@@ -213,8 +221,6 @@ class Level:
         messenger.send(self.getEntityTypePreCreateEvent(entType))
         messenger.send(self.getEntityTypePreCreateEvent(entType))
     def onEntityTypePostCreate(self, entType):
     def onEntityTypePostCreate(self, entType):
         """Level has just created these entities"""
         """Level has just created these entities"""
-        if entType == 'zone':
-            self.__handleAllZonesCreated()
         messenger.send(self.getEntityTypePostCreateEvent(entType))
         messenger.send(self.getEntityTypePostCreateEvent(entType))
     # ENTITY
     # ENTITY
     def onEntityCreate(self, entId):
     def onEntityCreate(self, entId):
@@ -226,31 +232,12 @@ class Level:
             self.getEntityOfTypeCreateEvent(self.getEntityType(entId)),
             self.getEntityOfTypeCreateEvent(self.getEntityType(entId)),
             [entId])
             [entId])
 
 
-    def __handleAllZonesCreated(self):
-        """once all the zone entities have been created, and we've got a
-        list of zoneIds in self.zoneIds, init zone tables"""
-        # create a table mapping the model's zone numbers to the zone
-        # entIds; zone entities are tied to model zone nums in the level spec,
-        # this is just for convenient lookup
-        self.zoneNum2entId = {}
-        for entId in self.entType2ids['zone']:
-            zoneEnt = self.getEntity(entId)
-            # NOTE: modelZoneNum comes from the entity's spec data
-            self.zoneNum2entId[zoneEnt.modelZoneNum] = entId
-
-        # At this point, we need to have a 'self.zoneIds' table of network
-        # zoneIds, one for each zone including the UberZone. This is where
-        # we decide which zoneNum/entId gets mapped to which zoneId.
-        # We sort the zoneNums, and then pair the sorted zoneNums up with the
-        # zoneIds in the order that they appear in the self.zoneIds table.
-        modelZoneNums = self.zoneNum2entId.keys()
-        modelZoneNums.sort()
-        # maps of zoneNum/zoneEntId to network zoneId
-        self.zoneNum2zoneId = {}
-        self.zoneEntId2zoneId = {}
-        for i in range(len(modelZoneNums)):
-            modelZoneNum = modelZoneNums[i]
-            zoneEntId = self.zoneNum2entId[modelZoneNum]
-            zoneId = self.zoneIds[i]
-            self.zoneNum2zoneId[modelZoneNum] = zoneId
-            self.zoneEntId2zoneId[zoneEntId] = zoneId
+    # these are events and handlers that are invoked as entities are destroyed
+    def getEntityDestroyEvent(self, entId):
+        """This is the event that is thrown immediately before an
+        entity is destroyed"""
+        return 'entityDestroy-%s-%s' % (self.levelId, entId)
+    def onEntityDestroy(self, entId):
+        """Level is about to destroy this entity"""
+        # send the entity-destroy event
+        messenger.send(self.getEntityDestroyEvent(entId))

+ 70 - 6
direct/src/level/LevelMgr.py

@@ -1,18 +1,82 @@
 """LevelMgr module: contains the LevelMgr class"""
 """LevelMgr module: contains the LevelMgr class"""
 
 
-import Entity
+from PythonUtil import Functor
+import LevelMgrBase
 
 
-class LevelMgr(Entity.Entity):
+class LevelMgr(LevelMgrBase.LevelMgrBase):
     """This class manages editable client-side level attributes"""
     """This class manages editable client-side level attributes"""
     def __init__(self, level, entId):
     def __init__(self, level, entId):
-        Entity.Entity.__init__(self, level, entId)
-        self.level.levelMgr = self
+        LevelMgrBase.LevelMgrBase.__init__(self, level, entId)
         
         
         # load the model
         # load the model
         self.geom = loader.loadModel(self.modelFilename)
         self.geom = loader.loadModel(self.modelFilename)
 
 
+        # modelZoneNum -> zone entId
+        self.level.zoneNum2entId = {}
+        # modelZoneNum -> network zoneId
+        self.level.zoneNum2zoneId = {}
+        # zone entId -> network zoneId
+        self.level.zoneEntId2zoneId = {}
+
+        # listen for every zone creation
+        self.accept(self.level.getEntityOfTypeCreateEvent('zone'),
+                    self.handleZoneCreated)
+
     def destroy(self):
     def destroy(self):
-        del self.level.levelMgr
+        del self.level.zoneIds
+        del self.level.zoneEntId2zoneId
+        del self.level.zoneNum2zoneId
+        del self.level.zoneNum2entId
         self.geom.removeNode()
         self.geom.removeNode()
         del self.geom
         del self.geom
-        Entity.Entity.destroy(self)
+        LevelMgrBase.LevelMgrBase.destroy(self)
+
+    def handleZoneCreated(self, entId):
+        zoneEnt = self.level.getEntity(entId)
+
+        # register the zone's info in the tables
+        # right off the bat, we have the zone's modelZoneNum and entId
+        self.level.zoneNum2entId[zoneEnt.modelZoneNum] = entId
+
+        # we can assume that we have a complete list of zoneIds in
+        # self.level.zoneIds. As each zone entity is created, set up
+        # as if we have all of the zone entities. This allows dynamic
+        # zone entity creation and deletion during editing.
+        # TODO: we should delay this until all zone entities have been
+        # created on level init
+        self.privAssignZoneIds()
+
+        # listen for the zone's destruction
+        self.accept(self.level.getEntityDestroyEvent(entId),
+                    Functor(self.handleZoneDestroy, entId))
+
+    def handleZoneDestroy(self, entId):
+        zoneEnt = self.level.getEntity(entId)
+        # unregister the zone from the maps
+        del self.level.zoneNum2entId[zoneEnt.modelZoneNum]
+        del self.level.zoneNum2zoneId[zoneEnt.modelZoneNum]
+        del self.level.zoneEntId2zoneId[entId]
+        # reassign the zoneIds (we may not need to do this, if all of the
+        # other entities already have their correct zoneId...?)
+        self.privAssignZoneIds()
+
+    def privAssignZoneIds(self):
+        """assign zoneIds from self.level.zoneIds, according to the zones
+        that are registered so far in self.level.zoneNum2entId"""
+        # sort the model zoneNums
+        modelZoneNums = self.level.zoneNum2entId.keys()
+        modelZoneNums.sort()
+
+        # dole out the zoneIds, in increasing order of zoneNum
+        for i in range(len(modelZoneNums)):
+            zoneNum = modelZoneNums[i]
+            entId = self.level.zoneNum2entId[zoneNum]
+            zoneEnt = self.level.getEntity(entId)
+            zoneEnt.setZoneId(self.level.zoneIds[i])
+
+        # the zoneIds have shifted. update the tables
+        for entId in self.level.zoneNum2entId.values():
+            zoneEnt = self.level.getEntity(entId)
+            zoneId = zoneEnt.getZoneId()
+            self.level.zoneNum2zoneId[zoneEnt.modelZoneNum] = zoneId
+            self.level.zoneEntId2zoneId[entId] = zoneId

+ 63 - 5
direct/src/level/LevelMgrAI.py

@@ -1,10 +1,68 @@
 """LevelMgrAI module: contains the LevelMgrAI class"""
 """LevelMgrAI module: contains the LevelMgrAI class"""
 
 
-import Entity
+from PythonUtil import Functor
+import LevelMgrBase
 
 
-class LevelMgrAI(Entity.Entity):
+class LevelMgrAI(LevelMgrBase.LevelMgrBase):
     """This class manages editable AI level attributes"""
     """This class manages editable AI level attributes"""
     def __init__(self, level, entId):
     def __init__(self, level, entId):
-        Entity.Entity.__init__(self, level, entId)
-        self.level.levelMgr = self
-        
+        LevelMgrBase.LevelMgrBase.__init__(self, level, entId)
+
+        # modelZoneNum -> zone entId
+        self.level.zoneNum2entId = {}
+        # modelZoneNum -> network zoneId
+        self.level.zoneNum2zoneId = {}
+        # zone entId -> network zoneId
+        self.level.zoneEntId2zoneId = {}
+        # list of network zoneIDs, sorted by modelZoneNum
+        self.level.zoneIds = []
+
+        # listen for every zone creation
+        self.accept(self.level.getEntityOfTypeCreateEvent('zone'),
+                    self.handleZoneCreated)
+
+    def destroy(self):
+        del self.level.zoneIds
+        del self.level.zoneEntId2zoneId
+        del self.level.zoneNum2zoneId
+        del self.level.zoneNum2entId
+        LevelMgrBase.LevelMgrBase.destroy(self)
+
+    def handleZoneCreated(self, entId):
+        zoneEnt = self.level.getEntity(entId)
+
+        # register the zone's info in the tables
+        self.level.zoneNum2entId[zoneEnt.modelZoneNum] = entId
+        self.level.zoneNum2zoneId[zoneEnt.modelZoneNum] = zoneEnt.getZoneId()
+        self.level.zoneEntId2zoneId[entId] = zoneEnt.getZoneId()
+
+        # TODO: we should delay this until all zone entities have been
+        # created on level init
+        self.privCreateSortedZoneIdList()
+
+        # listen for the zone's destruction
+        self.accept(self.level.getEntityDestroyEvent(entId),
+                    Functor(self.handleZoneDestroy, entId))
+
+    def handleZoneDestroy(self, entId):
+        zoneEnt = self.level.getEntity(entId)
+        # unregister the zone from the maps
+        del self.level.zoneNum2entId[zoneEnt.modelZoneNum]
+        del self.level.zoneNum2zoneId[zoneEnt.modelZoneNum]
+        del self.level.zoneEntId2zoneId[entId]
+        # recreate the sorted network zoneId list
+        self.privCreateSortedZoneIdList()
+
+    def privCreateSortedZoneIdList(self):
+        # sort the model zoneNums
+        modelZoneNums = self.level.zoneNum2entId.keys()
+        modelZoneNums.sort()
+
+        # create a list of network zoneIds, ordered by their corresponding
+        # sorted model zoneNum values
+        self.level.zoneIds = []
+        for zoneNum in modelZoneNums:
+            self.level.zoneIds.append(self.level.zoneNum2zoneId[zoneNum])
+
+        # TODO: if we just added or removed a zone entity AFTER the level
+        # was initialized, we need to re-send the zoneId list

+ 14 - 0
direct/src/level/LevelMgrBase.py

@@ -0,0 +1,14 @@
+"""LevelMgrBase module: contains the LevelMgrBase class"""
+
+import Entity
+
+class LevelMgrBase(Entity.Entity):
+    """This class contains LevelMgr code shared by the AI and client"""
+    def __init__(self, level, entId):
+        Entity.Entity.__init__(self, level, entId)
+        self.level.levelMgr = self
+
+    def destroy(self):
+        del self.level.levelMgr
+        Entity.Entity.destroy(self)
+        self.ignoreAll()

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

@@ -1,11 +1,11 @@
 """ZoneEntity module: contains the ZoneEntity class"""
 """ZoneEntity module: contains the ZoneEntity class"""
 
 
-import Entity
+import ZoneEntityBase
 import BasicEntities
 import BasicEntities
 
 
-class ZoneEntity(Entity.Entity, BasicEntities.NodePathAttribs):
+class ZoneEntity(ZoneEntityBase.ZoneEntityBase, BasicEntities.NodePathAttribs):
     def __init__(self, level, entId):
     def __init__(self, level, entId):
-        Entity.Entity.__init__(self, level, entId)
+        ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId)
 
 
         self.nodePath = self.level.getZoneNode(self.modelZoneNum)
         self.nodePath = self.level.getZoneNode(self.modelZoneNum)
         self.initNodePathAttribs(doReparent=0)
         self.initNodePathAttribs(doReparent=0)

+ 17 - 5
direct/src/level/ZoneEntityAI.py

@@ -1,9 +1,21 @@
 """ZoneEntityAI module: contains the ZoneEntityAI class"""
 """ZoneEntityAI module: contains the ZoneEntityAI class"""
 
 
-import Entity
+import ZoneEntityBase
 
 
-class ZoneEntityAI(Entity.Entity):
+class ZoneEntityAI(ZoneEntityBase.ZoneEntityBase):
     def __init__(self, level, entId):
     def __init__(self, level, entId):
-        Entity.Entity.__init__(self, level, entId)
-        # allocate a network zoneId for each zone
-        self.level.allocateZoneId()
+        ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId)
+
+        if self.isUberZone():
+            print 'uberZone'
+            # the uberzone is already allocated
+            self.setZoneId(self.level.uberZoneId)
+        else:
+            # allocate a network zoneId for this zone
+            # there is error checking in air.allocateZone
+            self.setZoneId(self.level.air.allocateZone())
+        
+    def destroy(self):
+        if not self.isUberZone():
+            self.level.air.deallocateZone(self.getZoneId())
+        ZoneEntityBase.ZoneEntityBase.destroy(self)

+ 23 - 0
direct/src/level/ZoneEntityBase.py

@@ -0,0 +1,23 @@
+"""ZoneEntityBase module: contains the ZoneEntityBase class"""
+
+import Entity
+import Level
+
+class ZoneEntityBase(Entity.Entity):
+    def __init__(self, level, entId):
+        Entity.Entity.__init__(self, level, entId)
+        self.zoneId = None
+
+    def destroy(self):
+        del self.zoneId
+        Entity.Entity.destroy(self)
+
+    def isUberZone(self):
+        return self.entId == Level.Level.UberZoneEntId
+
+    def setZoneId(self, zoneId):
+        """set the network zoneId that this zone entity corresponds to"""
+        self.zoneId = zoneId
+
+    def getZoneId(self):
+        return self.zoneId