Browse Source

added SpecUtil funcs for creating and adapting specs to new level models

Darren Ranalli 22 years ago
parent
commit
be086b33d7

+ 25 - 24
direct/src/level/DistributedLevel.py

@@ -10,6 +10,7 @@ import DirectNotifyGlobal
 import EntityCreator
 import OnscreenText
 import Task
+import LevelUtil
 
 class DistributedLevel(DistributedObject.DistributedObject,
                        Level.Level):
@@ -181,34 +182,13 @@ class DistributedLevel(DistributedObject.DistributedObject,
 
     def __handleLevelMgrCreated(self):
         # as soon as the levelMgr has been created, load up the model
-        # and extract zone info
+        # and extract zone info. We need to do this before any entities
+        # get parented to the level!
         levelMgr = self.getEntity(LevelConstants.LevelMgrEntId)
         self.geom = levelMgr.geom
 
-        def findNumberedNodes(baseString, model=self.geom, self=self):
-            # finds nodes whose name follows the pattern 'baseString#'
-            # where there are no characters after #
-            # returns dictionary that maps # to node
-            potentialNodes = model.findAllMatches(
-                '**/%s*' % baseString).asList()
-            num2node = {}
-            for potentialNode in potentialNodes:
-                name = potentialNode.getName()
-                DistributedLevel.notify.debug('potential match for %s: %s' %
-                                  (baseString, name))
-                try:
-                    num = int(name[len(baseString):])
-                except:
-                    continue
-                
-                num2node[num] = potentialNode
-
-            return num2node
-
         # find the zones in the model and fix them up
-        self.zoneNum2node = findNumberedNodes('Zone')
-        # add the UberZone to the table
-        self.zoneNum2node[0] = self.geom
+        self.zoneNum2node = LevelUtil.getZoneNum2Node(self.geom)
 
         self.zoneNums = self.zoneNum2node.keys()
         self.zoneNums.sort()
@@ -250,6 +230,27 @@ class DistributedLevel(DistributedObject.DistributedObject,
         dw.setH(0)
 
         # find the doorway nodes
+        # this is going to go away soon.
+        def findNumberedNodes(baseString, model=self.geom, self=self):
+            # finds nodes whose name follows the pattern 'baseString#'
+            # where there are no characters after #
+            # returns dictionary that maps # to node
+            potentialNodes = model.findAllMatches(
+                '**/%s*' % baseString).asList()
+            num2node = {}
+            for potentialNode in potentialNodes:
+                name = potentialNode.getName()
+                DistributedLevel.notify.debug('potential match for %s: %s' %
+                                  (baseString, name))
+                try:
+                    num = int(name[len(baseString):])
+                except:
+                    continue
+                
+                num2node[num] = potentialNode
+
+            return num2node
+
         self.doorwayNum2Node = findNumberedNodes('Doorway')
 
     def announceGenerate(self):

+ 2 - 0
direct/src/level/LevelConstants.py

@@ -6,3 +6,5 @@ UberZoneNum = 0
 LevelMgrEntId = 10
 
 EditMgrEntId = 20
+
+ZoneEntIdStart = 100

+ 8 - 6
direct/src/level/LevelSpec.py

@@ -16,22 +16,24 @@ class LevelSpec:
     
     def __init__(self, spec=None, scenario=0):
         """spec must be passed in as a python module or a dictionary"""
+        newSpec = 0
         if type(spec) is types.ModuleType:
             self.specDict = spec.levelSpec
-        else:
+            if __debug__:
+                self.setFilename(spec.__file__)
+        elif type(spec) is types.DictType:
             # we need this for repr/eval-ing LevelSpecs
-            assert type(spec) is types.DictType
             self.specDict = spec
-
-        if __debug__:
-            newSpec = 0
-            if self.specDict == None:
+        elif spec is None:
+            if __debug__:
                 newSpec = 1
                 self.specDict = {
                     'globalEntities': {},
                     'scenarios': [[{}, 1]],
                     }
 
+        assert hasattr(self, 'specDict')
+
         # this maps an entId to the dict that holds its spec;
         # entities are either in the global dict or a scenario dict
         # update the map of entId to spec dict

+ 63 - 4
direct/src/level/SpecUtil.py

@@ -2,13 +2,14 @@
 
 import LevelSpec
 import LevelConstants
+import LevelUtil
+from PythonUtil import list2dict
 
 def makeNewSpec(filename, modelPath):
     """call this to create a new level spec for the level model at 'modelPath'.
     Spec will be saved as 'filename'"""
     spec = LevelSpec.LevelSpec()
-    spec.doSetAttrib(LevelConstants.LevelMgrEntId,
-                     'modelFilename', modelPath)
+    privUpdateSpec(spec, modelPath)
     spec.saveToDisk(filename, makeBackup=0)
 
 def updateSpec(specModule, modelPath=None):
@@ -16,10 +17,68 @@ def updateSpec(specModule, modelPath=None):
     model. If the level model has a new path, pass it in as 'modelPath'.
     specModule must be a Python module"""
     spec = LevelSpec.LevelSpec(specModule)
+    privUpdateSpec(spec, modelPath)
+    spec.saveToDisk()
+
+def privUpdateSpec(spec, modelPath=None):
+    """internal: take a spec and update it to match its level model"""
     if modelPath is None:
         modelPath = spec.getEntitySpec(
             LevelConstants.LevelMgrEntId)['modelFilename']
+    else:
+        spec.doSetAttrib(LevelConstants.LevelMgrEntId,
+                         'modelFilename', modelPath)
 
-    # ...
+    # load the model
+    model = loader.loadModel(modelPath)
+    # get the model's zone info
+    modelZoneNum2node = LevelUtil.getZoneNum2Node(model)
+    modelZoneNums = modelZoneNum2node.keys()
 
-    spec.saveToDisk()
+    # what zone entities do we have specs for?
+    type2ids = spec.getEntType2ids(spec.getAllEntIds())
+    type2ids.setdefault('zone', [])
+    zoneEntIds = type2ids['zone']
+    zoneEntId2spec = list2dict(zoneEntIds)
+    for entId in zoneEntIds:
+        zoneEntId2spec[entId] = spec.getEntitySpec(entId)
+
+    def removeZoneEntity(entId, spec=spec, zoneEntIds=zoneEntIds,
+                         zoneEntId2spec=zoneEntId2spec):
+        spec.removeEntity(entId)
+        zoneEntIds.remove(entId)
+        del zoneEntId2spec[entId]
+
+    # create dict of zoneNum -> entId
+    zoneNum2entId = {}
+    for entId in list(zoneEntIds):
+        zoneNum = zoneEntId2spec[entId]['modelZoneNum']
+        if zoneNum in zoneNum2entId:
+            print ('multiple zone entities reference zoneNum %s; removing '
+                   'entity %s' % (zoneNum, entId))
+            removeZoneEntity(entId)
+            continue
+        zoneNum2entId[zoneNum] = entId
+
+    # prune zone entities that reference zones that no longer exist
+    for entId in list(zoneEntIds):
+        zoneNum = zoneEntId2spec[entId]['modelZoneNum']
+        if zoneNum not in modelZoneNums:
+            print 'zone %s no longer exists; removing entity %s' % (
+                zoneNum, entId)
+            removeZoneEntity(entId)
+            del zoneNum2entId[zoneNum]
+            
+    # add new zone entities for new zones
+    for zoneNum in modelZoneNums:
+        if zoneNum not in zoneNum2entId:
+            entIdDict = list2dict(spec.getAllEntIds())
+            entId = LevelConstants.ZoneEntIdStart
+            # we could do better than linear
+            while entId in entIdDict:
+                entId += 1
+
+            print 'adding entity %s for new zone %s' % (entId, zoneNum)
+            spec.insertEntity(entId, 'zone', LevelConstants.UberZoneEntId)
+            spec.doSetAttrib(entId, 'name', 'zone%s' % zoneNum)
+            spec.doSetAttrib(entId, 'modelZoneNum', zoneNum)