added editMgr, entity insertion, removal
This commit is contained in:
parent
60e7fb5596
commit
ecdb18879f
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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:])
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in New Issue