Пример #1
0
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
Пример #2
0
    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
Пример #3
0
    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 = {}
Пример #4
0
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)
Пример #5
0
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()
Пример #6
0
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)
Пример #7
0
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)
Пример #9
0
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
Пример #10
0
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