added 'permanent' entity types, fixed editor bugs
This commit is contained in:
parent
552291e85b
commit
cf7a1d1427
|
|
@ -156,7 +156,12 @@ class DistributedLevel(DistributedObject.DistributedObject,
|
|||
assert len(self.parent2ChildIds) == 0
|
||||
# make sure the zoneNums from the model match the zoneNums from
|
||||
# the zone entities
|
||||
assert sameElements(self.zoneNums, self.zoneNum2entId.keys())
|
||||
modelZoneNums = self.zoneNums
|
||||
entityZoneNums = self.zoneNum2entId.keys()
|
||||
if not sameElements(modelZoneNums, entityZoneNums):
|
||||
DistributedLevel.notify.error(
|
||||
'model zone nums (%s) do not match entity zone nums (%s)' %
|
||||
(modelZoneNums, entityZoneNums))
|
||||
|
||||
# load stuff
|
||||
self.initVisibility()
|
||||
|
|
@ -177,7 +182,8 @@ class DistributedLevel(DistributedObject.DistributedObject,
|
|||
def __handleLevelMgrCreated(self):
|
||||
# as soon as the levelMgr has been created, load up the model
|
||||
# and extract zone info
|
||||
self.geom = self.levelMgr.geom
|
||||
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#'
|
||||
|
|
@ -389,6 +395,11 @@ class DistributedLevel(DistributedObject.DistributedObject,
|
|||
if zoneNum == self.curZoneNum:
|
||||
return
|
||||
|
||||
if zoneNum not in self.zoneNum2entId:
|
||||
DistributedLevel.notify.error(
|
||||
'no ZoneEntity for this zone (%s)!!' % zoneNum)
|
||||
return
|
||||
|
||||
zoneEntId = self.zoneNum2entId[zoneNum]
|
||||
zoneSpec = self.levelSpec.getEntitySpec(zoneEntId)
|
||||
# use dicts to efficiently ensure that there are no duplicates
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class EditMgrAI(EditMgrBase.EditMgrBase):
|
|||
entIdDict = list2dict(entIds)
|
||||
|
||||
# dumb linear search for now
|
||||
# make this smarter (cache last-allocated id)
|
||||
# TODO: make this smarter (cache last-allocated id)
|
||||
# Note that this uses the ID range associated with the
|
||||
# AI's username, not the username of the user who requested
|
||||
# the new entity.
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ class Entity(DirectObject):
|
|||
self.initializeEntity(level, entId)
|
||||
|
||||
def initializeEntity(self, level, entId):
|
||||
"""Distributed entities don't know their level or entId values
|
||||
until they've been generated, so they call this after they've
|
||||
been generated. At that point, the entity is good to go."""
|
||||
"""Distributed entities on the client don't know their level or
|
||||
entId values until they've been generated, so they call this
|
||||
after they've been generated. At that point, the entity is good
|
||||
to go."""
|
||||
self.level = level
|
||||
self.entId = entId
|
||||
if (self.level is not None) and (self.entId is not None):
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ import ModelEntity
|
|||
|
||||
# some useful constructor functions
|
||||
# ctor functions must take (level, entId)
|
||||
# and they must return the entity that was created, or 'nothing'
|
||||
def nothing(*args):
|
||||
"""For entities that don't need to be created by the client"""
|
||||
return None
|
||||
return 'nothing'
|
||||
|
||||
class EntityCreator(EntityCreatorBase.EntityCreatorBase):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from PythonUtil import Functor
|
|||
# some useful constructor functions
|
||||
# ctor functions for entities must take
|
||||
# (level, entId, zoneId)
|
||||
# and they must return the entity that was created, or 'nothing'
|
||||
|
||||
# this func creates distributed entities whose constructors take
|
||||
# (air, level doId, entId)
|
||||
|
|
@ -25,11 +26,12 @@ def createDistributedEntity(AIclass, level, entId, zoneId):
|
|||
def createLocalEntity(AIclass, level, entId, zoneId):
|
||||
"""create a local entity"""
|
||||
ent = AIclass(level, entId)
|
||||
return ent
|
||||
|
||||
# take any number of args to support local and distributed entities
|
||||
def nothing(*args):
|
||||
"""Create entity that doesn't have a server side representation."""
|
||||
return None
|
||||
return 'nothing'
|
||||
|
||||
class EntityCreatorAI(EntityCreatorBase.EntityCreatorBase):
|
||||
"""This class is responsible for creating instances of Entities on the AI.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ class EntityCreatorBase:
|
|||
(entType, entId))
|
||||
|
||||
# inheritor must define doCreateEntity
|
||||
return self.doCreateEntity(self.entType2Ctor[entType], entId)
|
||||
ent = self.doCreateEntity(self.entType2Ctor[entType], entId)
|
||||
assert ent is not None # must be Entity or 'nothing'
|
||||
return ent
|
||||
|
||||
def getEntityTypes(self):
|
||||
"""by definition, this object knows the full list of entity types
|
||||
|
|
|
|||
|
|
@ -26,8 +26,14 @@ class EntityTypeDesc:
|
|||
self.attribDescDict[attribName] = desc
|
||||
|
||||
def isConcrete(self):
|
||||
""" means that entity of this exact type can be created """
|
||||
return not self.__class__.__dict__.has_key('abstract')
|
||||
|
||||
def isPermanent(self):
|
||||
""" means that entity of this exact type cannot be inserted or
|
||||
removed in the editor """
|
||||
return self.__class__.__dict__.has_key('permanent')
|
||||
|
||||
def getOutputType(self):
|
||||
return self.output
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ class EntityTypeRegistry:
|
|||
self.output2typeNames.setdefault(outputType, [])
|
||||
self.output2typeNames[outputType].append(typename)
|
||||
|
||||
# create list of permanent entity typenames (entity types that cannot
|
||||
# be inserted or removed in the editor)
|
||||
self.permanentTypeNames = []
|
||||
for typename, typeDesc in self.entTypeName2typeDesc.items():
|
||||
if typeDesc.isPermanent():
|
||||
assert typeDesc.isConcrete()
|
||||
self.permanentTypeNames.append(typename)
|
||||
|
||||
# create mapping of entity typename (abstract or concrete) to list
|
||||
# of entity typenames are concrete and are of that type or derive
|
||||
# from that type
|
||||
|
|
@ -80,6 +88,9 @@ class EntityTypeRegistry:
|
|||
def isDerivedAndBase(self, entType, baseEntType):
|
||||
return entType in self.getDerivedTypeNames(baseEntType)
|
||||
|
||||
def getPermanentTypeNames(self):
|
||||
return self.permanentTypeNames
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
def __repr__(self):
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class Entity(EntityTypeDesc):
|
|||
|
||||
class LevelMgr(Entity):
|
||||
type = 'levelMgr'
|
||||
permanent = 1
|
||||
attribs = (
|
||||
('cogLevel', 0, 'int', {'min':0, 'max':11}),
|
||||
('cogTrack', 'c', 'choice', {'choiceSet':['c','s','l','m']}),
|
||||
|
|
@ -22,6 +23,7 @@ class LevelMgr(Entity):
|
|||
|
||||
class EditMgr(Entity):
|
||||
type = 'editMgr'
|
||||
permanent = 1
|
||||
attribs = (
|
||||
('requestSave', None),
|
||||
('requestNewEntity', None),
|
||||
|
|
@ -29,18 +31,6 @@ class EditMgr(Entity):
|
|||
('removeEntity', None),
|
||||
)
|
||||
|
||||
class LogicGate(Entity):
|
||||
type = 'logicGate'
|
||||
output = 'bool'
|
||||
attribs = (
|
||||
('input1Event', 0, 'entId', {'output':'bool'}),
|
||||
('input2Event', 0, 'entId', {'output':'bool'}),
|
||||
('isInput1', 0, 'bool'),
|
||||
('isInput2', 0, 'bool'),
|
||||
('logicType', 'or', 'choice',
|
||||
{'choiceSet':['or','and','xor','nand','nor','xnor']}),
|
||||
)
|
||||
|
||||
class Nodepath(Entity):
|
||||
type = 'nodepath'
|
||||
attribs = (
|
||||
|
|
@ -51,6 +41,7 @@ class Nodepath(Entity):
|
|||
|
||||
class Zone(Nodepath):
|
||||
type = 'zone'
|
||||
permanent = 1
|
||||
delAttribs = (
|
||||
'parentEntId',
|
||||
'pos',
|
||||
|
|
@ -58,10 +49,22 @@ class Zone(Nodepath):
|
|||
)
|
||||
attribs = (
|
||||
('description', '', 'string'),
|
||||
('modelZoneNum', None, 'int'),
|
||||
('modelZoneNum', -1, 'int'),
|
||||
('visibility', [], 'visZoneList'),
|
||||
)
|
||||
|
||||
class LogicGate(Entity):
|
||||
type = 'logicGate'
|
||||
output = 'bool'
|
||||
attribs = (
|
||||
('input1Event', 0, 'entId', {'output':'bool'}),
|
||||
('input2Event', 0, 'entId', {'output':'bool'}),
|
||||
('isInput1', 0, 'bool'),
|
||||
('isInput2', 0, 'bool'),
|
||||
('logicType', 'or', 'choice',
|
||||
{'choiceSet':['or','and','xor','nand','nor','xnor']}),
|
||||
)
|
||||
|
||||
class CutScene(Entity):
|
||||
type = 'cutScene'
|
||||
output = 'bool'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import DirectNotifyGlobal
|
|||
import string
|
||||
import LevelConstants
|
||||
from PythonUtil import lineInfo, uniqueElements
|
||||
import types
|
||||
|
||||
"""
|
||||
Any data that can be edited by a level editor must be represented as
|
||||
|
|
@ -62,20 +63,26 @@ class Level:
|
|||
# TODO: maybe we should leave this to a subclass or the level user
|
||||
self.createAllEntities(priorityTypes=['levelMgr','zone'])
|
||||
|
||||
# check on the singleton entities
|
||||
# we make our own references to them rather than expect them to
|
||||
# create the references so that the editor can create dummy
|
||||
# do-nothing entities
|
||||
|
||||
# there should be one and only one levelMgr
|
||||
assert len(self.entType2ids['levelMgr']) == 1
|
||||
self.levelMgrEntity = self.getEntity(self.entType2ids['levelMgr'][0])
|
||||
assert self.levelMgrEntity.entId == LevelConstants.LevelMgrEntId
|
||||
assert self.entType2ids['levelMgr'][0] == LevelConstants.LevelMgrEntId
|
||||
self.levelMgrEntity = self.getEntity(LevelConstants.LevelMgrEntId)
|
||||
|
||||
if __debug__:
|
||||
# there should be one and only one editMgr
|
||||
assert len(self.entType2ids['editMgr']) == 1
|
||||
self.editMgrEntity = self.getEntity(self.entType2ids['editMgr'][0])
|
||||
assert self.editMgrEntity.entId == LevelConstants.EditMgrEntId
|
||||
assert self.entType2ids['editMgr'][0] == \
|
||||
LevelConstants.EditMgrEntId
|
||||
self.editMgrEntity = self.getEntity(LevelConstants.EditMgrEntId)
|
||||
|
||||
# make sure the uberzone is there
|
||||
# there should be one and only one UberZone
|
||||
assert LevelConstants.UberZoneEntId in self.entType2ids['zone']
|
||||
self.UberZoneEntity = self.getEntity(LevelConstants.UberZoneEntId)
|
||||
self.uberZoneEntity = self.getEntity(LevelConstants.UberZoneEntId)
|
||||
|
||||
self.initialized = 1
|
||||
|
||||
|
|
@ -153,7 +160,7 @@ class Level:
|
|||
# NOTE: the entity is not considered to really be created until
|
||||
# it has all of its initial spec data; see 'initializeEntity'
|
||||
# below.
|
||||
if entity is not None:
|
||||
if entity is not 'nothing':
|
||||
assert uniqueElements(self.createdEntIds)
|
||||
assert entId not in self.createdEntIds
|
||||
self.createdEntIds.append(entId)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ 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()
|
||||
|
|
|
|||
|
|
@ -4,24 +4,24 @@ import DirectNotifyGlobal
|
|||
from PythonUtil import list2dict, uniqueElements
|
||||
import string
|
||||
import LevelConstants
|
||||
import types
|
||||
if __debug__:
|
||||
import os
|
||||
|
||||
if __debug__:
|
||||
def makeNewSpec(filename, modelPath, ):
|
||||
spec = LevelSpec()
|
||||
spec.doSetAttrib(LevelConstants.LevelMgrEntId,
|
||||
'modelFilename', modelPath)
|
||||
spec.saveToDisk(filename, makeBackup=0)
|
||||
|
||||
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"""
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("LevelSpec")
|
||||
|
||||
def __init__(self, specDict=None, scenario=0):
|
||||
self.specDict = specDict
|
||||
def __init__(self, spec=None, scenario=0):
|
||||
"""spec must be passed in as a python module or a dictionary"""
|
||||
if type(spec) is types.ModuleType:
|
||||
self.specDict = spec.levelSpec
|
||||
else:
|
||||
# we need this for repr/eval-ing LevelSpecs
|
||||
assert type(spec) is types.DictType
|
||||
self.specDict = spec
|
||||
|
||||
if __debug__:
|
||||
newSpec = 0
|
||||
|
|
@ -179,6 +179,8 @@ class LevelSpec:
|
|||
if self.hasLevel():
|
||||
# notify the level
|
||||
self.level.handleEntityInsert(entId)
|
||||
else:
|
||||
LevelSpec.notify.warning('no level to be notified of insertion')
|
||||
|
||||
def removeEntity(self, entId):
|
||||
LevelSpec.notify.info('removing entity %s' % entId)
|
||||
|
|
@ -187,6 +189,8 @@ class LevelSpec:
|
|||
if self.hasLevel():
|
||||
# notify the level
|
||||
self.level.handleEntityRemove(entId)
|
||||
else:
|
||||
LevelSpec.notify.warning('no level to be notified of removal')
|
||||
|
||||
# remove the entity's spec
|
||||
dict = self.entId2specDict[entId]
|
||||
|
|
|
|||
|
|
@ -96,12 +96,11 @@ class LogicGateAI(Entity.Entity, PandaObject.PandaObject):
|
|||
|
||||
def destroy(self):
|
||||
assert(self.debugPrint("destroy()"))
|
||||
self.ignore(self.input1)
|
||||
self.input1 = None
|
||||
self.ignore(self.input2)
|
||||
self.input2 = None
|
||||
self.ignore(self.input1Event)
|
||||
self.input1Event = None
|
||||
self.ignore(self.input2Event)
|
||||
self.input2Event = None
|
||||
Entity.Entity.destroy(self)
|
||||
PandaObject.PandaObject.destroy(self)
|
||||
|
||||
def setLogicType(self, logicType):
|
||||
assert(self.debugPrint("setLogicType(logicType=%s)"%(logicType,)))
|
||||
|
|
@ -111,15 +110,15 @@ class LogicGateAI(Entity.Entity, PandaObject.PandaObject):
|
|||
|
||||
def setIsInput1(self, isTrue):
|
||||
assert(self.debugPrint("setIsInput1(isTrue=%s)"%(isTrue,)))
|
||||
if 1 or (not isTrue) != (not self.input1):
|
||||
# ...the logical state of self.input1 has changed.
|
||||
if 1 or (not isTrue) != (not self.input1Event):
|
||||
# ...the logical state of self.input1Event has changed.
|
||||
self.isInput1=isTrue
|
||||
self.logicTest(self, isTrue, self.isInput2)
|
||||
|
||||
def setIsInput2(self, isTrue):
|
||||
assert(self.debugPrint("setIsInput1(isTrue=%s)"%(isTrue,)))
|
||||
if 1 or (not isTrue) != (not self.input2):
|
||||
# ...the logical state of self.input2 has changed.
|
||||
if 1 or (not isTrue) != (not self.input2Event):
|
||||
# ...the logical state of self.input2Event has changed.
|
||||
self.isInput2=isTrue
|
||||
self.logicTest(self, isTrue, self.isInput1)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
"""SpecUtil module: contains utility functions for creating and managing level specs"""
|
||||
|
||||
import LevelSpec
|
||||
import LevelConstants
|
||||
|
||||
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)
|
||||
spec.saveToDisk(filename, makeBackup=0)
|
||||
|
||||
def updateSpec(specModule, modelPath=None):
|
||||
"""Call this to update an existing levelSpec to work with a new level
|
||||
model. If the level model has a new path, pass it in as 'modelPath'.
|
||||
specModule must be a Python module"""
|
||||
spec = LevelSpec.LevelSpec(specModule)
|
||||
if modelPath is None:
|
||||
modelPath = spec.getEntitySpec(
|
||||
LevelConstants.LevelMgrEntId)['modelFilename']
|
||||
|
||||
# ...
|
||||
|
||||
spec.saveToDisk()
|
||||
Loading…
Reference in New Issue