added editMgr, entity insertion, removal

This commit is contained in:
Darren Ranalli 2003-10-09 01:28:58 +00:00
parent 60e7fb5596
commit ecdb18879f
10 changed files with 162 additions and 57 deletions

View File

@ -28,7 +28,7 @@ class DistributedEntity(DistributedObject.DistributedObject, Entity.Entity):
self.entId = entId
def announceGenerate(self):
DistributedEntity.notify.debug('announceGenerate')
DistributedEntity.notify.debug('announceGenerate (%s)' % self.entId)
# ask our level obj for our spec data
level = toonbase.tcr.doId2do[self.levelDoId]
@ -39,8 +39,9 @@ class DistributedEntity(DistributedObject.DistributedObject, Entity.Entity):
DistributedObject.DistributedObject.announceGenerate(self)
def disable(self):
DistributedEntity.notify.debug('disable')
DistributedEntity.notify.debug('disable (%s)' % self.entId)
# stop things
self.destroy()
def delete(self):
DistributedEntity.notify.debug('delete')

View File

@ -89,10 +89,11 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI,
# entities
def setAttribChange(self, entId, attribName, valueStr):
value = eval(valueStr)
self.levelSpec.setAttribChange(entId, attribName, value)
# send a copy to the client-side level obj
# send a copy to the client-side level obj FIRST
# (it may be a message that creates an entity)
self.sendUpdate('setAttribChange',
[entId, attribName, valueStr])
self.levelSpec.setAttribChange(entId, attribName, value)
self.modified = 1
self.scheduleSave()

21
direct/src/level/EditMgr.py Executable file
View File

@ -0,0 +1,21 @@
"""EditMgr module: contains the EditMgr class"""
import Entity
class EditMgr(Entity.Entity):
"""This class handles entity/level functionality used by the level editor"""
def __init__(self, level, entId):
Entity.Entity.__init__(self, level, entId)
def destroy(self):
Entity.Entity.destroy(self)
self.ignoreAll()
def setInsertEntity(self, data):
self.level.levelSpec.insertEntity(data['entId'],
data['entType'],
data['parentEntId'],
)
def setRemoveEntity(self, entId):
self.level.levelSpec.removeEntity(entId)

View File

@ -28,6 +28,7 @@ class Entity(DirectObject):
def destroy(self):
self.level.onEntityDestroy(self.entId)
del self.level
del self.entId
def privGetSetter(self, attrib):
setFuncName = 'set%s%s' % (string.upper(attrib[0]), attrib[1:])

View File

