From 5a0a536ef5c8670d805b33db20202ec99e616c79 Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Wed, 5 Nov 2003 01:46:49 +0000 Subject: [PATCH] added EntrancePoint entity, extended entity NodePath support --- direct/src/level/BasicEntities.py | 158 +++++++++++++------------ direct/src/level/DistributedLevel.py | 23 ++++ direct/src/level/DistributedLevelAI.py | 19 ++- direct/src/level/Entity.py | 4 + direct/src/level/EntityCreator.py | 5 +- direct/src/level/EntityCreatorAI.py | 1 + direct/src/level/EntityTypes.py | 6 + direct/src/level/EntrancePoint.py | 31 +++++ direct/src/level/Level.py | 16 +++ direct/src/level/LevelSpec.py | 20 ++++ direct/src/level/ZoneEntity.py | 6 +- 11 files changed, 211 insertions(+), 78 deletions(-) create mode 100755 direct/src/level/EntrancePoint.py diff --git a/direct/src/level/BasicEntities.py b/direct/src/level/BasicEntities.py index 9d2deedefd..cde14c4055 100755 --- a/direct/src/level/BasicEntities.py +++ b/direct/src/level/BasicEntities.py @@ -4,11 +4,12 @@ import Entity import DistributedEntity import NodePath -# this is an abstract class, do not instantiate. -class NodePathAttribs: - """Derive from this class to give an entity the behavior of a - NodePath, without necessarily deriving from NodePath. Derived class - must implement getNodePath().""" +# base class for entities that support NodePath attributes +# *** Don't derive directly from this class; derive from the appropriate +# specialized class from the classes defined below. +class NodePathEntityBase: + # we don't call this __init__ because it doesn't have to be called + # upon object init def initNodePathAttribs(self, doReparent=1): """Call this after the entity has been initialized""" self.callSetters('pos','x','y','z', @@ -17,9 +18,64 @@ class NodePathAttribs: if doReparent: self.callSetters('parentEntId') + self.getNodePath().setName('%s-%s' % + (self.__class__.__name__, self.entId)) + if __debug__: + # for the editor self.getNodePath().setTag('entity', '1') + def setParentEntId(self, parentEntId): + self.parentEntId = parentEntId + self.level.requestReparent(self, self.parentEntId) + + def destroy(self): + if __debug__: + # for the editor + self.getNodePath().clearTag('entity') + +# Entities that already derive from NodePath and Entity should derive +# from this class +class NodePathAttribs(NodePathEntityBase): + def initNodePathAttribs(self, doReparent=1): + NodePathEntityBase.initNodePathAttribs(self, doReparent) + + def destroy(self): + NodePathEntityBase.destroy(self) + + def getNodePath(self): + return self + +# Entities that already derive from Entity, and do not derive from NodePath, +# but want to be a NodePath, should derive from this. +class NodePathAndAttribs(NodePathEntityBase, NodePath.NodePath): + def __init__(self): + node = hidden.attachNewNode('EntityNodePath') + NodePath.NodePath.__init__(self, node) + + def initNodePathAttribs(self, doReparent=1): + NodePathEntityBase.initNodePathAttribs(self, doReparent) + + def destroy(self): + NodePathEntityBase.destroy(self) + self.removeNode() + + def getNodePath(self): + return self + +# Entities that already derive from Entity, and do not derive from NodePath, +# but HAVE a NodePath that they want to represent them, should derive from +# this. They must define getNodePath(), which should return their 'proxy' +# NodePath instance. +class NodePathAttribsProxy(NodePathEntityBase): + def initNodePathAttribs(self, doReparent=1): + """Call this after the entity has been initialized""" + NodePathEntityBase.initNodePathAttribs(self, doReparent) + assert self.getNodePath() != self + + def destroy(self): + NodePathEntityBase.destroy(self) + def setPos(self, *args): self.getNodePath().setPos(*args) def setX(self, *args): self.getNodePath().setX(*args) def setY(self, *args): self.getNodePath().setY(*args) @@ -37,85 +93,39 @@ class NodePathAttribs: def reparentTo(self, *args): self.getNodePath().reparentTo(*args) - def setParentEntId(self, parentEntId): - self.parentEntId = parentEntId - self.level.requestReparent(self, self.parentEntId) - -# this is an abstract class, do not instantiate. -class NodePathSelfAttribs: - """Derive from this class to give an entity that is already a Nodepath - the behavior of a NodePathEntity, with ability to reparent and be - picked from the Direct/FactoryEditor interface""" - def initNodePathSelfAttribs(self): - if __debug__: - self.setTag('entity', '1') - self.callSetters('parentEntId') - - def setParentEntId(self, parentEntId): - self.parentEntId = parentEntId - self.level.requestReparent(self, self.parentEntId) - - -class privNodePathImpl(NodePath.NodePath): - def __init__(self, name): - node = hidden.attachNewNode(name) +# This is an entity that represents a NodePath on the client. +# It may be instantiated directly or used as a base class for other +# entity types that 'are' NodePaths. +class NodePathEntity(Entity.Entity, NodePath.NodePath, NodePathAttribs): + def __init__(self, level, entId): + node = hidden.attachNewNode('NodePathEntity') NodePath.NodePath.__init__(self, node) - if __debug__: - self.setTag('entity', '1') - - def initNodePathAttribs(self): - """Call this after the entity has been initialized, and all - of its attributes have been set""" - self.callSetters('pos','x','y','z', - 'hpr','h','p','r', - 'scale','sx','sy','sz', - 'parentEntId') - - def setParentEntId(self, parentEntId): - self.parentEntId = parentEntId - self.level.requestReparent(self.getNodePath(), self.parentEntId) + Entity.Entity.__init__(self, level, entId) + NodePathAttribs.initNodePathAttribs(self) def destroy(self): - if __debug__: - self.clearTag('entity') + NodePathAttribs.destroy(self) + Entity.Entity.destroy(self) self.removeNode() - def getNodePath(self): - return self - -class NodePathEntity(Entity.Entity, privNodePathImpl): - """This is an entity that represents a NodePath on the client. - It may be instantiated directly or used as a base class for other - entity types.""" - def __init__(self, level, entId): - privNodePathImpl.__init__(self, '') - Entity.Entity.__init__(self, level, entId) - self.setName(str(self)) - privNodePathImpl.initNodePathAttribs(self) - - def destroy(self): - Entity.Entity.destroy(self) - privNodePathImpl.destroy(self) - - def getNodePath(self): - return self - +# This is a distributed version of NodePathEntity. It should not +# be instantiated directly; distributed entities that are also NodePaths +# may derive from this instead of DistributedEntity. class DistributedNodePathEntity(DistributedEntity.DistributedEntity, - privNodePathImpl): - """This is a distributed version of NodePathEntity. It should not - be instantiated directly; derive your client-side distEntity from - this class instead of DistributedEntity.""" + NodePath.NodePath, NodePathAttribs): def __init__(self, cr): DistributedEntity.DistributedEntity.__init__(self, cr) - privNodePathImpl.__init__(self, 'DistributedNodePathEntity') + node = hidden.attachNewNode('DistributedNodePathEntity') + NodePath.NodePath.__init__(self, node) def announceGenerate(self): DistributedEntity.DistributedEntity.announceGenerate(self) - privNodePathImpl.initNodePathAttribs(self) + NodePathAttribs.initNodePathAttribs(self) - def destroy(self): - DistributedEntity.DistributedEntity.destroy(self) - privNodePathImpl.destroy(self) + def disable(self): + NodePathAttribs.destroy(self) + DistributedEntity.DistributedEntity.disable(self) - def getNodePath(self): - return self + def delete(self): + self.removeNode() + DistributedEntity.DistributedEntity.delete(self) diff --git a/direct/src/level/DistributedLevel.py b/direct/src/level/DistributedLevel.py index 28ccd662dd..d8f3409464 100755 --- a/direct/src/level/DistributedLevel.py +++ b/direct/src/level/DistributedLevel.py @@ -81,6 +81,16 @@ class DistributedLevel(DistributedObject.DistributedObject, # zone the entire time we're in here self.levelZone = zoneId + def setPlayerIds(self, avIdList): + self.avIdList = avIdList + assert toonbase.localToon.doId in self.avIdList + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + + def getEntranceId(self): + return self.entranceId + # "required" fields (these ought to be required fields, but # the AI level obj doesn't know the data values until it has been # generated.) @@ -168,6 +178,19 @@ class DistributedLevel(DistributedObject.DistributedObject, # load stuff self.initVisibility() + self.placeLocalToon() + + def placeLocalToon(self): + # the entrancePoint entities register themselves with us + if self.entranceId not in self.entranceId2entity: + self.notify.warning('unknown entranceId %s' % self.entranceId) + toonbase.localToon.setPos(0,0,0) + else: + epEnt = self.entranceId2entity[self.entranceId] + epEnt.placeToon(toonbase.localToon, + self.avIdList.index(toonbase.localToon.doId), + len(self.avIdList)) + def createEntityCreator(self): """Create the object that will be used to create Entities. Inheritors, override if desired.""" diff --git a/direct/src/level/DistributedLevelAI.py b/direct/src/level/DistributedLevelAI.py index c6dcae7683..be12e35429 100755 --- a/direct/src/level/DistributedLevelAI.py +++ b/direct/src/level/DistributedLevelAI.py @@ -13,11 +13,20 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, """DistributedLevelAI""" notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelAI') - def __init__(self, air, zoneId): + def __init__(self, air, zoneId, entranceId, avIds): DistributedObjectAI.DistributedObjectAI.__init__(self, air) Level.Level.__init__(self) - # this is one of the required fields + # these are required fields self.zoneId = zoneId + self.entranceId = entranceId + + assert len(avIds) > 0 and len(avIds) <= 4 + assert 0 not in avIds + assert None not in avIds + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.notify.debug("expecting avatars: %s" % str(self.avIdList)) + if __debug__: self.modified = 0 @@ -37,6 +46,12 @@ class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, same zone with the level""" return self.zoneId + def getPlayerIds(self): + return self.avIdList + + def getEntranceId(self): + return self.entranceId + def delete(self): self.notify.debug('delete') if __debug__: diff --git a/direct/src/level/Entity.py b/direct/src/level/Entity.py index 82866142fd..3cd125285e 100755 --- a/direct/src/level/Entity.py +++ b/direct/src/level/Entity.py @@ -38,6 +38,10 @@ class Entity(DirectObject): del self.level del self.entId + def getZoneEntId(self): + """returns entId of zone that contains this entity""" + return self.level.getEntityZoneEntId(self.entId) + def privGetSetter(self, attrib): setFuncName = 'set%s%s' % (string.upper(attrib[0]), attrib[1:]) if hasattr(self, setFuncName): diff --git a/direct/src/level/EntityCreator.py b/direct/src/level/EntityCreator.py index 09b9c8ccfb..a35902611d 100755 --- a/direct/src/level/EntityCreator.py +++ b/direct/src/level/EntityCreator.py @@ -5,6 +5,7 @@ import EntityCreatorBase import BasicEntities import DirectNotifyGlobal import EditMgr +import EntrancePoint import LevelMgr import ZoneEntity import ModelEntity @@ -14,7 +15,8 @@ import PathEntity # 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""" + """For entities that don't need to be created by the client or don't + exist on the client at all""" return 'nothing' class EntityCreator(EntityCreatorBase.EntityCreatorBase): @@ -29,6 +31,7 @@ class EntityCreator(EntityCreatorBase.EntityCreatorBase): self.privRegisterTypes({ 'cutScene': CutScene.CutScene, 'editMgr': EditMgr.EditMgr, + 'entrancePoint': EntrancePoint.EntrancePoint, 'levelMgr': LevelMgr.LevelMgr, 'logicGate': nothing, 'model' : ModelEntity.ModelEntity, diff --git a/direct/src/level/EntityCreatorAI.py b/direct/src/level/EntityCreatorAI.py index d5fa38331a..af53a374ff 100755 --- a/direct/src/level/EntityCreatorAI.py +++ b/direct/src/level/EntityCreatorAI.py @@ -46,6 +46,7 @@ class EntityCreatorAI(EntityCreatorBase.EntityCreatorBase): self.privRegisterTypes({ 'cutScene': nothing, 'editMgr': Functor(cLE, EditMgrAI.EditMgrAI), + 'entrancePoint': nothing, 'levelMgr': Functor(cLE, LevelMgrAI.LevelMgrAI), 'logicGate': Functor(cLE, LogicGateAI.LogicGateAI), 'model' : nothing, diff --git a/direct/src/level/EntityTypes.py b/direct/src/level/EntityTypes.py index 5947e5415b..5c82b4ea44 100755 --- a/direct/src/level/EntityTypes.py +++ b/direct/src/level/EntityTypes.py @@ -53,6 +53,12 @@ class Zone(Nodepath): ('visibility', [], 'visZoneList'), ) +class EntrancePoint(Nodepath): + type = 'entrancePoint' + attribs = ( + ('entranceId', -1, 'int'), + ) + class LogicGate(Entity): type = 'logicGate' output = 'bool' diff --git a/direct/src/level/EntrancePoint.py b/direct/src/level/EntrancePoint.py new file mode 100755 index 0000000000..366470a3bb --- /dev/null +++ b/direct/src/level/EntrancePoint.py @@ -0,0 +1,31 @@ +from ToontownGlobals import * +import DirectNotifyGlobal +import BasicEntities +import NodePath + +class EntrancePoint(BasicEntities.NodePathEntity): + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.initEntrancePoint() + + def destroy(self): + self.destroyEntrancePoint() + BasicEntities.NodePathEntity.destroy(self) + + def placeToon(self, toon, toonIndex, numToons): + toon.setPosHpr(self, 0,0,0, 0,0,0) + + def initEntrancePoint(self): + if self.entranceId >= 0: + self.level.entranceId2entity[self.entranceId] = self + + def destroyEntrancePoint(self): + if self.entranceId >= 0: + if self.level.entranceId2entity.has_key(self.entranceId): + del self.level.entranceId2entity[self.entranceId] + + if __debug__: + def attribChanged(self, *args): + BasicEntities.NodePathEntity.attribChanged(self, *args) + self.destroyEntrancePoint() + self.initEntrancePoint() diff --git a/direct/src/level/Level.py b/direct/src/level/Level.py index 4b368c055f..18978812b5 100755 --- a/direct/src/level/Level.py +++ b/direct/src/level/Level.py @@ -53,6 +53,9 @@ class Level: self.entType2ids = self.levelSpec.getEntType2ids( self.levelSpec.getAllEntIds()) + # entranceId to entrance entity + self.entranceId2entity = {} + # this list contains the entIds of entities that we have actually # created, in order of creation self.createdEntIds = [] @@ -198,6 +201,19 @@ class Level: def getEntityType(self, entId): return self.levelSpec.getEntityType(entId) + def getEntityZoneEntId(self, entId): + """return entId of zone that contains the entity""" + return self.levelSpec.getEntityZoneEntId(entId) + + def getEntityZoneNum(self, entId): + """return the model zoneNum of zone that contains the entity""" + return self.levelSpec.getEntityZoneNum(entId) + + def getEntityZoneId(self, entId): + """return network zoneId of zone that contains the entity""" + zoneEntId = self.getEntityZoneEntId(entId) + return self.zoneEntId2zoneId[entId] + def getZoneId(self, dummy=None, zoneNum=None, entId=None): """look up network zoneId by zoneNum or entId""" assert (zoneNum is not None) or (entId is not None) diff --git a/direct/src/level/LevelSpec.py b/direct/src/level/LevelSpec.py index 330874e2eb..3260e45153 100755 --- a/direct/src/level/LevelSpec.py +++ b/direct/src/level/LevelSpec.py @@ -103,6 +103,26 @@ class LevelSpec: def getEntityType(self, entId): return self.getEntitySpec(entId)['type'] + def getEntityZoneEntId(self, entId): + """ return the entId of the zone that entity is in; if entity + is a zone, returns its entId """ + spec = self.getEntitySpec(entId) + type = spec['type'] + # if it's a zone, this is our entity + if type == 'zone': + return entId + # if we have no parentEntId, assume we're in the UberZone + if not spec.has_key('parentEntId'): + return LevelConstants.UberZoneEntId + # keep looking up the heirarchy for a zone entity + return self.getEntityZoneId(spec['parentEntId']) + + def getEntityZoneNum(self, entId): + """ return the model zoneNum of zone that contains the entity """ + zoneEntId = self.getEntityZoneEntId(entId) + spec = self.getEntitySpec(zoneEntId) + return spec['modelZoneNum'] + def getEntType2ids(self, entIds): """given list of entIds, return dict of entType 2 entIds""" entType2ids = {} diff --git a/direct/src/level/ZoneEntity.py b/direct/src/level/ZoneEntity.py index c0176a3e14..7b2eaa3595 100755 --- a/direct/src/level/ZoneEntity.py +++ b/direct/src/level/ZoneEntity.py @@ -8,7 +8,11 @@ class ZoneEntity(ZoneEntityBase.ZoneEntityBase, BasicEntities.NodePathAttribs): ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId) self.nodePath = self.level.getZoneNode(self.modelZoneNum) - self.initNodePathAttribs(doReparent=0) + BasicEntities.NodePathAttribs.initNodePathAttribs(self, doReparent=0) + + def destroy(self): + BasicEntities.NodePathAttribs.destroy(self) + ZoneEntityBase.ZoneEntityBase.destroy(self) def getNodePath(self): return self.nodePath