added 'permanent' entity types, fixed editor bugs

This commit is contained in:
Darren Ranalli 2003-10-24 22:25:24 +00:00
parent 552291e85b
commit cf7a1d1427
14 changed files with 119 additions and 49 deletions

View File

@ -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

View File

@ -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.

View File

@ -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):

View File

@ -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):
"""

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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'

View File

@ -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)

View File

@ -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()

View File

@ -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]

View File

@ -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)

25
direct/src/level/SpecUtil.py Executable file
View File

@ -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()