@ -3,6 +3,7 @@
import EntityCreatorBase
import BasicEntities
import DirectNotifyGlobal
import EditMgr
import LevelMgr
import ZoneEntity
@ -20,6 +21,7 @@ class EntityCreator(EntityCreatorBase.EntityCreatorBase):
EntityCreatorBase.EntityCreatorBase.__init__(self, level)
self.level = level
self.privRegisterTypes({
'editMgr': EditMgr.EditMgr,
'levelMgr': LevelMgr.LevelMgr,
'logicGate': nothing,
'nodepath': BasicEntities.NodePathEntity,

View File

@ -2,6 +2,7 @@
import EntityCreatorBase
import LogicGateAI
import EditMgr
import LevelMgrAI
import ZoneEntityAI
from PythonUtil import Functor
@ -41,6 +42,7 @@ class EntityCreatorAI(EntityCreatorBase.EntityCreatorBase):
cLE = createLocalEntity
self.privRegisterTypes({
'editMgr': Functor(cLE, EditMgr.EditMgr),
'levelMgr': Functor(cLE, LevelMgrAI.LevelMgrAI),
'logicGate': Functor(cLE, LogicGateAI.LogicGateAI),
'nodepath': nothing,

View File

@ -47,7 +47,7 @@ class EntityTypeRegistry:
attribNames.append(desc.getName())
return attribNames
def getAttribDescs(self, entityTypeName):
def getAttribDescDict(self, entityTypeName):
""" returns dict of attribName -> attribDescriptor """
assert entityTypeName in self.typeName2class
# TODO: precompute this

View File

@ -25,6 +25,13 @@ class LevelMgr(Entity):
('modelFilename', None),
)
class EditMgr(Entity):
type = 'editMgr'
attribs = (
('insertEntity', None),
('removeEntity', None),
)
class LogicGate(Entity):
type = 'logicGate'
attribs = (

View File

@ -45,6 +45,8 @@ class Level:
self.scenarioIndex = scenarioIndex
self.levelSpec.setScenario(self.scenarioIndex)
if __debug__:
self.levelSpec.setLevel(self)
# create some handy tables
@ -57,31 +59,25 @@ class Level:
# make sure the uberzone is there
assert Level.UberZoneEntId in self.entType2ids['zone']
# this list contains the entIds of entities that we have actually
# created, in order of creation
self.createdEntIds = []
# get an entity creator object
self.entityCreator = self.createEntityCreator()
# create all the entities
# TODO: we should leave this to a subclass or the level user
self.createAllEntities(priorityTypes=['levelMgr','zone'])
self.levelSpec.setAttribChangeEventName(self.getAttribChangeEvent())
self.accept(self.getAttribChangeEvent(), self.handleAttribChange)
self.initialized = 1
def isInitialized(self):
return self.initialized
def destroyLevel(self):
self.destroyAllEntities()
self.initialized = 0
if hasattr(self, 'createdEntities'):
# destroy the entities in reverse order
while len(self.createdEntities) > 0:
entity = self.createdEntities.pop()
self.notify.debug('destroying %s entity %s' %
(self.getEntityType(entity.entId),
entity.entId))
entity.destroy()
del self.createdEntities
del self.createdEntIds
if hasattr(self, 'entities'):
del self.entities
if hasattr(self, 'levelSpec'):
@ -97,8 +93,6 @@ class Level:
# this will be filled in as the entities are created and report in
# this includes distributed objects on the client
self.entities = {}
# this list contains the entities that we have actually created
self.createdEntities = []
# get list of all entity types we need to create
entTypes = self.entType2ids.keys()
@ -117,6 +111,22 @@ class Level:
self.onLevelPostCreate()
def destroyAllEntities(self):
# destroy the entities in reverse order
while len(self.createdEntIds) > 0:
entId = self.createdEntIds[-1]
entity = self.getEntity(entId)
if entity is not None:
self.notify.debug('destroying %s %s' % (
self.getEntityType(entId), entId))
# this removes the entId from self.createdEntIds
# in order to support editor-based entity removal
entity.destroy()
else:
self.notify.warning('trying to destroy entity %s, but '
'it is already gone' % entId)
self.createdEntIds.pop()
def createAllEntitiesOfType(self, entType):
"""creates all entities of a given type"""
assert entType in self.entType2ids
@ -129,7 +139,7 @@ class Level:
self.onEntityTypePostCreate(entType)
def createEntity(self, entId):
assert not self.entities.has_key(entId)
assert not entId in self.createdEntIds
spec = self.levelSpec.getEntitySpec(entId)
self.notify.debug('creating %s %s' % (spec['type'], entId))
entity = self.entityCreator.createEntity(entId)
@ -137,13 +147,13 @@ class Level:
# it has all of its initial spec data; see 'initializeEntity'
# below.
if entity is not None:
self.createdEntities.append(entity)
self.createdEntIds.append(entId)
# 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
# note that now DistributedEntity's are responsible for calling
# derived entity __init__ funcs before their presence was announced
# Note that now DistributedEntity's are responsible for calling
# this for themselves
self.onEntityCreate(entId)
@ -163,7 +173,8 @@ class Level:
entity.setAttribInit(key, value)
# entity is initialized, add it to the list of entities
self.entities[entity.entId] = entity
assert not entId in self.entities
self.entities[entId] = entity
def getEntity(self, entId):
return self.entities.get(entId)
@ -243,17 +254,48 @@ class Level:
return 'entityDestroy-%s-%s' % (self.levelId, entId)
def onEntityDestroy(self, entId):
"""Level is about to destroy this entity"""
assert entId in self.entities
# send the entity-destroy event
messenger.send(self.getEntityDestroyEvent(entId))
if __debug__:
def getAttribChangeEvent(self):
return 'attribChange-%s' % self.levelId
del self.entities[entId]
# if we created this entity, remove its entId from the
# createdEntIds list
if len(self.createdEntIds) > 0:
# most often, we are destroying the entities in reverse order
if entId == self.createdEntIds[-1]:
self.createdEntIds.pop()
elif entId in self.createdEntIds:
# this should only happen if someone deleted an entity
# with an editor
self.createdEntIds.remove(entId)
# This handler is called immediately after a new attribute value
# has been set in the level's spec.
if __debug__:
# the level generates these events when the spec changes
def getAttribChangeEventName(self):
return 'attribChange-%s' % self.levelId
def getInsertEntityEventName(self):
return 'insertEntity-%s' % self.levelId
def getRemoveEntityEventName(self):
return 'removeEntity-%s' % self.levelId
# these handlers are called directly by our levelSpec
def handleAttribChange(self, entId, attrib, value):
entity = self.getEntity(entId)
# the entity might be AI- or client-only
if entity is not None:
entity.handleAttribChange(attrib, value)
messenger.send(self.getAttribChangeEventName(),
[entId, attrib, value])
def handleEntityInsert(self, entId):
self.createEntity(entId)
messenger.send(self.getInsertEntityEventName(), [entId])
def handleEntityRemove(self, entId):
messenger.send(self.getRemoveEntityEventName(), [entId])
# if we didn't create it, don't destroy it (probably a distributed
# entity on the client; wait for AI to destroy it)
if entId in self.createdEntIds:
entity = self.getEntity(entId)
entity.destroy()

View File

@ -45,7 +45,7 @@ class LevelSpec:
assert spec.has_key('type')
attribNames = entTypeReg.getAttribNames(spec['type'])
attribDescs = entTypeReg.getAttribDescs(spec['type'])
attribDescs = entTypeReg.getAttribDescDict(spec['type'])
# are there any unknown attribs in the spec?
for attrib in spec.keys():
@ -119,6 +119,9 @@ class LevelSpec:
return self.specDict['scenarios'][scenario][0]
if __debug__:
def setLevel(self, level):
self.level = level
def setAttribEditEventName(self, event):
self.attribEditEventName = event
def setAttribEdit(self, entId, attrib, value):
@ -127,54 +130,79 @@ class LevelSpec:
# with it
messenger.send(self.attribEditEventName, [entId, attrib, value])
def setAttribChangeEventName(self, event):
self.attribChangeEventName = event
def setAttribChange(self, entId, attrib, value):
specDict = self.entId2specDict[entId]
specDict[entId][attrib] = value
# locally broadcast the fact that this attribute value has
# let the level know that this attribute value has
# officially changed
if self.attribChangeEventName is not None:
messenger.send(self.attribChangeEventName,
[entId, attrib, value])
self.level.handleAttribChange(entId, attrib, value)
def insertEntity(self, entId, entType, parentEntId):
assert entId not in self.entId2specDict
globalEnts = self.privGetGlobalEntityDict()
self.entId2specDict[entId] = globalEnts
# create a new entity spec entry w/ default values
globalEnts[entId] = {}
spec = globalEnts[entId]
attribDescs = self.entTypeReg.getAttribDescDict(entType)
for name, desc in attribDescs.items():
spec[name] = desc.getDefaultValue()
spec['type'] = entType
if 'parent' in spec:
spec['parent'] = parentEntId
# notify the level
self.level.handleEntityInsert(entId)
def removeEntity(self, entId):
assert entId in self.entId2specDict
# notify the level
self.level.handleEntityRemove(entId)
# remove the entity's spec
dict = self.entId2specDict[entId]
del dict[entId]
del self.entId2specDict[entId]
def getSpecImportsModuleName(self):
# name of module that should be imported by spec py file
return 'SpecImports'
def saveToDisk(self, filename=None):
def saveToDisk(self, filename=None, createBackup=1):
"""returns zero on failure"""
import os
if filename is None:
filename = self.filename
# create a backup
try:
# does the file exist?
exists = 0
if createBackup:
# create a backup
try:
os.stat(filename)
exists = 1
except OSError:
pass
if exists:
def getBackupFilename(num, filename=filename):
return '%s.%03i' % (filename, num)
numBackups = 50
# does the file exist?
exists = 0
try:
os.unlink(getBackupFilename(numBackups-1))
os.stat(filename)
exists = 1
except OSError:
pass
for i in range(numBackups-1,0,-1):
if exists:
def getBackupFilename(num, filename=filename):
return '%s.%03i' % (filename, num)
numBackups = 100
try:
os.rename(getBackupFilename(i-1),
getBackupFilename(i))
os.unlink(getBackupFilename(numBackups-1))
except OSError:
pass
os.rename(filename, getBackupFilename(0))
except OSError, e:
LevelSpec.notify.warning('error during backup: %s' % str(e))
for i in range(numBackups-1,0,-1):
try:
os.rename(getBackupFilename(i-1),
getBackupFilename(i))
except OSError:
pass
os.rename(filename, getBackupFilename(0))
except OSError, e:
LevelSpec.notify.warning(
'error during backup: %s' % str(e))
retval = 1
# wb to create a UNIX-format file
@ -216,7 +244,7 @@ class LevelSpec:
result.extend(elements)
return result
firstTypes = ('levelMgr', 'zone',)
firstTypes = ('levelMgr', 'editMgr', 'zone',)
firstAttribs = ('type', 'name', 'comment', 'parent',
'pos', 'x', 'y', 'z',
'hpr', 'h', 'p', 'r',