class DistributedGardenBox(DistributedLawnDecor.DistributedLawnDecor): notify = DirectNotifyGlobal.directNotify.newCategory( 'DistributedGardenPlot') def __init__(self, cr): DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) #self.defaultModel = "phase_8/models/props/flower_treasure.bam" self.plantPath = NodePath('plantPath') self.plantPath.reparentTo(self) self.plotScale = 1.0 self.plantingGuiDoneEvent = "plantingGuiDone" self.defaultModel = "phase_5.5/models/estate/planterC" def announceGenerate(self): #print("box announceGenerate!!!!") self.notify.debug('announceGenerate') DistributedLawnDecor.DistributedLawnDecor.announceGenerate(self) def doModelSetup(self): if self.typeIndex == GardenGlobals.BOX_THREE: self.defaultModel = "phase_5.5/models/estate/planterA" elif self.typeIndex == GardenGlobals.BOX_TWO: self.defaultModel = "phase_5.5/models/estate/planterC" else: self.defaultModel = "phase_5.5/models/estate/planterD" self.collSphereOffset = 0.0 self.collSphereRadius = self.collSphereRadius * 1.41 self.plotScale = Vec3(1.0, 1.0, 1.0) def setupShadow(self): #we don't want the shadow pass def loadModel(self): self.rotateNode = self.plantPath.attachNewNode('rotate') self.model = None self.model = loader.loadModel(self.defaultModel) self.model.setScale(self.plotScale) self.model.reparentTo(self.rotateNode) self.stick2Ground() #uncomment line below if we want the toons to walk on top of the planter #self.model.find("**/collision").stash() def handleEnterPlot(self, entry=None): #print("Box entered") pass def handleExitPlot(self, entry=None): DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) pass def setTypeIndex(self, typeIndex): #print("box setting type index!!!!") self.typeIndex = typeIndex
def stick2Ground(self, taskfooler=0): if self.isEmpty(): return Task.done testPath = NodePath('testPath') testPath.reparentTo(render) # This is a ray cast down to detect floor polygons cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) cRayNode = CollisionNode(self.uniqueName('estate-FloorRay')) cRayNode.addSolid(cRay) cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) cRayNode.setIntoCollideMask(BitMask32.allOff()) cRayNodePath = testPath.attachNewNode(cRayNode) queue = CollisionHandlerQueue() picker = CollisionTraverser() picker.addCollider(cRayNodePath, queue) # fix the movie node if self.movieNode: testPath.setPos(self.movieNode.getX(render), self.movieNode.getY(render), 0) picker.traverse(render) if queue.getNumEntries() > 0: queue.sortEntries() for index in range(queue.getNumEntries()): entry = queue.getEntry(index) if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): self.movieNode.setZ(entry.getSurfacePoint(self)[2]) #testPath.setPos(self.plantPath.getX(),self.plantPath.getY(),0) # fix the main model testPath.setPos(self.getX(), self.getY(), 0) picker.traverse(render) if queue.getNumEntries() > 0: #print("Sticking") queue.sortEntries() for index in range(queue.getNumEntries()): entry = queue.getEntry(index) #TODO clean up this next bit to be more generic #if entry.getIntoNode().getParent(0).getParent(0).getParent(0).getName() == 'terrain_DNARoot': if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): #if str(entry.getIntoNodePath()) == "render/Estate/30000:estate/terrain_DNARoot/-PandaNode/group3/collision1": self.setZ( entry.getSurfacePoint(render)[2] + self.stickUp + 0.1) #print("Stickup %s" % (self.stickUp)) #print("Stick Height %s %s %s" % (entry.getSurfacePoint(render)[2],self.getX(),self.getY())) self.stickParts() return Task.done taskMgr.doMethodLater(1.0, self.stick2Ground, uniqueName("groundsticker")) #print("Not Sticking") return Task.done
def generate(self): DistCogdoGameAI.generate(self) mazeFactory = self.createMazeFactory(self.createRandomNumGen()) waterCoolerList = [] mazeModel = mazeFactory._loadAndBuildMazeModel() for waterCooler in mazeModel.findAllMatches('**/*waterCooler'): waterCoolerList.append( (waterCooler.getPos(mazeModel), waterCooler.getHpr(mazeModel))) waterCoolerList.sort() baseNp = NodePath('base') rotNp = baseNp.attachNewNode('rot') childNp = rotNp.attachNewNode('child') childNp.setPos(*Globals.WaterCoolerTriggerOffset) self._waterCoolerPosList = [] for (pos, hpr) in waterCoolerList: rotNp.setHpr(hpr) offset = childNp.getPos(baseNp) self._waterCoolerPosList.append(pos + offset) self._speedMonitor = SpeedMonitor('cogdoMazeGame-%s' % self.doId) self._toonId2speedToken = {}
class PartyCog(FSM): notify = directNotify.newCategory("PartyCog") HpTextGenerator = TextNode("HpTextGenerator") hpText = None height = 7 def __init__(self, parentNode, id, bounceSpeed=3, bounceHeight=1, rotateSpeed=1, heightShift=1, xMoveSpeed=0, xMoveDistance=0, bounceOffset=0): self.id = id FSM.__init__(self, "PartyCogFSM-%d" % self.id) self.showFacingStatus = False self.xMoveSpeed = xMoveSpeed self.xMoveDistance = xMoveDistance self.heightShift = heightShift self.bounceSpeed = bounceSpeed self.bounceHeight = bounceHeight self.rotateSpeed = rotateSpeed self.parentNode = parentNode self.bounceOffset = bounceOffset self.hitInterval = None self.kaboomTrack = None self.resetRollIval = None self.netTimeSentToStartByHit = 0 self.load() self.request("Down") def load(self): self.root = NodePath("PartyCog-%d" % self.id) self.root.reparentTo(self.parentNode) path = "phase_13/models/parties/cogPinata_" self.actor = Actor( path + "actor", { "idle": path + "idle_anim", "down": path + "down_anim", "up": path + "up_anim", "bodyHitBack": path + "bodyHitBack_anim", "bodyHitFront": path + "bodyHitFront_anim", "headHitBack": path + "headHitBack_anim", "headHitFront": path + "headHitFront_anim", }) self.actor.reparentTo(self.root) self.temp_transform = Mat4() self.head_locator = self.actor.attachNewNode("temphead") self.bodyColl = CollisionTube(0, 0, 1, 0, 0, 5.75, 0.75) self.bodyColl.setTangible(1) self.bodyCollNode = CollisionNode("PartyCog-%d-Body-Collision" % self.id) self.bodyCollNode.setCollideMask(ToontownGlobals.PieBitmask) self.bodyCollNode.addSolid(self.bodyColl) self.bodyCollNodePath = self.root.attachNewNode(self.bodyCollNode) self.headColl = CollisionTube(0, 0, 3, 0, 0, 3.0, 1.5) self.headColl.setTangible(1) self.headCollNode = CollisionNode("PartyCog-%d-Head-Collision" % self.id) self.headCollNode.setCollideMask(ToontownGlobals.PieBitmask) self.headCollNode.addSolid(self.headColl) self.headCollNodePath = self.root.attachNewNode(self.headCollNode) # Cog's Left Arm self.arm1Coll = CollisionSphere(1.65, 0, 3.95, 1.0) self.arm1Coll.setTangible(1) self.arm1CollNode = CollisionNode("PartyCog-%d-Arm1-Collision" % self.id) self.arm1CollNode.setCollideMask(ToontownGlobals.PieBitmask) self.arm1CollNode.addSolid(self.arm1Coll) self.arm1CollNodePath = self.root.attachNewNode(self.arm1CollNode) # Cog's Right Arm self.arm2Coll = CollisionSphere(-1.65, 0, 3.45, 1.0) self.arm2Coll.setTangible(1) self.arm2CollNode = CollisionNode("PartyCog-%d-Arm2-Collision" % self.id) self.arm2CollNode.setCollideMask(ToontownGlobals.PieBitmask) self.arm2CollNode.addSolid(self.arm2Coll) self.arm2CollNodePath = self.root.attachNewNode(self.arm2CollNode) splatName = 'splat-creampie' self.splat = globalPropPool.getProp(splatName) self.splat.setBillboardPointEye() self.splatType = globalPropPool.getPropType(splatName) self.pieHitSound = globalBattleSoundCache.getSound( 'AA_wholepie_only.mp3') self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.mp3') self.hole = loader.loadModel("phase_13/models/parties/cogPinataHole") self.hole.setTransparency(True) self.hole.setP(-90.0) self.hole.setScale(3) self.hole.setBin("ground", 3) self.hole.reparentTo(self.parentNode) def unload(self): self.request("Off") self.clearHitInterval() if self.hole is not None: self.hole.removeNode() self.hole = None if self.actor is not None: self.actor.cleanup() self.actor.removeNode() self.actor = None if self.root is not None: self.root.removeNode() self.root = None if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.kaboomTrack = None if self.resetRollIval is not None and self.resetRollIval.isPlaying(): self.resetRollIval.finish() self.resetRollIval = None if self.hitInterval is not None and self.hitInterval.isPlaying(): self.hitInterval.finish() self.hitInterval = None del self.upSound del self.pieHitSound #=============================================================================== # FSM States #=============================================================================== def enterStatic(self): pass def exitStatic(self): pass def enterActive(self, startTime): self.root.setR(0.0) updateTask = Task.Task(self.updateTask) updateTask.startTime = startTime taskMgr.add(updateTask, "PartyCog.update-%d" % self.id) def exitActive(self): taskMgr.remove("PartyCog.update-%d" % self.id) taskMgr.remove("PartyCog.bounceTask-%d" % self.id) self.clearHitInterval() self.resetRollIval = self.root.hprInterval(0.5, Point3( self.root.getH(), 0.0, 0.0), blendType="easeInOut") self.resetRollIval.start() self.actor.stop() def enterDown(self): if self.oldState == "Off": self.actor.pose("down", self.actor.getNumFrames("down") - 1) return self.clearHitInterval() startScale = self.hole.getScale() endScale = Point3(5, 5, 5) self.hitInterval = Sequence( LerpFunc(self.setAlongSpline, duration=1.0, fromData=self.currentT, toData=0.0), LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType="easeIn"), Parallel( SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, "down", loop=0), ), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType="easeOut"), ) self.hitInterval.start() def exitDown(self): self.root.setR(0.0) self.root.setH(0.0) self.targetDistance = 0.0 self.targetFacing = 0.0 self.currentT = 0.0 self.setAlongSpline(0.0) self.clearHitInterval() startScale = self.hole.getScale() endScale = Point3(5, 5, 5) self.hitInterval = Sequence( LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType="easeIn"), Parallel( SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, "up", loop=0), ), Func(self.actor.loop, "idle"), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType="easeOut"), ) self.hitInterval.start() def filterDown(self, request, args): if request == "Down": return None else: return self.defaultFilter(request, args) #------------------------------------------------------------------------------ def setEndPoints(self, start, end, amplitude=1.7): self.sinAmplitude = amplitude self.sinPeriod = (end.getX() - start.getX()) / 2 self.sinDisplacement = start.getY() self.startPoint = start self.endPoint = end self.currentT = 0.0 self.targetDistance = 0.0 self.currentFacing = 0.0 self.targetFacing = 0.0 self.setAlongSpline(self.currentT) self.hole.setPos(self.root.getPos()) self.hole.setZ(0.02) def rockBackAndForth(self, task): t = task.startTime + task.time angle = math.sin(t) * 20.0 self.root.setR(angle) # if self.id == 0: # print angle return task.cont def updateDistance(self, distance): self.targetDistance = clamp(distance, -1.0, 1.0) def updateTask(self, task): self.rockBackAndForth(task) if self.targetDistance > self.currentT: self.currentT += min(0.01, self.targetDistance - self.currentT) self.setAlongSpline(self.currentT) elif self.targetDistance < self.currentT: self.currentT += max(-0.01, self.targetDistance - self.currentT) self.setAlongSpline(self.currentT) if self.currentT < 0.0: self.targetFacing = -90.0 elif self.currentT > 0.0: self.targetFacing = 90.0 else: self.targetFacing = 0.0 if self.targetFacing > self.currentFacing: self.currentFacing += min(10, self.targetFacing - self.currentFacing) elif self.targetFacing < self.currentFacing: self.currentFacing += max(-10, self.targetFacing - self.currentFacing) self.root.setH(self.currentFacing) return task.cont def setAlongSpline(self, t): t = t + 1.0 dist = (self.endPoint.getX() - self.startPoint.getX()) / 2.0 x = self.startPoint.getX() + t * dist y = self.startPoint.getY() - math.sin( t * 2 * math.pi) * self.sinAmplitude self.root.setPos(x, y, 0) def startBounce(self): taskMgr.add(self.bounce, "PartyCog.bounceTask-%d" % self.id) def bounce(self, task): #self.root.setH(self.root.getH() - self.rotateSpeed) self.root.setZ((math.sin((self.bounceOffset + task.time) * self.bounceSpeed) * self.bounceHeight) + self.heightShift) return task.cont def setPos(self, position): self.root.setPos(position) def respondToPieHit(self, timestamp, position, hot=False, direction=1.0): """The toon hit us, react appropriately.""" assert (self.notify.debugStateCall(self)) if self.netTimeSentToStartByHit < timestamp: self.__showSplat(position, direction, hot) if self.netTimeSentToStartByHit < timestamp: self.netTimeSentToStartByHit = timestamp else: #self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit' % (localStamp, self.lastLocalTimeStampFromAI)) self.notify.debug( 'respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) def clearHitInterval(self): if self.hitInterval is not None and self.hitInterval.isPlaying(): self.hitInterval.clearToInitial() def __showSplat(self, position, direction, hot=False): """Show the splat graphic and sound.""" if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.clearHitInterval() splatName = 'splat-creampie' self.splat = globalPropPool.getProp(splatName) self.splat.setBillboardPointEye() self.splat.reparentTo(render) self.splat.setPos(self.root, position) self.splat.setAlphaScale(1.0) if not direction == 1.0: #self.splat.setColorScale(Vec4(0.0, 0.0, 50.0, 1.0)) self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0]) if self.currentFacing > 0.0: facing = "HitFront" else: facing = "HitBack" else: self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1]) #self.splat.setColorScale(Vec4(1.0, 0.6, 0.08, 1.0)) if self.currentFacing > 0.0: facing = "HitBack" else: facing = "HitFront" if hot: targetscale = 0.75 part = "head" else: targetscale = 0.5 part = "body" def setSplatAlpha(amount): self.splat.setAlphaScale(amount) self.hitInterval = Sequence( ActorInterval(self.actor, part + facing, loop=0), Func(self.actor.loop, "idle"), ) self.hitInterval.start() self.kaboomTrack = Parallel( SoundInterval(self.pieHitSound, volume=1.0, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence( Func(self.splat.showThrough), Parallel( Sequence( LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType="easeOut"), Wait(0.175), ), Sequence( Wait(0.1), LerpFunc( setSplatAlpha, duration=1.0, #0.4, fromData=1.0, toData=0.0, blendType="easeOut"))), Func(self.splat.cleanup), Func(self.splat.removeNode), )) self.kaboomTrack.start() def showHitScore(self, number, scale=1): """ Shows the hit score. Borrowed from otp.avatar.DistributedAvatar.showHpText """ if number <= 0: return # Get rid of the number if it is already there. if self.hpText: self.hideHitScore() # Set the font self.HpTextGenerator.setFont(ToontownGlobals.getSignFont()) # Show both negative and positive signs if number < 0: self.HpTextGenerator.setText(str(number)) else: self.HpTextGenerator.setText("+" + str(number)) # No shadow self.HpTextGenerator.clearShadow() # Center the number self.HpTextGenerator.setAlign(TextNode.ACenter) # Red, always #if number < 0: r = 1 #0.9 g = 1 #0 b = 0 a = 1 self.HpTextGenerator.setTextColor(r, g, b, a) self.hpTextNode = self.HpTextGenerator.generate() # Put the hpText over the head of the avatar self.hpText = render.attachNewNode(self.hpTextNode) self.hpText.setScale(scale) # Make sure it is a billboard self.hpText.setBillboardPointEye() # Render it after other things in the scene. self.hpText.setBin('fixed', 100) # Initial position ... Center of the body... the "tan tien" self.hpText.setPos(self.root, 0, 0, self.height / 2) # Black magic from the early days of Panda3D, later replaced by a Sequence seq = Task.sequence( # Fly the number out of the character self.hpText.lerpPos(Point3( self.root.getX(render), self.root.getY(render), self.root.getZ(render) + self.height + 1.0), 0.25, blendType='easeOut'), Task.pause(0.25), # Fade the number self.hpText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.1), # Get rid of the number Task.Task(self.__hideHitScoreTask)) taskMgr.add(seq, "PartyCogHpText" + str(self.id)) def __hideHitScoreTask(self, task): self.hideHitScore() return Task.done def hideHitScore(self): if self.hpText: taskMgr.remove("PartyCogHpText" + str(self.id)) self.hpText.removeNode() self.hpText = None def getHeadLocation(self): (self.actor.getJoints(jointName="head")[0]).getNetTransform( self.temp_transform) self.head_locator.setMat(self.temp_transform) #print self.head_locator.getZ() return self.head_locator.getZ(self.root)
class DistributedLawnDecor(DistributedNode.DistributedNode, NodePath, ShadowCaster.ShadowCaster): notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawnDecor') def __init__(self, cr): DistributedNode.DistributedNode.__init__(self, cr) NodePath.__init__(self, "decor") ShadowCaster.ShadowCaster.__init__(self, False) self.plantPath = NodePath('plantPath') self.plantPath.reparentTo(self) #self.defaultModel = 'phase_9/models/cogHQ/square_stomper' self.defaultModel = 'phase_9/models/cogHQ/woodCrateB' self.messageName = None self.model = None self.colSphereNode = None self.rotateNode = None self.collSphereOffset = 0.0 self.collSphereRadius = 1.0 self.stickUp = 0.0 self.movieNode = None self.shadowJoint = None self.shadowScale = 1 self.expectingReplacement = 0 self.movie = None def setHeading(self, h): self.notify.debug("setting h") DistributedNode.DistributedNode.setH(self, h) def generateInit(self): self.notify.debug('generateInit') DistributedNode.DistributedNode.generateInit(self) def generate(self): self.notify.debug('generate') self.reparentTo(render) DistributedNode.DistributedNode.generate(self) def announceGenerate(self): self.notify.debug('announceGenerate') DistributedNode.DistributedNode.announceGenerate(self) # at this point all the entity attributes have been initialized # we can load the model now. self.doModelSetup() self.loadModel() self.setupShadow() self.makeMovieNode() self.stick2Ground() self.setupCollision() def doModelSetup(self): pass def disable(self): self.notify.debug('disable') self.finishMovies() self.handleExitPlot() # stop things self.ignoreAll() DistributedNode.DistributedNode.disable(self) if hasattr(self, 'nodePath'): self.nodePath.detachNode() def delete(self): self.notify.debug('delete') # unload things ShadowCaster.ShadowCaster.delete(self) self.unloadModel() DistributedNode.DistributedNode.delete(self) def loadModel(self): if not self.rotateNode: self.rotateNode = self.plantPath.attachNewNode('rotate') self.model = None if __dev__: self.model = loader.loadModel(self.defaultModel) self.model.setScale(0.4,0.4,0.1) self.model.reparentTo(self.rotateNode) #self.stick2Ground() def setupShadow(self): # set up the shadow self.shadowJoint = self.rotateNode.attachNewNode('shadow') self.initializeDropShadow(False) self.shadowJoint.setScale(self.shadowScale) self.setActiveShadow() def makeMovieNode(self): # create a movieNode... this is where the toon will stand while playing a movie self.movieNode = self.rotateNode.attachNewNode('moviePos') self.movieNode.setPos(0, -3, 0) #axis = loader.loadModel("models/misc/xyzAxis.bam") #axis.setScale(0.2) #axis.reparentTo(self.movieNode) #self.stick2Ground() def setupCollision(self): self.messageName = self.uniqueName("enterplotSphere") self.messageStartName = self.uniqueName("plotSphere") self.exitMessageName = self.uniqueName("exitplotSphere") # setup a collision sphere #print("collsphere", self.collSphereRadius) if self.collSphereOffset <= 0.1: colSphere = CollisionSphere(0,0,0,self.collSphereRadius) else: colSphere = CollisionTube(0,-self.collSphereOffset,0, 0,self.collSphereOffset,0,self.collSphereRadius) #colSphere.setScale(self.collSphereWidth, 1, 1) colSphere.setTangible(0) #colNode = CollisionNode("plotSphere") colNode = CollisionNode(self.messageStartName) colNode.addSolid(colSphere) colSphereNode = self.attachNewNode(colNode) self.colSphereNode = colSphereNode #self.accept("enterplotSphere", self.handleEnterPlot) self.accept(self.messageName, self.handleEnterPlot) self.accept(self.exitMessageName, self.handleExitPlot) def handleEnterPlot(self, optional = None): #assert self.notify.debugStateCall(self) self.notify.debug('handleEnterPlot %d' % self.doId) #print("Plot Entered; Collision = %s" % (optional)) #DistributedFlower.py will override and replace this self.sendUpdate('plotEntered', []) def handleExitPlot(self, optional = None): if base.localAvatar.inGardenAction == self: base.localAvatar.handleEndPlantInteraction(self, replacement = self.expectingReplacement) pass #assert self.notify.debugStateCall(self) #self.notify.debug('handleExitPlot %d' % self.doId) #print("Plot Entered; Collision = %s" % (optional)) #DistributedFlower.py will override and replace this def handleWatering(self): self.handleExitPlot() base.localAvatar.removeShovelRelatedDoId(self.doId) def unloadModel(self): if self.model: self.model.removeNode() del self.model self.model = None if hasattr(self,'nodePath') and self.nodePath: self.nodePath.removeNode() self.nodePath = None taskMgr.remove(self.uniqueName("adjust tree")) def setPos(self, x, y, z): DistributedNode.DistributedNode.setPos(self, x, y, z) self.stick2Ground() def setPosition(self, x, y, z): DistributedNode.DistributedNode.setPos(self, x, y, z) self.stick2Ground() def stick2Ground(self , taskfooler = 0): if self.isEmpty(): return Task.done testPath = NodePath('testPath') testPath.reparentTo(render) # This is a ray cast down to detect floor polygons cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) cRayNode = CollisionNode(self.uniqueName('estate-FloorRay')) cRayNode.addSolid(cRay) cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) cRayNode.setIntoCollideMask(BitMask32.allOff()) cRayNodePath = testPath.attachNewNode(cRayNode) queue = CollisionHandlerQueue() picker = CollisionTraverser() picker.addCollider(cRayNodePath, queue) # fix the movie node if self.movieNode: testPath.setPos(self.movieNode.getX(render),self.movieNode.getY(render),0) picker.traverse(render) if queue.getNumEntries() > 0: queue.sortEntries() for index in range(queue.getNumEntries()): entry = queue.getEntry(index) if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): self.movieNode.setZ(entry.getSurfacePoint(self)[2]) #testPath.setPos(self.plantPath.getX(),self.plantPath.getY(),0) # fix the main model testPath.setPos(self.getX(),self.getY(),0) picker.traverse(render) if queue.getNumEntries() > 0: #print("Sticking") queue.sortEntries() for index in range(queue.getNumEntries()): entry = queue.getEntry(index) #TODO clean up this next bit to be more generic #if entry.getIntoNode().getParent(0).getParent(0).getParent(0).getName() == 'terrain_DNARoot': if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): #if str(entry.getIntoNodePath()) == "render/Estate/30000:estate/terrain_DNARoot/-PandaNode/group3/collision1": self.setZ(entry.getSurfacePoint(render)[2] + self.stickUp + 0.1) #print("Stickup %s" % (self.stickUp)) #print("Stick Height %s %s %s" % (entry.getSurfacePoint(render)[2],self.getX(),self.getY())) self.stickParts() testPath.removeNode() return Task.done taskMgr.doMethodLater(1.0, self.stick2Ground, uniqueName("groundsticker")) #print("Not Sticking") testPath.removeNode() return Task.done def stickParts(self): return def setPlot(self, plot): self.plot = plot def setH(self, h): DistributedNode.DistributedNode.setH(self, h) #print("setting H %s foo!!" % (h)) def getPlot(self): return self.plot def setOwnerIndex(self, index): self.ownerIndex = index def getOwnerIndex(self): return self.ownerIndex def getOwnerId(self): """ get the toonId of the toon who owns this item, on error returns 0 """ retval = 0 estate = base.cr.doFind('DistributedEstate') if estate and hasattr(estate,'idList') and estate.idList: if self.ownerIndex < len(estate.idList): retval = estate.idList[self.ownerIndex] return retval def canBePicked(self): """ Other subclasses may extend this function, but at the very least, you can't pick something you don't own """ retval = True # if there is no longer a localToon or it is not the owner, return false self.notify.debug('base.localAvatar.doId : %s' %base.localAvatar.doId) self.notify.debug('self.getOwnerId : %s ' %self.getOwnerId()) self.notify.debug("statue's DoId : %s " %self.doId) if not hasattr(base, 'localAvatar') or not (base.localAvatar.doId == self.getOwnerId()): retval = False return retval def unlockPick(self): return True def handleRemove(self): """ At this point assume we've already asked the player if he really wants to pick this flower """ #return # check if this toon can remove this item if not self.canBePicked(): self.notify.debug("I don't own this item, just returning") return base.localAvatar.hideShovelButton() base.localAvatar.hideWateringCanButton() # lock the toon down self.startInteraction() self.sendUpdate('removeItem',[]) def generateToonMoveTrack( self, toon ): """ """ #create a temp node that has the same position as # the toon that we tell to 'lookAt' the plant #import pdb; pdb.set_trace() node = NodePath('tempNode') displacement = Vec3( toon.getPos(render) - self.getPos(render) ) displacement.setZ(0) #hmmm should we set this to zero? displacement.normalize() movieDistance = self.movieNode.getDistance(self.rotateNode) displacement *= movieDistance node.reparentTo(render) node.setPos(displacement + self.getPos(render)) node.lookAt(self) # find the best H to turn the toon heading = PythonUtil.fitDestAngle2Src( toon.getH( render ), node.getH(render) ) #heading = PythonUtil.closestDestAngle( toon.getH( render ), node.getH(render) ) hpr = toon.getHpr( render ) hpr.setX( heading ) finalX = node.getX(render) finalY = node.getY(render) finalZ = node.getZ(render) node.removeNode() toonTrack = Sequence( Parallel( ActorInterval( toon, "walk", loop=True, duration=1), Parallel(LerpPosInterval( toon, 1., Point3( finalX, finalY, toon.getZ(render)), #finalZ ), fluid = True, bakeInStart = False, ), #Func(self.unprint, "generateToonMoveTrack LerpPosInterval") ), LerpHprInterval( toon, 1., hpr = hpr), ), Func( toon.loop, 'neutral' ), ) return toonTrack def unprint(self, string): print(string) def startInteraction(self): #print("### LawnDecor: startInteraction") place = base.cr.playGame.getPlace() if place: place.detectedGardenPlotUse() base.localAvatar.setInGardenAction(self) def finishInteraction(self): #print("### LawnDecor: finishInteraction") if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): base.cr.playGame.getPlace().detectedGardenPlotDone() self.notify.debug( "done interaction") else: self.notify.warning('base.cr.playGame.getPlace() does not have detectedGardenPlotDone') # maybe they went bye-bye? if hasattr(base, 'localAvatar'): base.localAvatar.handleEndPlantInteraction(self) def startCamIval(self, avId): track = Sequence() if avId == localAvatar.doId: track = Sequence( Func(base.localAvatar.disableSmartCameraViews), Func(base.localAvatar.setCameraPosForPetInteraction), ) return track def stopCamIval(self, avId): track = Sequence() if avId == localAvatar.doId: track = Sequence( Func(base.localAvatar.unsetCameraPosForPetInteraction), Wait(0.8), Func(base.localAvatar.enableSmartCameraViews), ) return track def canBeWatered(self): return 0 def getShovelAction(self): return None def getShovelCommand(self): return None def canBePlanted(self): return 0 def movieDone(self): self.sendUpdate('movieDone', []) # all lawn decor items should be able to be placed/planted and removed def setMovie(self, mode, avId): if mode == GardenGlobals.MOVIE_FINISHPLANTING: self.doFinishPlantingTrack(avId) elif mode == GardenGlobals.MOVIE_REMOVE: self.doDigupTrack(avId) def finishMovies(self): if self.movie: self.movie.finish() self.movie = None def doDigupTrack(self, avId): toon = base.cr.doId2do.get(avId) if not toon: return self.finishMovies() self.model.setTransparency(1) self.model.setAlphaScale(1) # load the shovel shovel = toon.attachShovel() shovel.hide() moveTrack = self.generateToonMoveTrack(toon) digupTrack = self.generateDigupTrack(toon) self.movie = Sequence(self.startCamIval(avId), moveTrack, Func(shovel.show), digupTrack, #self.stopCamIval(avId), ) if avId == localAvatar.doId: self.expectingReplacement = 1 #track.append(Func(self.finishInteraction)) self.movie.append(Func(self.movieDone)) self.movie.start() def generateDigupTrack( self, toon): """ """ sound = loader.loadSfx('phase_5.5/audio/sfx/burrow.mp3') sound.setPlayRate(0.5) pos = self.model.getPos() pos.setZ(pos[2]-1) track = Parallel() track.append( Sequence( ActorInterval( toon, "start-dig" ), Parallel(ActorInterval( toon, "loop-dig", loop=1, duration=5.13 ), Sequence(Wait(0.25), SoundInterval(sound, node=toon, duration=0.55), Wait(0.80), SoundInterval(sound, node=toon, duration=0.55), Wait(1.35), SoundInterval(sound, node=toon, duration=0.55), ), ), ActorInterval( toon, "start-dig", playRate=-1 ), LerpFunc(self.model.setAlphaScale, fromData=1, toData=0, duration=1), Func(toon.loop, 'neutral'), Func(toon.detachShovel), #LerpPosInterval(self.model, 3, pos), ), ) return track def doFinishPlantingTrack(self, avId): toon = base.cr.doId2do.get(avId) if not toon: return self.finishMovies() self.movie = Sequence() if avId == localAvatar.doId: self.startInteraction() if self.model: self.model.setTransparency(1) self.model.setAlphaScale(0) self.movie.append(LerpFunc(self.model.setAlphaScale, fromData=0, toData=1, duration=3)) self.movie.append(self.stopCamIval(avId)) self.movie.append(Func( toon.detachShovel )) self.movie.append(Func( toon.loop, 'neutral')) #import pdb; pdb.set_trace() if avId == localAvatar.doId: self.movie.append(Func(self.finishInteraction)) self.movie.append(Func(self.movieDone)) if hasattr(self, 'doResultDialog'): self.movie.append(Func(self.doResultDialog)) self.movie.start() def interactionDenied(self, avId): """ server said someone else was using the plant, we can't do the shovel or wateringCan action """ if avId == localAvatar.doId: self.finishInteraction()
class CogdoFlyingLevel(DirectObject): notify = directNotify.newCategory('CogdoFlyingLevel') def __init__(self, parent, frameModel, startPlatformModel, endPlatformModel, quadLengthUnits, quadVisibilityAhead, quadVisibiltyBehind): self.parent = parent self.quadLengthUnits = quadLengthUnits self._halfQuadLengthUnits = quadLengthUnits / 2.0 self.quadVisibiltyAhead = quadVisibilityAhead self.quadVisibiltyBehind = quadVisibiltyBehind self._frameModel = frameModel self.root = NodePath('CogdoFlyingLevel') self.quadrantRoot = NodePath('QuadrantsRoot') self.quadrantRoot.reparentTo(self.root) self._startPlatformModel = startPlatformModel self._startPlatformModel.reparentTo(self.root) self._startPlatformModel.setZ(Globals.Level.StartPlatformHeight) self._endPlatformModel = endPlatformModel self._endPlatformModel.reparentTo(self.root) self._endPlatformModel.setZ(Globals.Level.EndPlatformHeight) self.wallR = self._frameModel.find('**/wallR') self.wallL = self._frameModel.find('**/wallL') self._exit = CogdoGameExit() self._exit.reparentTo(self._endPlatformModel) loc = self._endPlatformModel.find('**/exit_loc') offset = loc.getPos(render) self._exit.setPos(render, offset) self.quadrants = [] self.visibleQuadIndices = [] self._numQuads = 0 self._currentQuadNum = -1 self._camera = None self._initCollisions() self.upLimit = self._frameModel.find('**/limit_up').getZ(render) self.downLimit = self._frameModel.find('**/limit_down').getZ(render) self.leftLimit = self._frameModel.find('**/limit_left').getX( render) - 30.0 self.rightLimit = self._frameModel.find('**/limit_right').getX( render) + 30.0 self.backLimit = -self.quadLengthUnits self.forwardLimit = self.quadLengthUnits * 20 self._frameModel.flattenStrong() self.gatherableFactory = CogdoFlyingGatherableFactory() self.obstacleFactory = CogdoFlyingObtacleFactory() return def getExit(self): return self._exit def getBounds(self): return ((self.leftLimit, self.rightLimit), (self.backLimit, self.forwardLimit), (self.downLimit, self.upLimit)) def getGatherable(self, serialNum): for quadrant in self.quadrants: for gatherable in quadrant.gatherables: if gatherable.serialNum == serialNum: return gatherable return None def ready(self): self.gatherableFactory.destroy() del self.gatherableFactory self.obstacleFactory.destroy() del self.obstacleFactory self._initStartEndPlatforms() self._frameModel.reparentTo(self.root) self.root.reparentTo(self.parent) self.root.stash() def _initStartEndPlatforms(self): self.startPlatform = CogdoFlyingPlatform( self._startPlatformModel, Globals.Level.PlatformTypes.StartPlatform) self.endPlatform = CogdoFlyingPlatform( self._endPlatformModel, Globals.Level.PlatformTypes.EndPlatform) self._endPlatformModel.setY(self.convertQuadNumToY(self._numQuads)) self.backLimit = self._startPlatformModel.getY( render) - Globals.Level.StartPlatformLength * 0.7 self.forwardLimit = self._endPlatformModel.getY( render) + Globals.Level.EndPlatformLength * 0.7 def _initCollisions(self): self.collPlane = CollisionPlane( Plane(Vec3(0, 0, 1.0), Point3(0, 0, 10))) self.collPlane.setTangible(0) self.collNode = CollisionNode('fogPlane') self.collNode.setIntoCollideMask(OTPGlobals.FloorBitmask) self.collNode.addSolid(self.collPlane) self.collNodePath = self.root.attachNewNode(self.collNode) self.collNodePath.hide() def destroy(self): del self.collPlane self.collNodePath.removeNode() del self.collNodePath del self.collNode for quadrant in self.quadrants: quadrant.destroy() self._exit.destroy() del self._exit self.root.removeNode() del self.root def onstage(self): self.root.unstash() self.update(0.0) def offstage(self): self.root.stash() def start(self, startTime=0.0): self._startTime = startTime def stop(self): pass def getLength(self): return self.quadLengthUnits * self.getNumQuadrants() def appendQuadrant(self, model): quadrant = CogdoFlyingLevelQuadrant(self._numQuads, model, self, self.root) if self._numQuads == 0: quadrant.generateGatherables(self._startPlatformModel) quadrant.offstage() self.quadrants.append(quadrant) self._numQuads = len(self.quadrants) def getNumQuadrants(self): return self._numQuads def setCamera(self, camera): self._camera = camera def getCameraActualQuadrant(self): camY = self._camera.getY(render) y = self.root.getY(render) return self.convertYToQuadNum(camY - y) def update(self, dt=0.0): if self._camera is None: return quadNum = clamp(self.getCameraActualQuadrant(), 0, self._numQuads - 1) if quadNum < self._numQuads: self.quadrants[quadNum].update(dt) if quadNum + 1 < self._numQuads: self.quadrants[quadNum + 1].update(dt) if quadNum != self._currentQuadNum: self._switchToQuadrant(quadNum) return def _switchToQuadrant(self, quadNum): self.visibleQuadIndices = [] if quadNum >= 0: if quadNum > 0: self.quadrants[max(quadNum - self.quadVisibiltyBehind, 0)].onstage() for i in range( quadNum, min(quadNum + self.quadVisibiltyAhead + 1, self._numQuads)): self.quadrants[i].onstage() self.visibleQuadIndices.append(i) if i == 0: self.startPlatform.onstage() elif i == self._numQuads - 1: self.endPlatform.onstage() self._currentQuadNum = quadNum for i in list( range(0, max(self._currentQuadNum - self.quadVisibiltyBehind, 0))) + list( range( min( self._currentQuadNum + self.quadVisibiltyAhead + 1, self._numQuads), self._numQuads)): self.quadrants[i].offstage() if i == 0: self.startPlatform.offstage() elif i == self._numQuads - 1: self.endPlatform.offstage() def convertQuadNumToY(self, quadNum): return quadNum * self.quadLengthUnits def convertYToQuadNum(self, y): return int(y / self.quadLengthUnits) def convertCenterYToQuadNum(self, y): return self.convertYToQuadNum(y + self._halfQuadLengthUnits)
class DistributedPartyActivity(DistributedObject.DistributedObject): """ Base class for Distributed Party Activity objects on the client side. A distributed party activity constitutes of any game or area at a party that involves multiple toons interacting with it at the same time. Note that a new notify category is not created here as this class expects subclasses to create it. """ def __init__(self, cr, activityId, activityType, wantLever=False, wantRewardGui=False): DistributedObject.DistributedObject.__init__(self, cr) self.activityId = activityId self.activityName = PartyGlobals.ActivityIds.getString(self.activityId) self.activityType = activityType self.wantLever = wantLever self.wantRewardGui = wantRewardGui self.messageGui = None self.rewardGui = None self.toonIds = [] # list of doIds of toons in this activity # related-object requests self._toonId2ror = {} # Put a reference to this activity on base, for easy debugging # access, this will get cleaned up in DistributedPartyActivity childName = "%s" % self childName = childName[childName.rfind(".DistributedParty") + len(".DistributedParty"):childName. rfind("Activity instance")] if not hasattr(base, "partyActivityDict"): base.partyActivityDict = {} base.partyActivityDict[childName] = self self.root = NodePath('root') # This label is used to give status of the activity to the player # self.activityStatusLabel = DirectLabel( # text = TTLocalizer.PartyActivityWaitingForOtherPlayers, # text_fg = VBase4(1,1,1,1), # relief = None, # pos = (-0.6, 0, -0.75), # scale = 0.075) # self.activityStatusLabel.hide() # Play Activity's party activity state data throws this when the local toon # is done reading the rules self.rulesDoneEvent = "rulesDone" # for the load bar self.modelCount = 500 self.cleanupActions = [] # these are flags that the subclass can manipulate to keep # this base class from messing with the toons as the activity # starts up; see setUsesSmoothing and setUsesLookAround below self.usesSmoothing = 0 self.usesLookAround = 0 # difficulty debug overrides self.difficultyOverride = None self.trolleyZoneOverride = None self._localToonRequestStatus = None #------------------------------------------------------------------------------- # join/exit request functions #------------------------------------------------------------------------------- def localToonExiting(self): assert self.notify.debugStateCall(self) self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Exiting def localToonJoining(self): assert self.notify.debugStateCall(self) self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Joining # Distributed (clsend airecv) def d_toonJoinRequest(self): """ Send request for local toon to join the activity. Expect a joinRequestDenied or handleToonJoined in reply. """ if self._localToonRequestStatus is None: assert (self.notify.debug("d_toonJoinRequest")) self.localToonJoining() self.sendUpdate("toonJoinRequest") else: assert (self.notify.debug( "d_toonJoinRequest not sending request as _localToonRequest=%d" % self._localToonRequest)) # Distributed (clsend airecv) def d_toonExitRequest(self): """ Requests to for local toon to drop out of the activity. Expect a exitRequestDenied or handleToonExited in reply. """ if self._localToonRequestStatus is None: assert (self.notify.debug("d_toonExitRequest")) self.localToonExiting() self.sendUpdate("toonExitRequest") else: assert (self.notify.debug( "d_toonExitRequest not sending request as _localToonRequest=%d" % self._localToonRequest)) # Distributed (clsend airecv) def d_toonExitDemand(self): """ Tells AI that the local toon is leaving the activity. Expect a toonExitResponse in reply. """ assert (self.notify.debug("d_toonExitDemand")) self.localToonExiting() self.sendUpdate("toonExitDemand") #------------------------------------------------------------------------------- # join/exit functions that subclasses should override #------------------------------------------------------------------------------- def joinRequestDenied(self, reason): """ Called when the client's request to join an activity has been denied. Subclasses can override this function. Parameters: reason -- a PartyGlobals.DenailReasons value """ self._localToonRequestStatus = None def exitRequestDenied(self, reason): """ Called when the client's request to exit an activity has been denied. Subclasses can override this function. Parameters: reason -- a PartyGlobals.DenailReasons value """ self._localToonRequestStatus = None def handleToonJoined(self, toonId): """ Whenever a new toon joins the activity, this function is called. Subclasses should override this function. Parameters: toonId -- doId of the toon that joined """ self.notify.error("BASE: handleToonJoined should be overridden %s" % self.activityName) def handleToonExited(self, toonId): """ Whenever a toon exits the activity, this function is called. Subclasses should override this function. Parameters: toonId -- doId of the toon that exited """ self.notify.error("BASE: handleToonExited should be overridden %s" % self.activityName) def handleToonDisabled(self, toonId): """ A toon dropped unexpectedly from the game. Handle it! """ self.notify.error("BASE: handleToonDisabled should be overridden %s" % self.activityName) #------------------------------------------------------------------------------- # Distributed (broadcast ram) def setToonsPlaying(self, toonIds): """ Broadcast response from the server that sends out the list of toons currently in the party activity. This is for all clients to properly sync up states for the toons in the activity. Parameters: toonIds -- is a list of all the toons in the activity """ assert (self.notify.debug("BASE: setToonsPlaying = %s" % toonIds)) # Split list into who joined and who exited: (exitedToons, joinedToons) = self.getToonsPlayingChanges(self.toonIds, toonIds) assert (self.notify.debug("\texitedToons: %s" % exitedToons)) assert (self.notify.debug("\tjoinedToons: %s" % joinedToons)) self.setToonIds(toonIds) self._processExitedToons(exitedToons) self._processJoinedToons(joinedToons) def _processExitedToons(self, exitedToons): """Handle the exited toons""" for toonId in exitedToons: if (toonId != base.localAvatar.doId or (toonId == base.localAvatar.doId and self.isLocalToonRequestStatus( PartyGlobals.ActivityRequestStatus.Exiting))): toon = self.getAvatar(toonId) if toon is not None: self.ignore(toon.uniqueName("disable")) self.handleToonExited(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None if toonId in self._toonId2ror: self.cr.relatedObjectMgr.abortRequest( self._toonId2ror[toonId]) del self._toonId2ror[toonId] def _processJoinedToons(self, joinedToons): """Handle the joining toons""" for toonId in joinedToons: # Only trigger handleToonJoined if it isn't the local Toon # or if the local Toon is joining this activity. if (toonId != base.localAvatar.doId or (toonId == base.localAvatar.doId and self.isLocalToonRequestStatus( PartyGlobals.ActivityRequestStatus.Joining))): if toonId not in self._toonId2ror: request = self.cr.relatedObjectMgr.requestObjects( [toonId], allCallback=self._handlePlayerPresent) if toonId in self._toonId2ror: # toon is already here del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = request def _handlePlayerPresent(self, toons): toon = toons[0] toonId = toon.doId if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: # toon is already here self._toonId2ror[toonId] = None self._enableHandleToonDisabled(toonId) self.handleToonJoined(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None def _enableHandleToonDisabled(self, toonId): toon = self.getAvatar(toonId) if toon is not None: self.acceptOnce( toon.uniqueName("disable"), self.handleToonDisabled, [toonId], ) else: self.notify.warning( "BASE: unable to get handle to toon with toonId:%d. Hook for handleToonDisabled not set." % toonId) def isLocalToonRequestStatus(self, requestStatus): return (self._localToonRequestStatus == requestStatus) def setToonIds(self, toonIds): """Updates the list of toon ids in the activity""" self.toonIds = toonIds def getToonsPlayingChanges(self, oldToonIds, newToonIds): """ Returns a tuple of (list of doIds of toons who exited, list of doIds of toons who joined) """ oldToons = set(oldToonIds) newToons = set(newToonIds) # find toons that are no longer in the game exitedToons = oldToons.difference(newToons) # find toons that just joined the game joinedToons = newToons.difference(oldToons) # return results return (list(exitedToons), list(joinedToons)) def setUsesSmoothing(self): self.usesSmoothing = True def setUsesLookAround(self): self.usesLookAround = True # def getTitle(self): # """ # Return the title of the party activity. # Subclasses should redefine. # """ # return TTLocalizer.DefaultPartyActivityTitle def getInstructions(self): """ Return the instructions for the party activity. Subclasses should redefine. """ return TTLocalizer.DefaultPartyActivityInstructions def getParentNodePath(self): """ Overwritten: Originally returns render. Returns Place NodePath. """ if hasattr(base.cr.playGame, "hood") and base.cr.playGame.hood and \ hasattr(base.cr.playGame.hood, "loader") and base.cr.playGame.hood.loader \ and hasattr(base.cr.playGame.hood.loader, "geom") and base.cr.playGame.hood.loader.geom: return base.cr.playGame.hood.loader.geom else: self.notify.warning( "Hood or loader not created, defaulting to render") return render def __createRandomNumGen(self): self.notify.debug("BASE: self.doId=0x%08X" % self.doId) # seed the random number generator with the party activity doId self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) def destroy(self=self): self.notify.debug("BASE: destroying random num gen") del self.randomNumGen self.cleanupActions.append(destroy) def generate(self): DistributedObject.DistributedObject.generate(self) self.notify.debug("BASE: generate, %s" % self.getTitle()) self.__createRandomNumGen() def announceGenerate(self): """ announceGenerate is called after all of the required fields are filled in """ DistributedObject.DistributedObject.announceGenerate(self) self.notify.debug("BASE: announceGenerate %s" % self.activityName) # update root's name and position within the party grounds self.root.setName(self.activityName + "Root") centeredX, centeredY = getCenterPosFromGridSize( self.x, self.y, PartyGlobals.ActivityInformationDict[self.activityId]["gridsize"]) self.root.setPos(centeredX, centeredY, 0.0) self.root.setH(self.h) # if this flag is set to zero, we won't notify the server that # we've left at the end of the activity self.normalExit = True if self.wantLever: self.leverTriggerEvent = self.uniqueName('leverTriggerEvent') self.load() def cleanup(self=self): self.notify.debug("BASE: cleanup: normalExit=%s" % self.normalExit) # make sure we clear the screen base.cr.renderFrame() # If we didn't abort, tell the AI we are exiting if self.normalExit: self.sendUpdate("toonExitRequest") self.cleanupActions.append(cleanup) def disable(self): self.notify.debug("BASE: disable") DistributedObject.DistributedObject.disable(self) rorToonIds = list(self._toonId2ror.keys()) for toonId in rorToonIds: self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) del self._toonId2ror[toonId] self.ignore(self.messageDoneEvent) if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None def delete(self): self.notify.debug("BASE: delete") self.unload() # make sure we're not accepting any events self.ignoreAll() DistributedObject.DistributedObject.delete(self) def load(self): self.notify.debug("BASE: load") # Load the sign for this activity self.loadSign() # Load the lever (if applicable) if self.wantLever: self.loadLever() if self.wantRewardGui: self.showRewardDoneEvent = self.uniqueName("showRewardDoneEvent") self.rewardGui = JellybeanRewardGui(self.showRewardDoneEvent) self.messageDoneEvent = self.uniqueName("messageDoneEvent") self.root.reparentTo(self.getParentNodePath()) self._enableCollisions() def loadSign(self): actNameForSign = self.activityName if self.activityId == PartyGlobals.ActivityIds.PartyJukebox40: actNameForSign = PartyGlobals.ActivityIds.getString( PartyGlobals.ActivityIds.PartyJukebox) elif self.activityId == PartyGlobals.ActivityIds.PartyDance20: actNameForSign = PartyGlobals.ActivityIds.getString( PartyGlobals.ActivityIds.PartyDance) self.sign = self.root.attachNewNode('%sSign' % self.activityName) self.signModel = self.party.defaultSignModel.copyTo(self.sign) self.signFlat = self.signModel.find("**/sign_flat") self.signFlatWithNote = self.signModel.find("**/sign_withNote") self.signTextLocator = self.signModel.find("**/signText_locator") textureNodePath = getPartyActivityIcon(self.party.activityIconsModel, actNameForSign) textureNodePath.setPos(0.0, -0.02, 2.2) textureNodePath.setScale(2.35) textureNodePath.copyTo(self.signFlat) textureNodePath.copyTo(self.signFlatWithNote) text = TextNode("noteText") text.setTextColor(0.2, 0.1, 0.7, 1.0) text.setAlign(TextNode.ACenter) text.setFont(OTPGlobals.getInterfaceFont()) text.setWordwrap(10.0) text.setText("") self.noteText = self.signFlatWithNote.attachNewNode(text) self.noteText.setPosHpr(self.signTextLocator, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0) self.noteText.setScale(0.2) self.signFlatWithNote.stash() self.signTextLocator.stash() def loadLever(self): """ SubClasses can override this if they want to move their lever somewhere special... call this, then change the position. """ self.lever = self.root.attachNewNode('%sLever' % self.activityName) self.leverModel = self.party.defaultLeverModel.copyTo(self.lever) # Do some crazy reparenting so you can scale the whole thing nicely self.controlColumn = NodePath('cc') column = self.leverModel.find('**/column') column.getChildren().reparentTo(self.controlColumn) self.controlColumn.reparentTo(column) self.stickHinge = self.controlColumn.attachNewNode('stickHinge') self.stick = self.party.defaultStickModel.copyTo(self.stickHinge) self.stickHinge.setHpr(0.0, 90.0, 0.0) self.stick.setHpr(0, -90.0, 0) self.stick.flattenLight() self.bottom = self.leverModel.find('**/bottom') self.bottom.wrtReparentTo(self.controlColumn) self.bottomPos = self.bottom.getPos() # Make a trigger sphere so we can detect when the local avatar # runs up to the lever. cs = CollisionSphere(0.0, 1.35, 2.0, 1.0) cs.setTangible(False) cn = CollisionNode(self.leverTriggerEvent) cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTrigger = self.root.attachNewNode(cn) self.leverTrigger.reparentTo(self.lever) self.leverTrigger.stash() # Also, a solid tube to keep us from running through the # lever itself. This one scales with the control # model. cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2) cn = CollisionNode('levertube') cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTube = self.leverModel.attachNewNode(cn) # Let's set the height of the lever to the height of the host host = base.cr.doId2do.get(self.party.partyInfo.hostId) if host is None: self.notify.debug( "%s loadLever : Host has left the game before lever could be created." % self.activityName) return # We start by figuring out where we are going by setting the # scale and position appropriately # origScale = self.leverModel.getSz() # origCcPos = self.controlColumn.getPos() # origBottomPos = self.bottom.getPos() # origStickHingeHpr = self.stickHinge.getHpr() # First, scale the thing overall to match the host's scale, # including cheesy effect scales. scale = host.getGeomNode().getChild(0).getSz(render) self.leverModel.setScale(scale) # Then get the position of the host's right hand when he's # standing at the controls in a leverNeutral pose. self.controlColumn.setPos(0, 0, 0) host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0) host.pose('leverNeutral', 0) host.update() pos = host.rightHand.getPos(self.controlColumn) # Now set the control column to the right height and position # to put the top of the stick approximately in his hand. self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1) # And put the bottom piece back on the floor, wherever that # is from here. self.bottom.setZ(host, 0.0) self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) # Also put the joystick in his hand. lookAtPoint = Point3(0.3, 0, 0.1) lookAtUp = Vec3(0, -1, 0) self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp) host.play('walk') host.update() def unloadLever(self): self.lever.removeNode() self.leverModel.removeNode() self.controlColumn.removeNode() self.stickHinge.removeNode() self.stick.removeNode() self.bottom.removeNode() self.leverTrigger.removeNode() self.leverTube.removeNode() del self.bottomPos del self.lever del self.leverModel del self.controlColumn del self.stickHinge del self.stick del self.bottom del self.leverTrigger del self.leverTube def _enableCollisions(self): if self.wantLever: self.leverTrigger.unstash() self.accept("enter%s" % self.leverTriggerEvent, self._leverPulled) def _disableCollisions(self): if self.wantLever: self.leverTrigger.stash() self.ignore("enter%s" % self.leverTriggerEvent) def _leverPulled(self, collEntry): """ This method is called when a toon collides with the activity's lever. Returns False if the activity should not be allowed to start due to it being a host initiated activity. Returns True otherwise. Subclasses should override, check the result of this method, and take appropriate action. """ self.notify.debug("_leverPulled : Someone pulled the lever!!! ") if (self.activityType == PartyGlobals.ActivityTypes.HostInitiated) and \ (base.localAvatar.doId != self.party.partyInfo.hostId): return False return True def getToonPullingLeverInterval(self, toon): walkTime = 0.2 reach = ActorInterval(toon, 'leverReach', playRate=2.0) pull = ActorInterval(toon, 'leverPull', startFrame=6) origPos = toon.getPos(render) origHpr = toon.getHpr(render) newPos = self.lever.getPos(render) newHpr = self.lever.getHpr(render) origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) toon.setPosHpr(origPos, origHpr) reachAndPull = Sequence( ActorInterval(toon, 'walk', loop=True, duration=walkTime - reach.getDuration()), reach, pull) leverSeq = Sequence( Wait(walkTime + reach.getDuration() - 0.1), self.stick.hprInterval(0.55, Point3(0.0, 25.0, 0.0), Point3(0.0, 0.0, 0.0)), Wait(0.3), self.stick.hprInterval(0.4, Point3(0.0, 0.0, 0.0), Point3(0.0, 25.0, 0.0)), ) returnSeq = Sequence( Parallel( toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), leverSeq, reachAndPull, ), ) return returnSeq def showMessage(self, message, endState='walk'): assert self.notify.debug("showMessage (endState=%s)" % endState) base.cr.playGame.getPlace().fsm.request("activity") self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) self.messageGui = TTDialog.TTGlobalDialog( doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge, ) self.messageGui.endState = endState def __handleMessageDone(self): self.ignore(self.messageDoneEvent) if hasattr(base.cr.playGame.getPlace(), 'fsm'): if self.messageGui and hasattr(self.messageGui, 'endState'): self.notify.info("__handleMessageDone (endState=%s)" % self.messageGui.endState) base.cr.playGame.getPlace().fsm.request( self.messageGui.endState) else: self.notify.warning( "messageGui has no endState, defaulting to 'walk'") base.cr.playGame.getPlace().fsm.request('walk') if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None def showJellybeanReward(self, earnedAmount, jarAmount, message): """ Subclasses may call this to show the local player how many jellybeans they got for participating in an activity. Parameters: earnedAmount -- How many jellybeans the toon gets jarAmount -- Amount in their pocketbook jar message -- Activity-specific information to display while showing the jellybean reward animation. """ # If the local toon is not in any activity or in this activity. if (not self.isLocalToonInActivity()) or (base.localAvatar.doId in self.getToonIdsAsList()): # pop up a gui element that shows the message and has room to display # a jellybean animation messenger.send('DistributedPartyActivity-showJellybeanReward') base.cr.playGame.getPlace().fsm.request("activity") self.acceptOnce(self.showRewardDoneEvent, self.__handleJellybeanRewardDone) self.rewardGui.showReward(earnedAmount, jarAmount, message) def __handleJellybeanRewardDone(self): """ The player is done viewing their jellybean reward. Clean up. """ self.ignore(self.showRewardDoneEvent) self.handleRewardDone() def handleRewardDone(self): if base.cr.playGame.getPlace() and \ hasattr(base.cr.playGame.getPlace(), "fsm"): base.cr.playGame.getPlace().fsm.request('walk') def setSignNote(self, note): self.noteText.node().setText(note) if len(note.strip()) > 0: self.signFlat.stash() self.signFlatWithNote.unstash() self.signTextLocator.unstash() else: self.signFlat.unstash() self.signFlatWithNote.stash() self.signTextLocator.stash() def unload(self): self.notify.debug("BASE: unload") self.finishRules() self._disableCollisions() # self.activityStatusLabel.destroy() # del self.activityStatusLabel self.signModel.removeNode() del self.signModel self.sign.removeNode() del self.sign self.ignoreAll() if self.wantLever: self.unloadLever() self.root.removeNode() del self.root del self.activityId del self.activityName del self.activityType del self.wantLever del self.messageGui if self.rewardGui is not None: self.rewardGui.destroy() del self.rewardGui # some subclasses redefine this, so they might have already cleaned it # up at this point if hasattr(self, "toonIds"): del self.toonIds del self.rulesDoneEvent del self.modelCount del self.cleanupActions del self.usesSmoothing del self.usesLookAround del self.difficultyOverride del self.trolleyZoneOverride if hasattr(base, 'partyActivityDict'): del base.partyActivityDict # Distributed (required broadcast) def setPartyDoId(self, partyDoId): self.party = base.cr.doId2do[partyDoId] # Distributed (required broadcast) def setX(self, x): self.x = x # Distributed (required broadcast) def setY(self, y): self.y = y # Distributed (required broadcast) def setH(self, h): self.h = h # Distributed (broadcast ram) def setState(self, newState, timestamp): """ Subclasses must extend this function to make the actual request to its activityFSM. We do not do it here as we do not know which parameters beyond newState that each activity's fsm's states will need. """ if newState == "Active": self.activityStartTime = globalClockDelta.networkToLocalTime( timestamp) def turnOffSmoothingOnGuests(self): # Disable smoothing, etc. for all the toons in the activity by default. # Some activities (e.g. the tag activity) may want to turn this back # on, but most activitys want full control over the toons' # placement onscreen. for toonId in self.toonIds: # Find the actual avatar in the cr avatar = self.getAvatar(toonId) if avatar: if not self.usesSmoothing: avatar.stopSmooth() if not self.usesLookAround: avatar.stopLookAround() def getAvatar(self, toonId): """ Instead of all the party activitys writing code to do avatar lookups based on toonIds, they should use this function. It returns a toon if that toon is in the doId2do. If the ID cannot be resolved for some reason, a warning is logged and we return None. Each activity will have to deal with this. Why would an avatar not be in the doId2do? It can happen when an avatar quits the activity early but we still get an activity update afterwards. """ # If it is an avatar, look it up in the doid2do if toonId in self.cr.doId2do: return self.cr.doId2do[toonId] # I do not know what this toonId is else: self.notify.warning( "BASE: getAvatar: No avatar in doId2do with id: " + str(toonId)) return None def getAvatarName(self, toonId): avatar = self.getAvatar(toonId) if avatar: return avatar.getName() else: return "Unknown" def isLocalToonInActivity(self): """ Returns True if the local toon is in an activity, False otherwise. Sub-classes should use this to ensure the local toon isn't in another activity when they request to join this activity. This is mostly to prevent bug associated with hitting another activity when flying after being shot from the cannon. """ result = False place = base.cr.playGame.getPlace() # fsm will be missing if this is called after the Party place obj is unloaded if (place and (place.__class__.__name__ == 'Party') and hasattr(place, 'fsm') and place.fsm): result = place.fsm.getCurrentState().getName() == "activity" return result def getToonIdsAsList(self): """ Returns a list of doId's of all toons in this activity. Sub-classes should override this if they change how toon doId's are stored. """ return self.toonIds def startRules(self, timeout=PartyGlobals.DefaultRulesTimeout): self.notify.debug("BASE: startRules") self.accept(self.rulesDoneEvent, self.handleRulesDone) # The rules panel is an onscreen panel self.rulesPanel = MinigameRulesPanel( "PartyRulesPanel", self.getTitle(), self.getInstructions(), self.rulesDoneEvent, timeout, ) # turn off use of all the bottom cells, and the cell nearest the bottom # on each side base.setCellsAvailable( base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) self.rulesPanel.load() self.rulesPanel.enter() def finishRules(self): self.notify.debug("BASE: finishRules") # Hide the rules self.ignore(self.rulesDoneEvent) if hasattr(self, "rulesPanel"): self.rulesPanel.exit() self.rulesPanel.unload() del self.rulesPanel base.setCellsAvailable( base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) def handleRulesDone(self): self.notify.error("BASE: handleRulesDone should be overridden") def getTitle(self): # Used by rulesPanel return TTLocalizer.PartyActivityNameDict[self.activityId]["generic"] # time-related utility functions def local2ActivityTime(self, timestamp): """ given a local-time timestamp, returns the corresponding timestamp relative to the start of the activity """ return timestamp - self.activityStartTime def activity2LocalTime(self, timestamp): """ given a activity-time timestamp, returns the corresponding local timestamp """ return timestamp + self.activityStartTime def getCurrentActivityTime(self): return self.local2ActivityTime(globalClock.getFrameTime()) # disableEmotes and enableEmotes can be overidden by the base # classes if different settings are wanted. But for most # party activities, we don't want emotes to be enabled # That is, we don't want toons going into different animations when they # speedchat... def disableEmotes(self): Emote.globalEmote.disableAll(base.localAvatar) def enableEmotes(self): Emote.globalEmote.releaseAll(base.localAvatar)
class CogdoMazeDoor: def __init__(self, closedDoorModel, openDoorModel): self.model = NodePath("CogdoMazeDoor") self.model.setPos(0, 0, 0) self.model.reparentTo(render) self.closedDoorModel = closedDoorModel self.closedDoorModel.reparentTo(self.model) self.openDoorModel = openDoorModel self.openDoorModel.reparentTo(self.model) self.openDoorModel.stash() self.lockId2lock = {} self._open = False self.revealed = False self.players = [] self._initCollisions() def setPosition(self, x, y): self.model.setPos(x, y, 2.5) def _initCollisions(self): name = "CogdoMazeDoor" collSphere = CollisionSphere(0, 0, 0.0, 0.25) collSphere.setTangible(0) collNode = CollisionNode(name) collNode.setFromCollideMask(ToontownGlobals.CatchGameBitmask) collNode.addSolid(collSphere) self.collNP = self.model.attachNewNode(collNode) self.enterCollisionEventName = "enter" + name def destroy(self): self.model.removeNode() del self.model del self.openDoorModel del self.closedDoorModel for lock in list(self.lockId2lock.values()): lock.destroy() del self.lockId2lock def onstage(self): self.model.unstash() def offstage(self): self.model.stash() def open(self): self._open = True self.closedDoorModel.stash() self.openDoorModel.unstash() def close(self): self._open = False self.closedDoorModel.unstash() self.openDoorModel.stash() def addLock(self, lock): self.lockId2lock[lock.id] = lock def unlock(self, lockId): lock = self.lockId2lock.get(lockId) if lock is not None and lock.isLocked(): lock.unlock() return True return False def getLocks(self): return list(self.lockId2lock.values()) def getLock(self, lockId): return self.lockId2lock.get(lockId) def isLocked(self): return True in [lock.isLocked() for lock in list(self.lockId2lock.values())] def playerEntersDoor(self, player): self.players.append(player) def getPlayerCount(self): return len(self.players)
class DistributedButterfly(DistributedObject.DistributedObject): notify = DirectNotifyGlobal.directNotify.newCategory( 'DistributedButterfly') id = 0 # wings_1 (solid yellow) # wings_2 (yellow w/ dots) (1, 1, 1), (0.2, 0, 1), (1, 0, 1), (0.8, 0, 1) # wings_3 (solid white) (0.8, 0, 0.8), (0, 0.8, 0.8), (0.9, 0.4, 0.6) # (0.9, 0.4, 0.4), (0.8, 0.5, 0.9), (0.4, 0.1, 0.7) # wings_4 (white w/ dots) # wings_5 (pale yellow w/ lines) (0.8, 0, 0.8), (0.6, 0.6, 0.9) # (0.7, 0.6, 0.9), (0.8, 0.6, 0.9), (0.9, 0.6, 0.9), # (1, 0.6, 0.9) # wings_6 (blue & yellow) wingTypes = ('wings_1', 'wings_2', 'wings_3', 'wings_4', 'wings_5', 'wings_6') yellowColors = (Vec4(1, 1, 1, 1), Vec4(0.2, 0, 1, 1), Vec4(0.8, 0, 1, 1)) whiteColors = (Vec4(0.8, 0, 0.8, 1), Vec4(0, 0.8, 0.8, 1), Vec4(0.9, 0.4, 0.6, 1), Vec4(0.9, 0.4, 0.4, 1), Vec4(0.8, 0.5, 0.9, 1), Vec4(0.4, 0.1, 0.7, 1)) paleYellowColors = (Vec4(0.8, 0, 0.8, 1), Vec4(0.6, 0.6, 0.9, 1), Vec4(0.7, 0.6, 0.9, 1), Vec4(0.8, 0.6, 0.9, 1), Vec4(0.9, 0.6, 0.9, 1), Vec4(1, 0.6, 0.9, 1)) shadowScaleBig = Point3(0.07, 0.07, 0.07) shadowScaleSmall = Point3(0.01, 0.01, 0.01) def __init__(self, cr): """__init__(cr) """ DistributedObject.DistributedObject.__init__(self, cr) self.fsm = ClassicFSM.ClassicFSM( 'DistributedButterfly', [ State.State('off', self.enterOff, self.exitOff, ['Flying', 'Landed']), State.State('Flying', self.enterFlying, self.exitFlying, ['Landed']), State.State('Landed', self.enterLanded, self.exitLanded, ['Flying']) ], # Initial State 'off', # Final State 'off', ) self.butterfly = None self.butterflyNode = None self.curIndex = 0 self.destIndex = 0 self.time = 0.0 self.ival = None self.fsm.enterInitialState() def generate(self): """generate(self) This method is called when the DistributedObject is reintroduced to the world, either for the first time or from the cache. """ DistributedObject.DistributedObject.generate(self) if self.butterfly: return self.butterfly = Actor.Actor() self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod.bam') self.butterfly.loadAnims({ 'flutter': 'phase_4/models/props/SZ_butterfly-flutter.bam', 'glide': 'phase_4/models/props/SZ_butterfly-glide.bam', 'land': 'phase_4/models/props/SZ_butterfly-land.bam' }) # Randomly choose one of the butterfly wing patterns index = self.doId % len(self.wingTypes) chosenType = self.wingTypes[index] node = self.butterfly.getGeomNode() for type in self.wingTypes: wing = node.find('**/' + type) if (type != chosenType): wing.removeNode() else: # Choose an appropriate blend color if (index == 0 or index == 1): color = self.yellowColors[self.doId % len(self.yellowColors)] elif (index == 2 or index == 3): color = self.whiteColors[self.doId % len(self.whiteColors)] elif (index == 4): color = self.paleYellowColors[self.doId % len(self.paleYellowColors)] else: color = Vec4(1, 1, 1, 1) wing.setColor(color) # Make another copy of the butterfly model so we can LOD the # blending. Butterflies that are far away won't bother to # blend animations; nearby butterflies will use dynamic # blending to combine two or more animations at once on # playback for a nice fluttering and landing effect. self.butterfly2 = Actor.Actor(other=self.butterfly) # Allow the nearby butterfly to blend between its three # animations. All animations will be playing all the time; # we'll control which one is visible by varying the control # effect. #self.butterfly.enableBlend(blendType = PartBundle.BTLinear) #self.butterfly.loop('flutter', layer = 0) #self.butterfly.loop('land', layer = 1) #self.butterfly.loop('glide', layer = 2) # Set up a pose parameter to blend between the butterfly animations. #self.butterflyPoseParam = self.butterfly.addPoseParameter("butterfly", 0, 100) # Now create the sequence that will blend between the animations using # the pose parameter value. #seq = AnimSequence("butterfly") #seq.setNumFrames(50) ##seq.setFrameRate(24) #seq.addLayer(self.butterfly.getAnim("land"), 0, 0, 1, 1, False, False, False, self.butterflyPoseParam) #seq.addLayer(self.butterfly.getAnim("glide"), 1, 1, 50, 50, False, False, False, self.butterflyPoseParam) #seq.addLayer(self.butterfly.getAnim("land"), 50, 50, 100, 100, False, False, False, self.butterflyPoseParam) # Make a random play rate so all the butterflies will be # flapping at slightly different rates. This doesn't affect # the rate at which the butterfly moves, just the rate at # which the animation plays on the butterfly. rng = RandomNumGen.RandomNumGen(self.doId) playRate = 0.6 + 0.8 * rng.random() self.butterfly.setPlayRate(playRate, 'flutter') self.butterfly.setPlayRate(playRate, 'land') self.butterfly.setPlayRate(playRate, 'glide') self.butterfly2.setPlayRate(playRate, 'flutter') self.butterfly2.setPlayRate(playRate, 'land') self.butterfly2.setPlayRate(playRate, 'glide') # Also, a random glide contribution ratio. We'll blend a bit # of the glide animation in with the flutter animation to # dampen the effect of flutter. The larger the number here, # the greater the dampening effect. Some butterflies will be # more active than others. (Except when seen from a long way # off, because of the LODNode, below.) self.glideWeight = rng.random() * 2 lodNode = LODNode('butterfly-node') lodNode.addSwitch(100, 40) # self.butterfly2 lodNode.addSwitch(40, 0) # self.butterfly self.butterflyNode = NodePath(lodNode) self.butterfly2.setH(180.0) self.butterfly2.reparentTo(self.butterflyNode) self.butterfly.setH(180.0) self.butterfly.reparentTo(self.butterflyNode) self.__initCollisions() # Set up the drop shadow if ShadowCaster.globalDropShadowFlag: self.dropShadow = loader.loadModel( 'phase_3/models/props/drop_shadow') else: self.dropShadow = NodePath("dummy_drop_shadow") self.dropShadow.setColor(0, 0, 0, 0.3) self.dropShadow.setPos(0, 0.1, -0.05) self.dropShadow.setScale(self.shadowScaleBig) self.dropShadow.reparentTo(self.butterfly) def disable(self): """disable(self) This method is called when the DistributedObject is removed from active duty and stored in a cache. """ self.butterflyNode.reparentTo(hidden) if (self.ival != None): self.ival.finish() self.__ignoreAvatars() DistributedObject.DistributedObject.disable(self) def delete(self): """delete(self) This method is called when the DistributedObject is permanently removed from the world and deleted from the cache. """ self.butterfly.cleanup() self.butterfly = None self.butterfly2.cleanup() self.butterfly2 = None self.butterflyNode.removeNode() self.__deleteCollisions() self.ival = None del self.fsm DistributedObject.DistributedObject.delete(self) def uniqueButterflyName(self, name): DistributedButterfly.id += 1 return (name + '-%d' % DistributedButterfly.id) def __detectAvatars(self): self.accept('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter) def __ignoreAvatars(self): self.ignore('enter' + self.cSphereNode.getName()) def __initCollisions(self): self.cSphere = CollisionSphere(0., 1., 0., 3.) self.cSphere.setTangible(0) self.cSphereNode = CollisionNode( self.uniqueButterflyName('cSphereNode')) self.cSphereNode.addSolid(self.cSphere) self.cSphereNodePath = self.butterflyNode.attachNewNode( self.cSphereNode) self.cSphereNodePath.hide() self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) def __deleteCollisions(self): del self.cSphere del self.cSphereNode self.cSphereNodePath.removeNode() del self.cSphereNodePath def __handleCollisionSphereEnter(self, collEntry): """ Response for a toon walking up to this NPC """ assert (self.notify.debug("Entering collision sphere...")) # Tell the server self.sendUpdate('avatarEnter', []) def setArea(self, playground, area): self.playground = playground self.area = area def setState(self, stateIndex, curIndex, destIndex, time, timestamp): self.curIndex = curIndex self.destIndex = destIndex self.time = time self.fsm.request(ButterflyGlobals.states[stateIndex], [globalClockDelta.localElapsedTime(timestamp)]) ##### Off state ##### def enterOff(self, ts=0.0): if (self.butterflyNode != None): self.butterflyNode.reparentTo(hidden) return None def exitOff(self): if (self.butterflyNode != None): self.butterflyNode.reparentTo(render) return None ##### Flying state ##### def enterFlying(self, ts): self.__detectAvatars() curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][ self.curIndex] destPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][ self.destIndex] # We'll hit the ground if we go straight from curPos to destPos flyHeight = max( curPos[2], destPos[2]) + ButterflyGlobals.BUTTERFLY_HEIGHT[self.playground] curPosHigh = Point3(curPos[0], curPos[1], flyHeight) destPosHigh = Point3(destPos[0], destPos[1], flyHeight) if (ts <= self.time): flyTime = self.time - ( ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] + ButterflyGlobals.BUTTERFLY_LANDING[self.playground]) self.butterflyNode.setPos(curPos) self.dropShadow.show() self.dropShadow.setScale(self.shadowScaleBig) oldHpr = self.butterflyNode.getHpr() self.butterflyNode.headsUp(destPos) newHpr = self.butterflyNode.getHpr() self.butterflyNode.setHpr(oldHpr) takeoffShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_TAKEOFF[ self.playground] landShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_LANDING[ self.playground] self.butterfly2.loop('flutter') self.butterfly.loop('flutter') self.ival = Sequence( Parallel( LerpPosHprInterval( self.butterflyNode, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], curPosHigh, newHpr), #LerpAnimInterval(self.butterfly, # ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], # 'land', 'flutter'), #LerpAnimInterval(self.butterfly, # ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], # None, 'glide', # startWeight = 0, endWeight = self.glideWeight), Sequence( LerpScaleInterval(self.dropShadow, takeoffShadowT, self.shadowScaleSmall, startScale=self.shadowScaleBig), HideInterval(self.dropShadow)), ), LerpPosInterval(self.butterflyNode, flyTime, destPosHigh), Parallel( LerpPosInterval( self.butterflyNode, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], destPos), #LerpAnimInterval(self.butterfly, # ButterflyGlobals.BUTTERFLY_LANDING[self.playground], # 'flutter', 'land'), #LerpAnimInterval(self.butterfly, # ButterflyGlobals.BUTTERFLY_LANDING[self.playground], # None, 'glide', # startWeight = self.glideWeight, endWeight = 0), Sequence( Wait(ButterflyGlobals.BUTTERFLY_LANDING[ self.playground] - landShadowT), ShowInterval(self.dropShadow), LerpScaleInterval(self.dropShadow, landShadowT, self.shadowScaleBig, startScale=self.shadowScaleSmall)), ), name=self.uniqueName("Butterfly")) self.ival.start(ts) else: self.ival = None self.butterflyNode.setPos(destPos) self.butterfly.loop('land') #self.butterfly.setControlEffect('land', 1.0) #self.butterfly.setControlEffect('flutter', 0.0) #self.butterfly.setControlEffect('glide', 0.0) self.butterfly2.loop('land') return None def exitFlying(self): self.__ignoreAvatars() if (self.ival != None): self.ival.finish() self.ival = None return None ##### Landed state ##### def enterLanded(self, ts): self.__detectAvatars() curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][ self.curIndex] self.butterflyNode.setPos(curPos) self.dropShadow.show() self.dropShadow.setScale(self.shadowScaleBig) self.butterfly.loop('land') #self.butterfly.setControlEffect('land', 1.0) #self.butterfly.setControlEffect('flutter', 0.0) #self.butterfly.setControlEffect('glide', 0.0) self.butterfly2.pose( 'land', random.randrange(self.butterfly2.getNumFrames('land'))) return None def exitLanded(self): self.__ignoreAvatars() return None
class DistributedGardenPlot(DistributedLawnDecor.DistributedLawnDecor): notify = DirectNotifyGlobal.directNotify.newCategory( 'DistributedGardenPlot') def __init__(self, cr): DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) #self.defaultModel = "phase_8/models/props/flower_treasure.bam" self.plantPath = NodePath('plantPath') self.plantPath.reparentTo(self) self.plotScale = 1.0 self.plantingGuiDoneEvent = "plantingGuiDone" self.toonStatueSelectionDoneEvent = "toonStatueSelectionDone" self.defaultModel = "phase_5.5/models/estate/dirt_mound" self.colorScaler = Vec4(1, 1, 1, 1) self.plantingGui = None def delete(self): if self.plantingGui: self.plantingGui.destroy() self.plantingGui = None DistributedLawnDecor.DistributedLawnDecor.delete(self) def announceGenerate(self): #need to wait to do this until ownerindex and plot come in from the ai self.plotType = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) #differentiate the plot types here self.stickUp = 0.0 if self.getOwnerId() != localAvatar.doId: self.defaultModel = None elif self.plotType == GardenGlobals.FLOWER_TYPE: self.collSphereRadius = 2.0 self.collSphereOffset = 0.0 self.plotScale = .70 self.stickUp = 1.1 # self.defaultModel = "phase_5.5/models/estate/planterA" elif self.plotType == GardenGlobals.GAG_TREE_TYPE: self.collSphereRadius = 3.0 self.plotScale = 1.5 self.colorScaler = Vec4(1.0, 1.0, 1.0, 1) #self.defaultModel = "phase_5.5/models/estate/planterB" elif self.plotType == GardenGlobals.STATUARY_TYPE: self.collSphereRadius = 3.0 self.plotScale = 0.075 self.stickUp = -0.0 #self.defaultModel = "phase_5.5/models/estate/dirt_mound" self.defaultModel = "phase_5.5/models/estate/garden_slab" else: self.collSphereOffset = 0.0 self.notify.debug('announceGenerate') DistributedLawnDecor.DistributedLawnDecor.announceGenerate(self) def loadModel(self): self.rotateNode = self.plantPath.attachNewNode('rotate') self.model = None if self.defaultModel: self.model = loader.loadModel(self.defaultModel) if type(self.plotScale) == tuple: self.model.setScale(*self.plotScale) else: self.model.setScale(self.plotScale) self.model.reparentTo(self.rotateNode) self.model.setColorScale(self.colorScaler) self.stick2Ground() def setupShadow(self): pass def getShovelCommand(self): return self.plantSomething def getShovelAction(self): return self.getPlantingText() def handleEnterPlot(self, entry=None): #print("plot entered %s" % (self.doId)) #print entry dist = self.getDistance(localAvatar) if self.canBePlanted(): base.localAvatar.addShovelRelatedDoId( self.doId) #, self.plantSomething) def handleExitPlot(self, entry=None): DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) #print("plot exited %s" % (self.doId)) #print entry base.localAvatar.removeShovelRelatedDoId(self.doId) #base.localAvatar.clearPlantToWater(self.doId) #base.localAvatar.hideWateringCanButton() def getPlantingText(self): plantText = "hardcoding" if self.canBePlanted(): whatCanBePlanted = GardenGlobals.whatCanBePlanted( self.ownerIndex, self.plot) plantText = TTLocalizer.GardeningPlant if whatCanBePlanted == GardenGlobals.INVALID_TYPE: self.notify.warning( 'whatCanBePlanted returned INVALID_TYPE for %d %d' % (self.ownerIndex, self.plot)) elif whatCanBePlanted == GardenGlobals.FLOWER_TYPE: plantText = TTLocalizer.GardeningPlantFlower elif whatCanBePlanted == GardenGlobals.GAG_TREE_TYPE: plantText = TTLocalizer.GardeningPlantTree elif whatCanBePlanted == GardenGlobals.STATUARY_TYPE: plantText = TTLocalizer.GardeningPlantItem return plantText def canBePlanted(self): """ Other subclasses may extend this function, but at the very least, you can't plant on something you don't own """ retval = True if not base.localAvatar.doId == self.getOwnerId(): retval = False return retval def plantSomething(self): whatCanBePlanted = GardenGlobals.whatCanBePlanted( self.ownerIndex, self.plot) if whatCanBePlanted == GardenGlobals.INVALID_TYPE: self.notify.warning( 'whatCanBePlanted returned INVALID_TYPE for %d %d' % (self.ownerIndex, self.plot)) elif whatCanBePlanted == GardenGlobals.FLOWER_TYPE: #self.sendUpdate('plantFlower',[48,1]) self.popupFlowerPlantingGui() #self.cr.playGame.getPlace().detectedGardenPlotUse() self.startInteraction() elif whatCanBePlanted == GardenGlobals.GAG_TREE_TYPE: self.popupTreePlantingGui() #self.cr.playGame.getPlace().detectedGardenPlotUse() self.startInteraction() elif whatCanBePlanted == GardenGlobals.STATUARY_TYPE: self.popupItemPlantingGui() #self.cr.playGame.getPlace().detectedGardenPlotUse() self.startInteraction() pass pass def __handleFlowerPlantingDone(self, willPlant=0, recipeStr="", special=-1): assert self.notify.debugStateCall(self) self.ignore(self.plantingGuiDoneEvent) self.ignore('stoppedAsleep') # Ask the AI to complete the sale #self.sendUpdate("completeFlowerSale", [sell]) self.plantingGui.destroy() self.plantingGui = None base.localAvatar.showGardeningGui() base.localAvatar.removeShovelRelatedDoId(self.doId) successPlanting = False if willPlant: recipeKey = GardenGlobals.getRecipeKey(recipeStr, special) if recipeKey >= 0: species, variety = GardenGlobals.getSpeciesVarietyGivenRecipe( recipeKey) if species >= 0 and variety >= 0: self.sendUpdate('plantFlower', [species, variety]) successPlanting = True else: self.notify.debug("%s %d is not a valid recipe" % (recipeStr, special)) burntBeans = len(recipeStr) self.sendUpdate('plantNothing', [burntBeans]) if successPlanting: flowerName = GardenGlobals.getFlowerVarietyName(species, variety) stringToShow = TTLocalizer.getResultPlantedSomethingSentence( flowerName) # the dialog will now come up after the planting movie # self.resultDialog = TTDialog.TTDialog( # style = TTDialog.Acknowledge, # text = stringToShow, # command = self.resultsCallback # ) elif willPlant: self.resultDialog = TTDialog.TTDialog( style=TTDialog.Acknowledge, text=TTLocalizer.ResultPlantedNothing, command=self.popupFlowerPlantingGuiAgain) else: #base.cr.playGame.getPlace().detectedGardenPlotDone() self.finishInteraction() def popupFlowerPlantingGui(self): assert self.notify.debugStateCall(self) base.localAvatar.hideGardeningGui() self.acceptOnce(self.plantingGuiDoneEvent, self.__handleFlowerPlantingDone) self.plantingGui = PlantingGUI.PlantingGUI(self.plantingGuiDoneEvent) self.accept('stoppedAsleep', self.__handleFlowerPlantingDone) def resultsCallback(self, value): self.notify.debug('value=%d' % value) self.resultDialog.destroy() self.resultDialog = None #base.cr.playGame.getPlace().detectedGardenPlotDone() self.finishInteraction() def popupFlowerPlantingGuiAgain(self, value): self.notify.debug('value=%d' % value) self.resultDialog.destroy() self.resultDialog = None self.popupFlowerPlantingGui() def popupItemPlantingGuiAgain(self, value): self.notify.debug('value=%d' % value) self.resultDialog.destroy() self.resultDialog = None self.popupItemPlantingGui() def __handleItemPlantingDone(self, willPlant=0, recipeStr="", selectedSpecial=-1): assert self.notify.debugStateCall(self) self.ignore(self.plantingGuiDoneEvent) self.ignore('stoppedAsleep') # Ask the AI to complete the sale #self.sendUpdate("completeFlowerSale", [sell]) self.plantingGui.destroy() self.plantingGui = None base.localAvatar.showGardeningGui() base.localAvatar.removeShovelRelatedDoId(self.doId) gardenSpecials = base.localAvatar.getGardenSpecials() special = -1 if selectedSpecial >= 0: special = gardenSpecials[selectedSpecial][0] successPlanting = False successToonStatue = False if willPlant: recipeKey = GardenGlobals.getRecipeKey(recipeStr, special) if recipeKey >= 0: species, variety = GardenGlobals.getSpeciesVarietyGivenRecipe( recipeKey) if species >= 0 and variety >= 0: #make sure it is a statuary and not a flower if GardenGlobals.PlantAttributes[species][ 'plantType'] == GardenGlobals.STATUARY_TYPE: successPlanting = True # Handle ToonStatues separately because another screen has to be added if species >= 205 and species <= 208: successToonStatue = True else: self.sendUpdate('plantStatuary', [species]) else: self.notify.debug("%s %d is not a valid recipe" % (recipeStr, special)) burntBeans = len(recipeStr) self.sendUpdate('plantNothing', [burntBeans]) if successPlanting: itemName = GardenGlobals.PlantAttributes[species]['name'] stringToShow = TTLocalizer.getResultPlantedSomethingSentence( itemName) # self.resultDialog = TTDialog.TTDialog( # style = TTDialog.Acknowledge, # text = stringToShow, # command = self.resultsCallback # ) elif willPlant: self.resultDialog = TTDialog.TTDialog( style=TTDialog.Acknowledge, text=TTLocalizer.ResultPlantedNothing, command=self.popupItemPlantingGuiAgain) else: #base.cr.playGame.getPlace().detectedGardenPlotDone() self.finishInteraction() if successToonStatue: self.popupToonStatueSelectionGui(species) def popupItemPlantingGui(self): assert self.notify.debugStateCall(self) base.localAvatar.hideGardeningGui() self.acceptOnce(self.plantingGuiDoneEvent, self.__handleItemPlantingDone) self.plantingGui = PlantingGUI.PlantingGUI(self.plantingGuiDoneEvent, True) self.plantingGui.showFirstSpecial() self.accept('stoppedAsleep', self.__handleItemPlantingDone) def popupToonStatueSelectionGui(self, species): assert self.notify.debugStateCall(self) base.localAvatar.hideGardeningGui() self.acceptOnce(self.toonStatueSelectionDoneEvent, self.__handleToonStatueSelectionDone, extraArgs=[species]) self.toonStatueSelectionGui = ToonStatueSelectionGUI.ToonStatueSelectionGUI( self.toonStatueSelectionDoneEvent, True) self.accept('stoppedAsleep', self.__handleToonStatueSelectionDone) def popupToonStatueSelectionGuiAgain(self, species): assert self.notify.debugStateCall(self) self.resultDialog.destroy() self.resultDialog = None self.popupToonStatueSelectionGui(species) def __handleToonStatueSelectionDone(self, species, willPlant=0, recipeStr="", dnaCode=-1): assert self.notify.debugStateCall(self) self.ignore(self.toonStatueSelectionDoneEvent) self.ignore('stoppedAsleep') self.toonStatueSelectionGui.destroy() self.toonStatueSelectionGui = None base.localAvatar.showGardeningGui() base.localAvatar.removeShovelRelatedDoId(self.doId) if willPlant: self.sendUpdate('plantToonStatuary', [species, dnaCode]) else: self.popupItemPlantingGui() ## self.finishInteraction() def popupTreePlantingGui(self): assert self.notify.debugStateCall(self) base.localAvatar.hideGardeningGui() self.acceptOnce(self.plantingGuiDoneEvent, self.__handleTreePlantingDone) self.plantingGui = PlantTreeGUI.PlantTreeGUI(self.plantingGuiDoneEvent) self.accept('stoppedAsleep', self.__handleTreePlantingDone) def __handleTreePlantingDone(self, willPlant=False, gagTrack=None, gagLevel=None): assert self.notify.debugStateCall(self) self.ignore(self.plantingGuiDoneEvent) self.ignore('stoppedAsleep') self.plantingGui.destroy() self.plantingGui = None base.localAvatar.showGardeningGui() base.localAvatar.removeShovelRelatedDoId(self.doId) if willPlant: self.sendUpdate('plantGagTree', [gagTrack, gagLevel]) #species = GardenGlobals.getTreeTypeIndex(gagTrack, gagLevel) #stringToShow = TTLocalizer.ResultPlantedSomething % GardenGlobals.PlantAttributes[species]['name'] #self.resultDialog = TTDialog.TTDialog( # style = TTDialog.Acknowledge, # text = stringToShow, # command = self.resultsCallback # ) else: #base.cr.playGame.getPlace().detectedGardenPlotDone() self.finishInteraction() def setMovie(self, mode, avId): if mode == GardenGlobals.MOVIE_PLANT: self.doPlaceItemTrack(avId) elif mode == GardenGlobals.MOVIE_FINISHREMOVING: self.doFinishRemovingTrack(avId) elif mode == GardenGlobals.MOVIE_PLANT_REJECTED: self.doPlantRejectedTrack(avId) def doPlantRejectedTrack(self, avId): toon = base.cr.doId2do.get(avId) if not toon: return self.finishMovies() self.movie = Sequence() self.movie.append(Func(toon.detachShovel)) self.movie.append(Func(toon.loop, 'neutral')) if avId == localAvatar.doId: self.movie.append(Func(self.finishInteraction)) self.movie.append(Func(self.movieDone)) self.movie.start() def doFinishRemovingTrack(self, avId): toon = base.cr.doId2do.get(avId) if not toon: return self.finishMovies() self.movie = Sequence() self.movie.append(Func(toon.detachShovel)) if self.model: pos = self.model.getPos() pos.setZ(pos[2] - 1) animProp = LerpPosInterval(self.model, 3, self.model.getPos(), pos) shrinkProp = LerpScaleInterval(self.model, 3, scale=self.plotScale, startScale=0.01) objAnimShrink = ParallelEndTogether(animProp, shrinkProp) self.movie.append(objAnimShrink) self.movie.append(self.stopCamIval(avId)) self.movie.append(Func(toon.loop, 'neutral')) if avId == localAvatar.doId: self.movie.append(Func(self.finishInteraction)) self.movie.append(Func(self.movieDone)) self.movie.start() def doPlaceItemTrack(self, avId, item=None): toon = base.cr.doId2do.get(avId) if not toon: return self.finishMovies() # load the shovel if avId == localAvatar.doId: self.startInteraction() shovel = toon.attachShovel() shovel.hide() moveTrack = self.generateToonMoveTrack(toon) placeItemTrack = self.generatePlaceItemTrack(toon, item) self.movie = Sequence( self.startCamIval(avId), moveTrack, Func(shovel.show), placeItemTrack, #self.stopCamIval(avId), ) if avId == localAvatar.doId: self.expectingReplacement = 1 self.movie.append(Func(self.movieDone)) self.movie.start() def generatePlaceItemTrack(self, toon, item): """ """ sound = loader.loadSfx('phase_5.5/audio/sfx/burrow.mp3') sound.setPlayRate(0.5) placeItemTrack = Parallel() placeItemTrack.append( Sequence( ActorInterval(toon, "start-dig"), Parallel( ActorInterval(toon, "loop-dig", loop=1, duration=5.13), Sequence( Wait(0.25), SoundInterval(sound, node=toon, duration=0.55), Wait(0.80), SoundInterval(sound, node=toon, duration=0.55), Wait(1.35), SoundInterval(sound, node=toon, duration=0.55), ), ), ActorInterval(toon, "start-dig", playRate=-1), Func(toon.loop, 'neutral'), Func(toon.detachShovel), )) if self.model: pos = self.model.getPos() pos.setZ(pos[2] - 1) #placeItemTrack.append(LerpPosInterval(self.model, 3, pos)) animProp = LerpPosInterval(self.model, 3, pos) shrinkProp = LerpScaleInterval(self.model, 3, scale=0.01, startScale=self.model.getScale()) objAnimShrink = ParallelEndTogether(animProp, shrinkProp) placeItemTrack.append(objAnimShrink) if item: placeItemTrack.append( Sequence( Func(item.reparentTo, toon.rightHand), Wait(0.55), Func(item.wrtReparentTo, render), Parallel( LerpHprInterval(item, hpr=self.getHpr(render), duration=1.2), ProjectileInterval(item, endPos=self.getPos(render), duration=1.2, gravityMult=0.45), ), Func(item.removeNode), )) return placeItemTrack def makeMovieNode(self): # create a movieNode... this is where the toon will stand while playing a movie if self.plotType == GardenGlobals.FLOWER_TYPE: self.movieNode = self.rotateNode.attachNewNode('moviePos') self.movieNode.setPos(0, 3, 0) self.movieNode.setH(180) self.stick2Ground() else: DistributedLawnDecor.DistributedLawnDecor.makeMovieNode(self)
class CogdoFlyingGuiManager: def __init__(self, player): self.player = player self.root = NodePath("CogdoFlyingGui") self.root.reparentTo(aspect2d) self.fuelMeter = NodePath("scrubMeter") self.fuelMeter.reparentTo(self.root) self.fuelMeter.setPos(1.1, 0.0, -0.7) self.fuelMeter.setSz(2.0) cm = CardMaker('card') cm.setFrame(-0.07, 0.07, 0.0, 0.75) self.fuelMeterBar = self.fuelMeter.attachNewNode(cm.generate()) self.fuelMeterBar.setColor(0.95, 0.95, 0.0, 1.0) self.fuelLabel = DirectLabel( parent=self.root, relief=None, pos=(1.1, 0, -0.8), scale=0.075, text="Fuel", text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), ) self.messageLabel = DirectLabel( parent=self.root, relief=None, pos=(0.0, 0.0, -0.9), scale=0.1, text=" ", text_align=TextNode.ACenter, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=1, ) self.messageLabel.stash() self.winLabel = DirectLabel( parent=self.root, relief=None, pos=(0.0, 0.0, 0.0), scale=0.25, text="You win!", text_align=TextNode.ACenter, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), ) self.winLabel.stash() self.refuelLerp = LerpFunctionInterval(self.fuelMeterBar.setSz, fromData=0.0, toData=1.0, duration=2.0) def setRefuelLerpFromData(self): startScale = self.fuelMeterBar.getSz() self.refuelLerp.fromData = startScale def setMessageLabelText(self, text): self.messageLabel["text"] = text self.messageLabel.setText() def update(self): self.fuelMeterBar.setSz(self.player.fuel) def destroy(self): # print "Destroying GUI" self.fuelMeterBar.detachNode() self.fuelMeterBar = None self.fuelLabel.detachNode() self.fuelLabel = None self.fuelMeter.detachNode() self.fuelMeter = None self.winLabel.detachNode() self.winLabel = None self.root.detachNode() self.root = None self.player = None