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

added LevelSpec data wrapper

Darren Ranalli 22 жил өмнө
parent
commit
5c113af66a

+ 7 - 7
direct/src/level/DistributedLevel.py

@@ -64,11 +64,11 @@ class DistributedLevel(DistributedObject.DistributedObject,
     def levelAnnounceGenerate(self):
         pass
 
-    def initializeLevel(self, spec):
-        """subclass should call this as soon as it's located its spec data.
+    def initializeLevel(self, levelSpec):
+        """subclass should call this as soon as it's located its level spec.
         Must be called after obj has been generated."""
         Level.Level.initializeLevel(self, self.doId,
-                                    spec, self.scenarioIndex)
+                                    levelSpec, self.scenarioIndex)
 
         # all of the entities have been created now.
         # there should not be any pending reparents left at this point
@@ -287,7 +287,7 @@ class DistributedLevel(DistributedObject.DistributedObject,
             return
 
         zoneEntId = self.zoneNum2entId[zoneNum]
-        zoneSpec = self.entId2spec[zoneEntId]
+        zoneSpec = self.levelSpec.getEntitySpec(zoneEntId)
         # use dicts to efficiently ensure that there are no duplicates
         visibleZoneNums = list2dict([zoneNum])
         visibleZoneNums.update(list2dict(zoneSpec['visibility']))
@@ -352,12 +352,12 @@ class DistributedLevel(DistributedObject.DistributedObject,
     if __debug__:
         # if someone has edited the level, we'll get the full up-to-date
         # spec in this message
-        def setSpecOverride(self, specStr):
-            if self.spec is not None:
+        def setLevelSpecOverride(self, specStr):
+            if self.levelSpec is not None:
                 return
 
             try:
-                self.spec = eval(specStr)
+                self.levelSpec = eval(specStr)
             except Exception, e:
                 print ('Exception in %s(%s):\n\t%s' %
                        (lineInfo()[2], specStr, e))

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

@@ -17,11 +17,11 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
         Level.Level.__init__(self)
         self.uberZoneId = zoneId
 
-    def generate(self, spec):
+    def generate(self, levelSpec):
         self.notify.debug('generate')
         DistributedObjectAI.DistributedObjectAI.generate(self)
 
-        self.initializeLevel(spec)
+        self.initializeLevel(levelSpec)
 
         self.sendUpdate('setZoneIds', [self.zoneIds])
         self.sendUpdate('setStartTimestamp', [self.startTimestamp])
@@ -32,19 +32,21 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
         self.destroyLevel()
         DistributedObjectAI.DistributedObjectAI.delete(self)
 
-    def initializeLevel(self, spec):
+    def initializeLevel(self, levelSpec):
         # record the level's start time so that we can sync the clients
         self.startTime = globalClock.getRealTime()
         self.startTimestamp = globalClockDelta.localToNetworkTime(
             self.startTime, bits=32)
 
         # choose a scenario
-        wc = WeightedChoice.WeightedChoice(spec['scenarios'], 1)
-        scenario = wc.choose()
-        scenarioIndex = spec['scenarios'].index(scenario)
+        # make list of lists: [(weight, scenarioIndex), ...]
+        lol = zip(levelSpec.getScenarioWeights(),
+                  range(levelSpec.getNumScenarios()))
+        wc = WeightedChoice.WeightedChoice(lol)
+        scenarioIndex = wc.choose()[1]
 
         Level.Level.initializeLevel(self, self.doId,
-                                    spec, scenarioIndex)
+                                    levelSpec, scenarioIndex)
 
     def createEntityCreator(self):
         """Create the object that will be used to create Entities.
@@ -56,7 +58,7 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
         # this func is called before the entity has been created; look
         # 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.levelSpec.getEntitySpec(entId)
         type = spec['type']
         if type == 'zone':
             if not hasattr(self, 'zoneNum2zoneId'):
@@ -85,11 +87,11 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
 
             # send a copy of the entire spec for any new users that
             # might come in
-            ##self.sendUpdate('setSpecOverride', [repr(self.spec)])
+            ##self.sendUpdate('setSpecOverride', [repr(self.levelSpec)])
 
-        def getCurrentSpec(self):
+        def getCurrentLevelSpec(self):
             """returns the complete, current spec, including any edits"""
-            return self.spec
+            return self.levelSpec
 
         """
         def getSpecOverride(self):

+ 11 - 22
direct/src/level/Level.py

@@ -34,31 +34,22 @@ class Level:
     UberZoneEntId = 0
 
     def __init__(self):
-        self.spec = None
+        self.levelSpec = None
 
-    def initializeLevel(self, levelId, spec, scenarioIndex):
+    def initializeLevel(self, levelId, levelSpec, scenarioIndex):
         """ subclass should call this as soon as it has located
         its spec data """
         self.levelId = levelId
-        self.spec = spec
+        self.levelSpec = levelSpec
         self.scenarioIndex = scenarioIndex
 
-        # create a complete set of global and scenario-specific entity specs
-        globalEntities = self.spec['globalEntities']
-        scenarioEntities = self.spec['scenarios'][self.scenarioIndex][0]
-        entId2spec = {}
-        entId2spec.update(globalEntities)
-        entId2spec.update(scenarioEntities)
-        self.entId2spec = entId2spec
+        self.levelSpec.setScenario(self.scenarioIndex)
 
         # create some handy tables
 
         # entity type -> list of entIds
-        self.entType2ids = {}
-        for entId, spec in self.entId2spec.items():
-            entType = spec['type']
-            self.entType2ids.setdefault(entType, [])
-            self.entType2ids[entType].append(entId)
+        self.entType2ids = self.levelSpec.getEntType2ids(
+            self.levelSpec.getAllEntIds())
 
         # there should be one and only one levelMgr
         assert len(self.entType2ids['levelMgr']) == 1
@@ -83,10 +74,8 @@ class Level:
             del self.createdEntities
         if hasattr(self, 'entities'):
             del self.entities
-        if hasattr(self, 'entId2spec'):
-            del self.entId2spec
-        if hasattr(self, 'spec'):
-            del self.spec
+        if hasattr(self, 'levelSpec'):
+            del self.levelSpec
 
     def createEntityCreator(self):
         self.notify.error(
@@ -131,7 +120,7 @@ class Level:
 
     def createEntity(self, entId):
         assert not self.entities.has_key(entId)
-        spec = self.entId2spec[entId]
+        spec = self.levelSpec.getEntitySpec(entId)
         self.notify.debug('creating %s %s' % (spec['type'], entId))
         entity = self.entityCreator.createEntity(entId)
         # NOTE: the entity is not considered to really be created until
@@ -156,7 +145,7 @@ class Level:
         entities; this is called directly by Entity.
         """
         entId = entity.entId
-        spec = self.entId2spec[entId]
+        spec = self.levelSpec.getEntitySpec(entId)
         # on initialization, set items directly on entity
         for key,value in spec.items():
             if key in ('type', 'name', 'comment',):
@@ -170,7 +159,7 @@ class Level:
         return self.entities.get(entId)
 
     def getEntityType(self, entId):
-        return self.entId2spec[entId]['type']
+        return self.levelSpec.getEntityType(entId)
 
     def getZoneId(self, dummy=None, zoneNum=None, entId=None):
         """look up network zoneId by zoneNum or entId"""

+ 203 - 0
direct/src/level/LevelSpec.py

@@ -0,0 +1,203 @@
+"""LevelSpec module: contains the LevelSpec class"""
+
+from PythonUtil import list2dict
+import string
+
+class LevelSpec:
+    """contains spec data for a level, is responsible for handing the data
+    out upon request, as well as recording changes made during editing, and
+    saving out modified spec data"""
+    def __init__(self, specDict, scenario=0):
+        self.specDict = 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
+        self.entId2specDict = {}
+        self.entId2specDict.update(
+            list2dict(self.getGlobalEntIds(),
+                      value=self.privGetGlobalEntityDict()))
+        for i in range(self.getNumScenarios()):
+            self.entId2specDict.update(
+                list2dict(self.getScenarioEntIds(i),
+                          value=self.privGetScenarioEntityDict(i)))
+
+        self.setScenario(scenario)
+
+    def getNumScenarios(self):
+        return len(self.specDict['scenarios'])
+
+    def getScenarioWeights(self):
+        weights = []
+        for entry in self.specDict['scenarios']:
+            weights.append(entry[1])
+        return weights
+
+    def setScenario(self, scenario):
+        assert scenario in range(0, self.getNumScenarios())
+        self.scenario = scenario
+
+    def getScenario(self):
+        return self.scenario
+
+    def getGlobalEntIds(self):
+        return self.privGetGlobalEntityDict().keys()
+
+    def getScenarioEntIds(self, scenario=None):
+        if scenario is None:
+            scenario = self.scenario
+        return self.privGetScenarioEntityDict(scenario).keys()
+
+    def getAllEntIds(self):
+        return self.getGlobalEntIds() + self.getScenarioEntIds()
+
+    def getEntitySpec(self, entId):
+        assert entId in self.entId2specDict
+        specDict = self.entId2specDict[entId]
+        return specDict[entId]
+
+    def getEntityType(self, entId):
+        return self.getEntitySpec(entId)['type']
+
+    def getEntType2ids(self, entIds):
+        """given list of entIds, return dict of entType 2 entIds"""
+        entType2ids = {}
+        for entId in entIds:
+            type = self.getEntityType(entId)
+            entType2ids.setdefault(type, [])
+            entType2ids[type].append(entId)
+        return entType2ids
+
+    # private support functions to abstract dict structure
+    def privGetGlobalEntityDict(self):
+        return self.specDict['globalEntities']
+
+    def privGetScenarioEntityDict(self, scenario):
+        return self.specDict['scenarios'][scenario][0]
+
+    if __debug__:
+        def setAttribChange(self, entId, attrib, value):
+            pass
+
+        def getSpecImportsModuleName(self):
+            # name of module that should be imported by spec py file
+            return 'SpecImports'
+
+        def getPrettyString(self):
+            """Returns a string that contains the spec data, nicely formatted.
+            This should be used when writing the spec out to file."""
+            import pprint
+            
+            tabWidth = 4
+            tab = ' ' * tabWidth
+            # structure names
+            globalEntitiesName = 'GlobalEntities'
+            scenarioEntitiesName = 'Scenario%s'
+            scenarioWeightName = 'Scenarios'
+            topLevelName = 'levelSpec'
+            def getPrettyEntityDictStr(name, dict, tabs=0):
+                def t(n):
+                    return (tabs+n)*tab
+                def sortList(lst, firstElements=[]):
+                    """sort list; elements in firstElements will be put
+                    first, in the order that they appear in firstElements;
+                    rest of elements will follow, sorted"""
+                    elements = list(lst)
+                    # put elements in order
+                    result = []
+                    for el in firstElements:
+                        if el in elements:
+                            result.append(el)
+                            elements.remove(el)
+                    elements.sort()
+                    result.extend(elements)
+                    return result
+   
+                firstTypes = ('levelMgr', 'zone',)
+                firstAttribs = ('type', 'name', 'comment', 'parent',
+                                'pos', 'x', 'y', 'z',
+                                'hpr', 'h', 'p', 'r',
+                                'scale', 'sx', 'sy', 'sz',
+                                'color',
+                                'model',
+                                )
+                str = t(0)+'%s = {\n' % name
+                # get list of types
+                entIds = dict.keys()
+                entType2ids = self.getEntType2ids(entIds)
+                # put types in order
+                types = sortList(entType2ids.keys(), firstTypes)
+                for type in types:
+                    str += t(1)+'# %s\n' % string.upper(type)
+                    entIds = entType2ids[type]
+                    entIds.sort()
+                    for entId in entIds:
+                        str += t(1)+'%s: {\n' % entId
+                        spec = dict[entId]
+                        attribs = sortList(spec.keys(), firstAttribs)
+                        for attrib in attribs:
+                            str += t(2)+"'%s': %s,\n" % (attrib,
+                                                         repr(spec[attrib]))
+                        str += t(2)+'},\n'
+                        
+                str += t(1)+'}\n'
+                return str
+            def getPrettyScenarioWeightTableStr(tabs=0, self=self):
+                def t(n):
+                    return (tabs+n)*tab
+                str  = t(0)+'%s = [\n' % scenarioWeightName
+                for i in range(self.getNumScenarios()):
+                    str += t(1)+'[%s, %s],\n' % (scenarioEntitiesName % i,
+                                                  self.getScenarioWeights()[i])
+                str += t(1)+']\n'
+                return str
+            def getPrettyTopLevelDictStr(tabs=0):
+                def t(n):
+                    return (tabs+n)*tab
+                str  = t(0)+'%s = {\n' % topLevelName
+                str += t(1)+"'globalEntities': %s,\n" % globalEntitiesName
+                str += t(1)+"'scenarios': %s,\n" % scenarioWeightName
+                str += t(1)+'}\n'
+                return str
+            
+            str  = 'from %s import *\n' % self.getSpecImportsModuleName()
+            str += '\n'
+
+            # add the global entities
+            str += getPrettyEntityDictStr('GlobalEntities',
+                                          self.privGetGlobalEntityDict())
+            str += '\n'
+
+            # add the scenario entities
+            numScenarios = self.getNumScenarios()
+            for i in range(numScenarios):
+                str += getPrettyEntityDictStr('Scenario%s' % i,
+                                              self.privGetScenarioEntityDict(i))
+                str += '\n'
+
+            # add the scenario weight table
+            str += getPrettyScenarioWeightTableStr()
+            str += '\n'
+
+            # add the top-level table
+            str += getPrettyTopLevelDictStr()
+
+            self.testPrettyString(prettyString=str)
+
+            return str
+
+        def testPrettyString(self, prettyString=None):
+            # execute the pretty output in our local scope
+            if prettyString is None:
+                prettyString=self.getPrettyString()
+            exec(prettyString)
+            assert levelSpec == self.specDict, (
+                'LevelSpec pretty string does not match spec data.\n'
+                'pretty=%s\n'
+                'specData=%s' %
+                (levelSpec, self.specDict)
+                )
+
+        def __repr__(self):
+            return 'LevelSpec(%s, scenario=%s)' % (repr(self.specDict),
+                                                   self.scenario)