def loadAttack(self):
     self.phone = loader.loadModel('phase_3.5/models/props/phone.bam')
     self.phone.setHpr(0, 0, 180)
     if self.suit.type == 'B':
         self.phone.setPos(0.7, 0.15, 0)
     else:
         if self.suit.type == 'C':
             self.phone.setPos(0.25, 0, 0)
     self.receiver = loader.loadModel('phase_3.5/models/props/receiver.bam')
     self.receiver.reparentTo(self.phone)
     self.cord = Rope()
     self.cord.ropeNode.setUseVertexColor(1)
     self.cord.ropeNode.setUseVertexThickness(1)
     self.cord.setup(3, ({'node': self.phone, 'point': (0.8, 0, 0.2), 'color': (0, 0, 0, 1), 'thickness': 1000}, {'node': self.phone, 'point': (2, 0, 0), 'color': (0, 0, 0, 1), 'thickness': 1000}, {'node': self.receiver, 'point': (1.1, 0.25, 0.5), 'color': (0, 0, 0, 1), 'thickness': 1000}), [])
     self.cord.setH(180)
     self.phoneSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup.ogg')
     base.audio3d.attachSoundToObject(self.phoneSfx, self.phone)
     self.hangupSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup_place_down.ogg')
     base.audio3d.attachSoundToObject(self.hangupSfx, self.phone)
     collSphere = CollisionSphere(0, 0, 0, 2)
     collSphere.setTangible(0)
     collNode = CollisionNode('phone_shootout')
     collNode.addSolid(collSphere)
     collNode.setCollideMask(CIGlobals.WallBitmask)
     self.collNP = self.phone.attachNewNode(collNode)
Example #2
0
 def loadAttack(self):
     self.phone = loader.loadModel('phase_3.5/models/props/phone.bam')
     self.phone.setHpr(0, 0, 180)
     if self.suit.type == 'B':
         self.phone.setPos(0.7, 0.15, 0)
     elif self.suit.type == 'C':
         self.phone.setPos(0.25, 0, 0)
     self.receiver = loader.loadModel('phase_3.5/models/props/receiver.bam')
     self.receiver.reparentTo(self.phone)
     self.cord = Rope()
     self.cord.ropeNode.setUseVertexColor(1)
     self.cord.ropeNode.setUseVertexThickness(1)
     self.cord.setup(3, ({'node': self.phone,
       'point': (0.8, 0, 0.2),
       'color': (0, 0, 0, 1),
       'thickness': 1000}, {'node': self.phone,
       'point': (2, 0, 0),
       'color': (0, 0, 0, 1),
       'thickness': 1000}, {'node': self.receiver,
       'point': (1.1, 0.25, 0.5),
       'color': (0, 0, 0, 1),
       'thickness': 1000}), [])
     self.cord.setH(180)
     self.phoneSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup.mp3')
     base.audio3d.attachSoundToObject(self.phoneSfx, self.phone)
     self.hangupSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup_place_down.mp3')
     base.audio3d.attachSoundToObject(self.hangupSfx, self.phone)
     collSphere = CollisionSphere(0, 0, 0, 2)
     collSphere.setTangible(0)
     collNode = CollisionNode('phone_shootout')
     collNode.addSolid(collSphere)
     collNode.setCollideMask(CIGlobals.WallBitmask)
     self.collNP = self.phone.attachNewNode(collNode)
 def announceGenerate(self):
     DistributedNode.announceGenerate(self)
     self.kart = loader.loadModel('phase_6/models/karting/Kart3_Final.bam')
     self.kart.find('**/decals').removeNode()
     self.kart.reparentTo(self)
     self.pod = loader.loadModel('phase_4/models/minigames/pods_truck.egg')
     self.pod.reparentTo(self)
     self.pod.setScale(0.2)
     self.pod.setY(8.5)
     self.pod.setH(180)
     self.rope = Rope()
     self.rope.ropeNode.setUseVertexColor(1)
     self.rope.setup(3, ({'node': self.kart,
       'point': (0, 1.5, 0.7),
       'color': (0, 0, 0, 1),
       'thickness': 1000}, {'node': self.kart,
       'point': (0, 1.5, 0.7),
       'color': (0, 0, 0, 1),
       'thickness': 1000}, {'node': self.pod,
       'point': (0, 31, 5),
       'color': (0, 0, 0, 1),
       'thickness': 1000}), [])
     self.rope.setH(180)
     self.rope.reparentTo(self)
     sphere = CollisionSphere(0, 0, 0, 2)
     sphere.setTangible(0)
     node = CollisionNode(self.uniqueName('truck_trigger'))
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     self.triggerNP = self.attachNewNode(node)
     self.triggerNP.setPos(0, 8.0, 2.0)
     self.setScale(2.0)
     self.accept('enter' + self.triggerNP.node().getName(), self.__handleTruckTrigger)
Example #4
0
 def loadToonup(self, toonupIndex):
     self.toonupNum += 1
     if toonupIndex in self.toonupModelDict:
         toonupModel = self.toonupModelDict[toonupIndex]
         toonupModel.reparentTo(self.foodNodes[toonupIndex])
         colNp = toonupModel.find('**/ToonupCol*')
         colNp.setTag('toonupNum', str(self.toonupNum))
     else:
         toonupModelScale = self.ToonupScales[toonupIndex]
         modelName = self.ToonupModels[toonupIndex]
         toonupModel = loader.loadModel(modelName)
         self.foodNodes[toonupIndex].setZ(self.beltHeight - 0.1)
         toonupModel.setZ(self.ToonupZOffsets[toonupIndex])
         toonupModel.setScale(toonupModelScale)
         toonupModel.reparentTo(self.foodNodes[toonupIndex])
         target = CollisionTube(4, 0, 0, -4, 0, 0, 2)
         target.setTangible(0)
         colName = 'ToonupCol-%d-%d' % (self.index, toonupIndex)
         targetNode = CollisionNode(colName)
         targetNode.addSolid(target)
         targetNode.setCollideMask(ToontownGlobals.WallBitmask)
         targetNodePath = toonupModel.attachNewNode(targetNode)
         targetNodePath.setScale(1.0 / toonupModelScale)
         targetNodePath.setTag('toonupIndex', str(toonupIndex))
         targetNodePath.setTag('beltIndex', str(self.index))
         targetNodePath.setTag('toonupNum', str(self.toonupNum))
         targetNodePath.setZ(targetNodePath.getZ() - 1.5 / toonupModelScale)
         self.accept('enter' + colName, self.touchedToonup)
         self.toonupModelDict[toonupIndex] = toonupModel
    def doAttack(self, particlePaths, track_name, particleCollId, animation_name, delayUntilRelease, animationSpeed=1, handObjPath=None, handObjParent=None, startRightAway=True, ts=0):
        for path in particlePaths:
            particle = ParticleLoader.loadParticleEffect(path)
            self.particles.append(particle)

        sphere = CollisionSphere(0, 0, 0, 1)
        sphere.setTangible(0)
        node = CollisionNode(particleCollId)
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        if len(self.particles) == 1:
            self.shootOutCollNP = self.particles[0].attachNewNode(node)
        else:
            self.shootOutCollNP = self.suit.attachNewNode(node)
        if handObjPath and handObjParent:
            self.handObj = loader.loadModel(handObjPath)
            self.handObj.reparentTo(handObjParent)
        self.suit.setPlayRate(animationSpeed, animation_name)
        self.suit.play(animation_name)
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            particleCollId = self.suit.uniqueName(particleCollId)
        self.suitTrack = Sequence(name=track_name)
        self.suitTrack.append(Wait(delayUntilRelease))
        self.suitTrack.append(Func(self.releaseAttack))
        self.suitTrack.append(Wait(self.particleIvalDur))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        if startRightAway:
            self.suitTrack.start(ts)
Example #6
0
 def loadFood(self, foodIndex):
     self.foodNum += 1
     if foodIndex in self.foodModelDict:
         foodModel = self.foodModelDict[foodIndex]
         foodModel.reparentTo(self.foodNodes[foodIndex])
         colNp = foodModel.find('**/FoodCol*')
         colNp.setTag('foodNum', str(self.foodNum))
     else:
         foodModelScale = ToontownGlobals.BossbotFoodModelScale
         foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood')
         foodModel.setScale(foodModelScale)
         foodModel.reparentTo(self.foodNodes[foodIndex])
         target = CollisionTube(4, 0, 0, -4, 0, 0, 2)
         target.setTangible(0)
         colName = 'FoodCol-%d-%d' % (self.index, foodIndex)
         targetNode = CollisionNode(colName)
         targetNode.addSolid(target)
         targetNode.setCollideMask(ToontownGlobals.WallBitmask)
         targetNodePath = foodModel.attachNewNode(targetNode)
         targetNodePath.setScale(1.0 / foodModelScale)
         targetNodePath.setTag('foodIndex', str(foodIndex))
         targetNodePath.setTag('beltIndex', str(self.index))
         targetNodePath.setTag('foodNum', str(self.foodNum))
         targetNodePath.setZ(targetNodePath.getZ() - 1.5)
         self.accept('enter' + colName, self.touchedFood)
         self.foodModelDict[foodIndex] = foodModel
Example #7
0
 def loadFood(self, foodIndex):
     self.foodNum += 1
     if foodIndex in self.foodModelDict:
         foodModel = self.foodModelDict[foodIndex]
         foodModel.reparentTo(self.foodNodes[foodIndex])
         colNp = foodModel.find('**/FoodCol*')
         colNp.setTag('foodNum', str(self.foodNum))
     else:
         foodModelScale = ToontownGlobals.BossbotFoodModelScale
         foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood')
         foodModel.setScale(foodModelScale)
         foodModel.reparentTo(self.foodNodes[foodIndex])
         target = CollisionTube(4, 0, 0, -4, 0, 0, 2)
         target.setTangible(0)
         colName = 'FoodCol-%d-%d' % (self.index, foodIndex)
         targetNode = CollisionNode(colName)
         targetNode.addSolid(target)
         targetNode.setCollideMask(ToontownGlobals.WallBitmask)
         targetNodePath = foodModel.attachNewNode(targetNode)
         targetNodePath.setScale(1.0 / foodModelScale)
         targetNodePath.setTag('foodIndex', str(foodIndex))
         targetNodePath.setTag('beltIndex', str(self.index))
         targetNodePath.setTag('foodNum', str(self.foodNum))
         targetNodePath.setZ(targetNodePath.getZ() - 1.5)
         self.accept('enter' + colName, self.touchedFood)
         self.foodModelDict[foodIndex] = foodModel
 def setupCollisions(self):
     sphere = CollisionSphere(0, 0, 0, 2)
     sphere.setTangible(0)
     node = CollisionNode(self.collisionName)
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     self.collisionNode = self.attachNewNode(node)
    def start(self):
        base.camLens.setNear(0.1)

        self.shooterTrav = CollisionTraverser('ToonFPS.shooterTrav')
        ray = CollisionRay()
        rayNode = CollisionNode('ToonFPS.rayNode')
        rayNode.addSolid(ray)
        rayNode.setCollideMask(BitMask32(0))
        rayNode.setFromCollideMask(CIGlobals.WallBitmask | CIGlobals.FloorBitmask)
        self.shooterRay = ray
        self.shooterRayNode = base.camera.attachNewNode(rayNode)
        self.shooterHandler = CollisionHandlerQueue()
        self.shooterTrav.addCollider(self.shooterRayNode, self.shooterHandler)

        self.firstPerson.start()
        self.v_model_root.reparentTo(base.camera)
        self.v_model.reparentTo(self.v_model_root)
        if self.weaponName == "pistol":
            self.v_model_root.setZ(-1.8)
            self.v_model_root.setY(0.3)
            self.v_model_root.setX(-0.1)
            self.v_model_root.setH(2)
        elif self.weaponName == "sniper":
            self.v_model_root.setPos(-0.42, -0.81, -1.7)
            self.v_model_root.setHpr(359, 352.87, 0.00)
        elif self.weaponName == "shotgun":
            self.v_model_root.setPos(-0.42, -0.81, -1.7)
            self.v_model_root.setHpr(359, 352.87, 0.00)
        self.gui.start()
        self.firstPerson.disableMouse()
        self.aliveFSM.request('draw')
 def setupCollisions(self):
     sphere = CollisionSphere(0, 0, 0, 2)
     sphere.setTangible(0)
     node = CollisionNode(self.collisionName)
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     self.collisionNode = self.attachNewNode(node)
Example #11
0
    def doAttack(self, particlePaths, track_name, particleCollId, animation_name, delayUntilRelease, animationSpeed = 1, handObjPath = None, handObjParent = None, startRightAway = True, ts = 0):
        for path in particlePaths:
            particle = ParticleLoader.loadParticleEffect(path)
            self.particles.append(particle)

        sphere = CollisionSphere(0, 0, 0, 1)
        sphere.setTangible(0)
        node = CollisionNode(particleCollId)
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        if len(self.particles) == 1:
            self.shootOutCollNP = self.particles[0].attachNewNode(node)
        else:
            self.shootOutCollNP = self.suit.attachNewNode(node)
        if handObjPath and handObjParent:
            self.handObj = loader.loadModel(handObjPath)
            self.handObj.reparentTo(handObjParent)
        self.suit.setPlayRate(animationSpeed, animation_name)
        self.suit.play(animation_name)
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            particleCollId = self.suit.uniqueName(particleCollId)
        self.suitTrack = Sequence(name=track_name)
        self.suitTrack.append(Wait(delayUntilRelease))
        self.suitTrack.append(Func(self.releaseAttack))
        self.suitTrack.append(Wait(self.particleIvalDur))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        if startRightAway:
            self.suitTrack.start(ts)
Example #12
0
 def loadToonup(self, toonupIndex):
     self.toonupNum += 1
     if toonupIndex in self.toonupModelDict:
         toonupModel = self.toonupModelDict[toonupIndex]
         toonupModel.reparentTo(self.foodNodes[toonupIndex])
         colNp = toonupModel.find('**/ToonupCol*')
         colNp.setTag('toonupNum', str(self.toonupNum))
     else:
         toonupModelScale = self.ToonupScales[toonupIndex]
         modelName = self.ToonupModels[toonupIndex]
         toonupModel = loader.loadModel(modelName)
         self.foodNodes[toonupIndex].setZ(self.beltHeight - 0.1)
         toonupModel.setZ(self.ToonupZOffsets[toonupIndex])
         toonupModel.setScale(toonupModelScale)
         toonupModel.reparentTo(self.foodNodes[toonupIndex])
         target = CollisionTube(4, 0, 0, -4, 0, 0, 2)
         target.setTangible(0)
         colName = 'ToonupCol-%d-%d' % (self.index, toonupIndex)
         targetNode = CollisionNode(colName)
         targetNode.addSolid(target)
         targetNode.setCollideMask(ToontownGlobals.WallBitmask)
         targetNodePath = toonupModel.attachNewNode(targetNode)
         targetNodePath.setScale(1.0 / toonupModelScale)
         targetNodePath.setTag('toonupIndex', str(toonupIndex))
         targetNodePath.setTag('beltIndex', str(self.index))
         targetNodePath.setTag('toonupNum', str(self.toonupNum))
         targetNodePath.setZ(targetNodePath.getZ() - 1.5 / toonupModelScale)
         self.accept('enter' + colName, self.touchedToonup)
         self.toonupModelDict[toonupIndex] = toonupModel
Example #13
0
    def announceGenerate(self):
        self.reparentTo(render)
        base.minigame.flags.append(self)
        self.flagMdl = loader.loadModel(
            'phase_4/models/minigames/flag_new.egg')
        self.flagMdl.reparentTo(render)
        self.flagMdl.find('**/flag').setTwoSided(1)
        self.flagMdl.find('**/flag_pole').setColor(self.pole_color)
        self.flagMdl.find('**/flag').setColor(self.colors[self.team])
        hideNodes = ['icon2', 'icon3']
        if self.team == GGG.Teams.BLUE:
            hideNodes = ['icon', 'icon1']
        for node in hideNodes:
            part = self.flagMdl.find('**/%s' % node)
            if part:
                part.removeNode()

        sphere = CollisionSphere(0, 0, 0, 4)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('flagpoint_colnode'))
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.collNP = self.attachNewNode(node)
        sphere = CollisionSphere(0, 0, 0, 2)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('flag_colnode'))
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.flagCollNP = self.flagMdl.attachNewNode(node)
        pos, hpr = base.minigame.loader.getFlagPoint_Point(self.team)
        self.setPos(pos)
        self.setHpr(hpr)
Example #14
0
 def __setupCollisions(self):
     sphere = CollisionSphere(0, 0, 0, 4)
     sphere.setTangible(0)
     collisionNode = CollisionNode(self.uniqueName('NPCToonSphere'))
     collisionNode.addSolid(sphere)
     collisionNode.setCollideMask(CIGlobals.WallBitmask)
     self.collisionNodePath = self.attachNewNode(collisionNode)
     self.collisionNodePath.setY(1.5)
 def __setupCollisions(self):
     sphere = CollisionSphere(0, 0, 0, 4)
     sphere.setTangible(0)
     collisionNode = CollisionNode(self.uniqueName('NPCToonSphere'))
     collisionNode.addSolid(sphere)
     collisionNode.setCollideMask(CIGlobals.WallBitmask)
     self.collisionNodePath = self.attachNewNode(collisionNode)
     self.collisionNodePath.setY(1.5)
 def setupWallSphere(self):
     sphere = CollisionSphere(0.0, 0.0, 0.0, 3.0)
     node = CollisionNode('DistributedPieTurret.WallSphere')
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     self.wallCollNode = self.cannon.attachNewNode(node)
     self.wallCollNode.setZ(2)
     self.wallCollNode.setY(1.0)
Example #17
0
    def getSprayTrack(self, origin, target, scaleUp, hold, scaleDown, horizScale = 1.0, vertScale = 1.0):
        base.localAvatar.stop(self.toonAnim)
        self.lastFrame = self.avatar.getCurrentFrame(self.toonAnim)
        track = Sequence()
        sprayProp = loader.loadModel(GagGlobals.SPRAY_MDL)
        sprayProp.setTwoSided(1)
        sprayScale = hidden.attachNewNode('spray-parent')
        sprayRot = hidden.attachNewNode('spray-rotate')
        sprayRot.setColor(GagGlobals.WATER_SPRAY_COLOR)
        sprayRot.setTransparency(1)
        collNode = CollisionNode('Collision')
        spraySphere = CollisionSphere(0, 0, 0, 1)
        spraySphere.setTangible(0)
        collNode.addSolid(spraySphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        sprayNP = sprayRot.attachNewNode(collNode)
        sprayNP.setY(1)
        self.sprayNP = sprayNP
        event = CollisionHandlerEvent()
        event.set_in_pattern('%fn-into')
        event.set_out_pattern('%fn-out')
        base.cTrav.add_collider(sprayNP, event)
        self.avatar.acceptOnce(sprayNP.node().getName() + '-into', self.onCollision)

        def showSpray(sprayScale, sprayProp, origin, target):
            objects = [sprayRot, sprayScale, sprayProp]
            for item in objects:
                index = objects.index(item)
                if index == 0:
                    item.reparentTo(self.sprayJoint)
                    item.setPos(self.sprayJoint.getPos(render))
                    item.setHpr(self.sprayJoint.getHpr(render))
                    item.setP(0)
                else:
                    item.reparentTo(objects[index - 1])
                item.clearMat()

        track.append(Func(showSpray, sprayScale, sprayProp, origin, target))
        self.spray = sprayRot

        def calcTargetScale():
            distance = Vec3(target - origin).length()
            yScale = distance / GagGlobals.SPRAY_LEN
            targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale)
            return targetScale

        track.append(Parallel(LerpScaleInterval(sprayScale, scaleUp, calcTargetScale, startScale=GagGlobals.PNT3NEAR0), sprayNP.posInterval(0.25, self.spray.getPos(render) + Point3(0, 50, 0), startPos=self.spray.getPos(render) + Point3(0, 5, 0))))
        track.append(Wait(hold))
        track.append(Func(self.handleMiss))
        track.append(LerpScaleInterval(sprayScale, 0.75, GagGlobals.PNT3NEAR0))

        def hideSpray():
            (lambda prop: prop.removeNode(), [sprayProp, sprayRot, sprayScale])

        track.append(Func(hideSpray))
        track.append(Func(self.completeSquirt))
        return track
Example #18
0
 def __initShopCollisions(self, colName):
     self.notify.debug('Setting up shop collisions')
     shopSphere = CollisionSphere(0, 0, 0, 5)
     shopSphere.setTangible(0)
     shopNode = CollisionNode(colName)
     shopNode.addSolid(shopSphere)
     shopNode.setCollideMask(CIGlobals.WallBitmask)
     self.shopNP = self.attachNewNode(shopNode)
     self.shopNP.setZ(3)
     self.acceptOnce('enter' + self.shopNP.node().getName(), self.__handleShopCollision)
Example #19
0
 def setupPicker(self):
     self.pickerTrav = CollisionTraverser('LT.pickerTrav')
     self.pickerRay = CollisionRay()
     rayNode = CollisionNode('LT.pickerNode')
     rayNode.addSolid(self.pickerRay)
     rayNode.setCollideMask(BitMask32(0))
     rayNode.setFromCollideMask(CIGlobals.WallBitmask)
     self.pickerRayNode = base.camera.attachNewNode(rayNode)
     self.pickerHandler = CollisionHandlerQueue()
     self.pickerTrav.addCollider(self.pickerRayNode, self.pickerHandler)
Example #20
0
 def __initializeEventSphere(self):
     sphere = CollisionSphere(0, 0, 0, 2)
     sphere.setTangible(0)
     node = CollisionNode(self.uniqueName('DEagleSuit-eventSphere'))
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     np = self.attachNewNode(node)
     np.setSz(2.5)
     np.setZ(5.5)
     self.eventSphereNodePath = np
 def __initializeEventSphere(self):
     sphere = CollisionSphere(0, 0, 0, 2)
     sphere.setTangible(0)
     node = CollisionNode(self.uniqueName('DEagleSuit-eventSphere'))
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     np = self.attachNewNode(node)
     np.setSz(2.5)
     np.setZ(5.5)
     self.eventSphereNodePath = np
Example #22
0
 def __initShopCollisions(self, colName):
     self.notify.debug('Setting up shop collisions')
     shopSphere = CollisionSphere(0, 0, 0, 5)
     shopSphere.setTangible(0)
     shopNode = CollisionNode(colName)
     shopNode.addSolid(shopSphere)
     shopNode.setCollideMask(CIGlobals.WallBitmask)
     self.shopNP = self.attachNewNode(shopNode)
     self.shopNP.setZ(3)
     self.acceptOnce('enter' + self.shopNP.node().getName(),
                     self.__handleShopCollision)
 def __initInteractCollisions(self, colName):
     self.notify.debug('Setting up Coach collisions')
     collSphere = CollisionSphere(0, 0, 0, 5)
     collSphere.setTangible(0)
     collNode = CollisionNode(colName)
     collNode.addSolid(collSphere)
     collNode.setCollideMask(CIGlobals.WallBitmask)
     self.coachNP = self.attachNewNode(collNode)
     self.coachNP.setZ(3)
     self.acceptOnce('enter' + self.coachNP.node().getName(),
                     self.__handleCollision)
 def loadCollisions(self):
     sphere = CollisionSphere(0, 0, 0, 1)
     sphere.setTangible(0)
     node = CollisionNode(self.uniqueName('collectableCollNode'))
     node.addSolid(sphere)
     node.setCollideMask(CIGlobals.WallBitmask)
     self.collSensorNodePath = self.attachNewNode(node)
     ray = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
     rayNode = CollisionNode(self.uniqueName('collectableRayNode'))
     rayNode.addSolid(ray)
     rayNode.setFromCollideMask(CIGlobals.FloorBitmask)
     rayNode.setIntoCollideMask(BitMask32.allOff())
     self.collRayNodePath = self.attachNewNode(rayNode)
     lifter = CollisionHandlerFloor()
     lifter.addCollider(self.collRayNodePath, self)
     base.cTrav.addCollider(self.collRayNodePath, lifter)
Example #25
0
 def doAttack(self, weapon_path, weapon_scale, track_name, animation_name, collsphere_radius, weapon_coll_id, weapon_h = 0, weapon_p = 0, weapon_r = 0, weapon_x = 0, weapon_y = 0, weapon_z = 0, ts = 0):
     self.weapon_state = 'start'
     if hasattr(self.suit, 'uniqueName'):
         track_name = self.suit.uniqueName(track_name)
         weapon_coll_id = self.suit.uniqueName(weapon_coll_id)
     self.weapon = loader.loadModel(weapon_path)
     self.weapon.setScale(weapon_scale)
     self.weapon.setHpr(weapon_h, weapon_p, weapon_r)
     self.weapon.setPos(weapon_x, weapon_y, weapon_z)
     self.wss = CollisionSphere(0, 0, 0, collsphere_radius)
     self.wss.setTangible(0)
     self.targetX = self.attacksClass.target.getX(render)
     self.targetY = self.attacksClass.target.getY(render)
     self.targetZ = self.attacksClass.target.getZ(render)
     if self.attack not in ('glowerpower',):
         actorIval = ActorInterval(self.suit, animation_name, playRate=2.0, duration=1.2)
         actorIval2 = ActorInterval(self.suit, animation_name, startTime=2.4)
     else:
         actorIval = ActorInterval(self.suit, animation_name)
     seq = Sequence()
     if self.attack not in ('glowerpower',):
         self.suitTrack = Parallel(Sequence(actorIval, actorIval2), name=track_name)
         self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
         seq.append(Wait(1.2))
         if self.suit.suitPlan.getSuitType() == 'C':
             seq.append(Wait(0))
         else:
             seq.append(Wait(0.7))
         seq.append(Func(self.throwObject))
         seq.append(Wait(1.0))
         seq.append(Func(self.delWeapon))
     else:
         self.suitTrack = Parallel(actorIval, name=track_name)
         seq.append(Wait(1))
         seq.append(Func(self.throwObject))
         seq.append(Wait(0.5))
         seq.append(Func(self.delWeapon))
     self.suitTrack.append(seq)
     wsnode = CollisionNode(weapon_coll_id)
     wsnode.addSolid(self.wss)
     wsnode.setCollideMask(CIGlobals.WallBitmask)
     self.wsnp = self.weapon.attachNewNode(wsnode)
     self.suitTrack.setDoneEvent(self.suitTrack.getName())
     self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
     self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, track_name)
     self.suitTrack.start(ts)
    def announceGenerate(self):
        DistributedNode.announceGenerate(self)
        self.kart = loader.loadModel('phase_6/models/karting/Kart3_Final.bam')
        self.kart.find('**/decals').removeNode()
        self.kart.reparentTo(self)
        self.pod = loader.loadModel('phase_4/models/minigames/pods_truck.egg')
        self.pod.reparentTo(self)
        self.pod.setScale(0.2)
        self.pod.setY(8.5)
        self.pod.setH(180)
        self.pod.find('**/metal_ramp').setBin('ground', 18)
        self.pod.find('**/metal_ramp_coll').setCollideMask(
            CIGlobals.FloorBitmask)
        self.rope = Rope()
        self.rope.ropeNode.setUseVertexColor(1)
        self.rope.setup(3, ({
            'node': self.kart,
            'point': (0, 1.5, 0.7),
            'color': (0, 0, 0, 1),
            'thickness': 1000
        }, {
            'node': self.kart,
            'point': (0, 1.5, 0.7),
            'color': (0, 0, 0, 1),
            'thickness': 1000
        }, {
            'node': self.pod,
            'point': (0, 31, 5),
            'color': (0, 0, 0, 1),
            'thickness': 1000
        }), [])
        self.rope.setH(180)
        self.rope.reparentTo(self)
        sphere = CollisionSphere(0, 0, 0, 2)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('truck_trigger'))
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.triggerNP = self.attachNewNode(node)
        self.triggerNP.setPos(0, 8.0, 2.0)
        self.setScale(2.0)

        self.accept('enter' + self.triggerNP.node().getName(),
                    self.__handleTruckTrigger)
Example #27
0
    def __init__(self, base, n, x, y, z):
        self.base = base
        self.n = n
        self.health = 4
        self.np = base.loader.loadModel("./mdl/enemy.egg")
        self.np.setColor(1, 1, 0, 1)
        self.np.reparentTo(base.render)
        self.np.setPos(x, y, z)
        self.np.setAlphaScale(0.)
        base.target = self
        self.radar = OnscreenImage(image="./png/radar.png",
                                   pos=Vec3(0),
                                   scale=0.01)
        self.radar.setTransparency(TransparencyAttrib.MAlpha)

        # collisions
        cn = CollisionNode("enemy" + str(n))
        cn.addSolid(CollisionBox(0, 2.5, 2, 0.5))
        cn.setCollideMask(BitMask32(0x1) | BitMask32(0x2))
        # cc = self.np.attachNewNode(cn)
        self.np.attachNewNode(cn)
        base.accept("fighter-into-enemy" + str(n), self.ship_collision)
        base.accept("bullet-into-enemy" + str(n), self.bullet_collision)
        base.accept("missile-into-enemy" + str(n), self.missile_collision)

        # sound
        self.snd_crash = base.loader.loadSfx("./snd/crash.flac")
        self.snd_blip = base.audio3d.loadSfx("./snd/blip.flac")
        self.snd_hit = base.loader.loadSfx("./snd/hit.flac")
        self.snd_explode = base.loader.loadSfx("./snd/explosion.flac")
        base.audio3d.attachSoundToObject(self.snd_blip, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_blip)
        base.audio3d.attachSoundToObject(self.snd_crash, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_crash)
        base.audio3d.attachSoundToObject(self.snd_hit, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_hit)
        base.audio3d.attachSoundToObject(self.snd_explode, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_explode)
        self.snd_blip.setLoop(True)
        self.snd_blip.play()

        self.setAI()
Example #28
0
    def __init__(self, size):
        DirectFrame.__init__(self,
                             parent=aspect2d,
                             pos=(0, -1.0, 0),
                             relief=None)
        image = loader.loadModel(
            "phase_4/models/minigames/photo_game_viewfinder.bam")
        self['image'] = image
        self['image_scale'] = (size, 1.0, size)

        self.screenSizeMult = size * Viewfinder.Ratio

        self.setTransparency(True)
        self.setDepthWrite(1)
        self.setDepthTest(1)

        self.initialiseoptions(Viewfinder)

        self.captureCam = NodePath(Camera("CaptureCamera"))
        self.captureCam.reparentTo(base.camera)
        self.captureLens = PerspectiveLens()
        self.captureCam.node().setLens(self.captureLens)

        self.focusTrav = CollisionTraverser('focusTrav')
        ray = CollisionRay()
        rayNode = CollisionNode('rayNode')
        rayNode.addSolid(ray)
        rayNode.setCollideMask(BitMask32(0))
        rayNode.setFromCollideMask(CIGlobals.WallBitmask)
        self.focusRay = ray
        self.focusRayNode = self.captureCam.attachNewNode(rayNode)
        self.focusHandler = CollisionHandlerQueue()
        self.focusTrav.addCollider(self.focusRayNode, self.focusHandler)

        self.textureBuffer = base.win.makeTextureBuffer(
            "ViewFinderCapture", int(128 * 1.33), 128)
        self.displayRegion = self.textureBuffer.makeDisplayRegion()
        self.displayRegion.setCamera(self.captureCam)

        self.__updateRegions()

        taskMgr.add(self.__update, "viewfinderUpdate")
Example #29
0
 def loadAttack(self):
     self.pen = loader.loadModel('phase_5/models/props/pen.bam')
     self.pen.reparentTo(self.suit.find('**/joint_Rhold'))
     self.sprayParticle = ParticleLoader.loadParticleEffect('phase_5/etc/penSpill.ptf')
     self.spray = loader.loadModel('phase_3.5/models/props/spray.bam')
     self.spray.setColor(VBase4(0, 0, 0, 1))
     self.splat = Actor('phase_3.5/models/props/splat-mod.bam', {'chan': 'phase_3.5/models/props/splat-chan.bam'})
     self.splat.setColor(VBase4(0, 0, 0, 1))
     self.sprayScaleIval = LerpScaleInterval(self.spray, duration=0.3, scale=(1, 20, 1), startScale=(1, 1, 1))
     sphere = CollisionSphere(0, 0, 0, 0.5)
     sphere.setTangible(0)
     if hasattr(self.suit, 'uniqueName'):
         collName = self.suit.uniqueName('fountainPenCollNode')
     else:
         collName = 'fountainPenCollNode'
     collNode = CollisionNode(collName)
     collNode.addSolid(sphere)
     collNode.setCollideMask(CIGlobals.WallBitmask)
     self.wsnp = self.spray.attachNewNode(collNode)
     self.wsnp.setY(1)
 def doAttack(self, weapon_path, weapon_scale, track_name, animation_name, collsphere_radius, weapon_coll_id, weapon_h=0, weapon_p=0, weapon_r=0, weapon_x=0, weapon_y=0, weapon_z=0, ts=0):
     self.weapon_state = 'start'
     if hasattr(self.suit, 'uniqueName'):
         track_name = self.suit.uniqueName(track_name)
         weapon_coll_id = self.suit.uniqueName(weapon_coll_id)
     self.weapon = loader.loadModel(weapon_path)
     self.weapon.setScale(weapon_scale)
     self.weapon.setHpr(weapon_h, weapon_p, weapon_r)
     self.weapon.setPos(weapon_x, weapon_y, weapon_z)
     self.wss = CollisionSphere(0, 0, 0, collsphere_radius)
     self.wss.setTangible(0)
     self.targetX = self.attacksClass.target.getX(render)
     self.targetY = self.attacksClass.target.getY(render)
     self.targetZ = self.attacksClass.target.getZ(render)
     self.suitTrack = Sequence(name=track_name)
     if self.attack not in ('glowerpower', ):
         self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
         self.suitTrack.append(Wait(1.2))
         self.suitTrack.append(Func(self.suit.setPlayRate, 1.0, animation_name))
         if self.suit.type == 'C':
             self.suitTrack.append(Wait(0))
         else:
             self.suitTrack.append(Wait(0.7))
         self.suit.setPlayRate(2.0, animation_name)
         self.suitTrack.append(Func(self.throwObject))
         self.suitTrack.append(Wait(1.0))
         self.suitTrack.append(Func(self.delWeapon))
     else:
         self.suitTrack.append(Wait(1))
         self.suitTrack.append(Func(self.throwObject))
         self.suitTrack.append(Wait(0.5))
         self.suitTrack.append(Func(self.delWeapon))
     self.suit.play(animation_name)
     wsnode = CollisionNode(weapon_coll_id)
     wsnode.addSolid(self.wss)
     wsnode.setCollideMask(CIGlobals.WallBitmask)
     self.wsnp = self.weapon.attachNewNode(wsnode)
     self.suitTrack.setDoneEvent(self.suitTrack.getName())
     self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
     self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, track_name)
     self.suitTrack.start(ts)
    def start(self):
        self.fullyChargedSound = base.loadSfx(
            'phase_4/audio/sfx/MG_pairing_match.ogg')
        self.rechargeSound = base.loadSfx(
            'phase_4/audio/sfx/MG_sfx_travel_game_blue_arrow.ogg')
        self.batteryFrame = DirectFrame(parent=base.a2dBottomRight,
                                        pos=(-0.2, 0, 0.1),
                                        scale=(0.8, 0, 1))
        self.batteryBg = OnscreenImage(
            image='phase_4/maps/battery_charge_frame.png',
            parent=self.batteryFrame)
        self.batteryBg.setTransparency(1)
        self.batteryBg.setX(0.03)
        self.batteryBg.setScale(0.17, 0, 0.05)
        self.batteryBar = DirectWaitBar(value=0,
                                        range=5,
                                        barColor=(1, 1, 1, 1),
                                        relief=None,
                                        scale=(0.12, 0.0, 0.3),
                                        parent=self.batteryFrame)
        self.cameraFocus = loader.loadModel(
            "phase_4/models/minigames/photo_game_viewfinder.bam")
        self.cameraFocus.reparentTo(base.aspect2d)

        self.focusTrav = CollisionTraverser('CSFP.focusTrav')
        ray = CollisionRay()
        rayNode = CollisionNode('CSFP.rayNode')
        rayNode.addSolid(ray)
        rayNode.setCollideMask(BitMask32(0))
        rayNode.setFromCollideMask(CIGlobals.WallBitmask)
        self.focusRay = ray
        self.focusRayNode = base.camera.attachNewNode(rayNode)
        self.focusHandler = CollisionHandlerQueue()
        self.focusTrav.addCollider(self.focusRayNode, self.focusHandler)

        base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed,
                                                   0.0,
                                                   CIGlobals.ToonReverseSpeed,
                                                   CIGlobals.ToonRotateSpeed)
        FirstPerson.start(self)
Example #32
0
    def __init__(self, base, n, x, y, z):
        self.base = base
        self.n = n
        self.health = 4
        self.np = base.loader.loadModel("./mdl/enemy.egg")
        self.np.setColor(1, 1, 0, 1)
        self.np.reparentTo(base.render)
        self.np.setPos(x, y, z)
        self.np.setAlphaScale(0.)
        base.target = self
        self.radar = OnscreenImage(image="./png/radar.png", pos=Vec3(0), scale=0.01)
        self.radar.setTransparency(TransparencyAttrib.MAlpha)

        # collisions
        cn = CollisionNode("enemy"+str(n))
        cn.addSolid(CollisionBox(0, 2.5, 2, 0.5))
        cn.setCollideMask(BitMask32(0x1) | BitMask32(0x2))
        # cc = self.np.attachNewNode(cn)
        self.np.attachNewNode(cn)
        base.accept("fighter-into-enemy"+str(n), self.ship_collision)
        base.accept("bullet-into-enemy"+str(n), self.bullet_collision)
        base.accept("missile-into-enemy"+str(n), self.missile_collision)

        # sound
        self.snd_crash = base.loader.loadSfx("./snd/crash.flac")
        self.snd_blip = base.audio3d.loadSfx("./snd/blip.flac")
        self.snd_hit = base.loader.loadSfx("./snd/hit.flac")
        self.snd_explode = base.loader.loadSfx("./snd/explosion.flac")
        base.audio3d.attachSoundToObject(self.snd_blip, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_blip)
        base.audio3d.attachSoundToObject(self.snd_crash, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_crash)
        base.audio3d.attachSoundToObject(self.snd_hit, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_hit)
        base.audio3d.attachSoundToObject(self.snd_explode, self.np)
        base.audio3d.setSoundVelocityAuto(self.snd_explode)
        self.snd_blip.setLoop(True)
        self.snd_blip.play()

        self.setAI()
Example #33
0
    def generate(self):
        DistributedNode.generate(self)
        self.cart = loader.loadModel(self.cartModelPath)
        self.cart.reparentTo(self)
        self.cart.setH(180)
        heads = []
        for head in CIGlobals.SuitBodyData.keys():
            if CIGlobals.SuitBodyData[head][0] != 'B':
                heads.append(head)

        head = random.choice(heads)
        suitType = CIGlobals.SuitBodyData[head][0]
        suitDept = CIGlobals.SuitBodyData[head][1]
        self.suitInCar = Suit()
        self.suitInCar.generateSuit(suitType, head, suitDept, 137, 0, False)
        self.suitInCar.loop('sit')
        self.suitInCar.disableRay()
        self.suitInCar.setScale(0.7)
        self.suitInCar.setH(180)
        self.suitInCar.setPos(0, -1, -1.5)
        self.suitInCar.reparentTo(self.cart.find('**/seat1'))
        self.soundEngineLoop = base.audio3d.loadSfx('phase_6/audio/sfx/KART_Engine_loop_0.wav')
        base.audio3d.attachSoundToObject(self.soundEngineLoop, self)
        base.playSfx(self.soundEngineLoop, looping=1)
        self.soundDriveByHorn = base.audio3d.loadSfx(self.honkSfxPath)
        base.audio3d.attachSoundToObject(self.soundDriveByHorn, self)
        self.soundDriveBy = base.audio3d.loadSfx('phase_14/audio/sfx/cogtropolis_citycar_driveby.mp3')
        base.audio3d.attachSoundToObject(self.soundDriveBy, self)
        sphere = CollisionSphere(0, 0, 0, 2.5)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('cartSphere'))
        node.setCollideMask(CIGlobals.WallBitmask)
        node.addSolid(sphere)
        self.collNodePath = self.attachNewNode(node)
        self.collNodePath.setZ(1.5)
        self.collNodePath.setSy(2.0)
        self.collNodePath.setSx(1.75)
 def loadAttack(self):
     self.pen = loader.loadModel('phase_5/models/props/pen.bam')
     self.pen.reparentTo(self.suit.find('**/joint_Rhold'))
     self.sprayParticle = ParticleLoader.loadParticleEffect('phase_5/etc/penSpill.ptf')
     self.spray = loader.loadModel('phase_3.5/models/props/spray.bam')
     self.spray.setColor(VBase4(0, 0, 0, 1))
     self.splat = Actor('phase_3.5/models/props/splat-mod.bam', {'chan': 'phase_3.5/models/props/splat-chan.bam'})
     self.splat.setColor(VBase4(0, 0, 0, 1))
     self.sprayScaleIval = LerpScaleInterval(self.spray, duration=0.3, scale=(1,
                                                                              20,
                                                                              1), startScale=(1,
                                                                                              1,
                                                                                              1))
     sphere = CollisionSphere(0, 0, 0, 0.5)
     sphere.setTangible(0)
     if hasattr(self.suit, 'uniqueName'):
         collName = self.suit.uniqueName('fountainPenCollNode')
     else:
         collName = 'fountainPenCollNode'
     collNode = CollisionNode(collName)
     collNode.addSolid(sphere)
     collNode.setCollideMask(CIGlobals.WallBitmask)
     self.wsnp = self.spray.attachNewNode(collNode)
     self.wsnp.setY(1)
 def generate(self):
     DistributedNode.generate(self)
     self.cart = loader.loadModel(self.cartModelPath)
     self.cart.reparentTo(self)
     self.cart.setH(180)
     heads = []
     for head in CIGlobals.SuitBodyData.keys():
         if CIGlobals.SuitBodyData[head][0] != 'B':
             heads.append(head)
     head = random.choice(heads)
     suitType = CIGlobals.SuitBodyData[head][0]
     suitDept = CIGlobals.SuitBodyData[head][1]
     self.suitInCar = Suit()
     self.suitInCar.generateSuit(suitType, head, suitDept, 137, 0, False)
     self.suitInCar.loop('sit')
     self.suitInCar.disableRay()
     self.suitInCar.setScale(0.7)
     self.suitInCar.setH(180)
     self.suitInCar.setPos(0, -1, -1.5)
     self.suitInCar.reparentTo(self.cart.find('**/seat1'))
     self.soundEngineLoop = base.audio3d.loadSfx('phase_6/audio/sfx/KART_Engine_loop_0.ogg')
     base.audio3d.attachSoundToObject(self.soundEngineLoop, self)
     base.playSfx(self.soundEngineLoop, looping = 1)
     self.soundDriveByHorn = base.audio3d.loadSfx(self.honkSfxPath)
     base.audio3d.attachSoundToObject(self.soundDriveByHorn, self)
     self.soundDriveBy = base.audio3d.loadSfx('phase_14/audio/sfx/cogtropolis_citycar_driveby.ogg')
     base.audio3d.attachSoundToObject(self.soundDriveBy, self)
     sphere = CollisionSphere(0, 0, 0, 2.5)
     sphere.setTangible(0)
     node = CollisionNode(self.uniqueName('cartSphere'))
     node.setCollideMask(CIGlobals.WallBitmask)
     node.addSolid(sphere)
     self.collNodePath = self.attachNewNode(node)
     self.collNodePath.setZ(1.5)
     self.collNodePath.setSy(2.0)
     self.collNodePath.setSx(1.75)
 def generate(self):
     DistributedNode.generate(self)
     self.cart = loader.loadModel(self.cartModelPath)
     self.cart.reparentTo(self)
     self.cart.setH(180)
     plans = []
     for plan in SuitBank.getSuits():
         if plan.getSuitType() != SuitType.B:
             plans.append(plan)
     plan = random.choice(plans)
     self.suitInCar = Suit()
     self.suitInCar.level = 0
     self.suitInCar.generate(plan, Variant.NORMAL)
     self.suitInCar.loop('sit')
     self.suitInCar.disableRay()
     self.suitInCar.setScale(0.7)
     self.suitInCar.setH(180)
     self.suitInCar.setPos(0, -1, -1.5)
     self.suitInCar.reparentTo(self.cart.find('**/seat1'))
     self.suitInCar.show()
     self.soundEngineLoop = base.audio3d.loadSfx('phase_6/audio/sfx/KART_Engine_loop_0.ogg')
     base.audio3d.attachSoundToObject(self.soundEngineLoop, self)
     base.playSfx(self.soundEngineLoop, looping = 1)
     self.soundDriveByHorn = base.audio3d.loadSfx(self.honkSfxPath)
     base.audio3d.attachSoundToObject(self.soundDriveByHorn, self)
     self.soundDriveBy = base.audio3d.loadSfx('phase_14/audio/sfx/cogtropolis_citycar_driveby.ogg')
     base.audio3d.attachSoundToObject(self.soundDriveBy, self)
     sphere = CollisionSphere(0, 0, 0, 2.5)
     sphere.setTangible(0)
     node = CollisionNode(self.uniqueName('cartSphere'))
     node.setCollideMask(CIGlobals.WallBitmask)
     node.addSolid(sphere)
     self.collNodePath = self.attachNewNode(node)
     self.collNodePath.setZ(1.5)
     self.collNodePath.setSy(2.0)
     self.collNodePath.setSx(1.75)
Example #37
0
class DistributedLawbotCannon(DistributedObject.DistributedObject):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedLawbotCannon')
    LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask'
    FIRE_KEY = 'control'
    UP_KEY = 'arrow_up'
    DOWN_KEY = 'arrow_down'
    LEFT_KEY = 'arrow_left'
    RIGHT_KEY = 'arrow_right'
    HIT_GROUND = 0

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.index = None
        self.avId = 0
        self.av = None
        self.localToonShooting = 0
        self.cannonsActive = 0
        self.cannonLocation = None
        self.cannonPostion = None
        self.cannon = None
        self.madeGui = 0
        self.jurorToon = None
        self.toonModel = None
        self.toonHead = None
        self.toonScale = None
        self.dustCloud = None
        self.hitBumper = 0
        self.hitTarget = 0
        self.lastPos = Vec3(0, 0, 0)
        self.lastVel = Vec3(0, 0, 0)
        self.vel = Vec3(0, 0, 0)
        self.landingPos = Vec3(0, 0, 0)
        self.t = 0
        self.lastT = 0
        self.deltaT = 0
        self.hitTrack = None
        self.flyColNode = None
        self.flyColNodePath = None
        self.localAvId = base.localAvatar.doId
        self.model_Created = 0
        return

    def disable(self):
        taskMgr.remove(self.uniqueName('fireCannon'))
        taskMgr.remove(self.uniqueName('shootTask'))
        self.__stopFlyTask(self.avId)
        taskMgr.remove(self.uniqueName('flyTask'))
        self.ignoreAll()
        self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0)
        self.nodePath.detachNode()
        self.__unmakeGui()
        if self.hitTrack:
            self.hitTrack.finish()
            del self.hitTrack
            self.hitTrack = None
        DistributedObject.DistributedObject.disable(self)
        return

    def delete(self):
        self.offstage()
        self.unload()
        DistributedObject.DistributedObject.delete(self)

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        self.boss.cannons[self.index] = self

    def generateInit(self):
        DistributedObject.DistributedObject.generateInit(self)
        self.nodePath = NodePath(self.uniqueName('Cannon'))
        self.load()
        self.activateCannons()

    def setPosHpr(self, x, y, z, h, p, r):
        self.nodePath.setPosHpr(x, y, z, h, p, r)

    def setBossCogId(self, bossCogId):
        self.bossCogId = bossCogId
        self.boss = base.cr.doId2do[bossCogId]

    def getSphereRadius(self):
        return 1.5

    def getParentNodePath(self):
        return render

    def setIndex(self, index):
        self.index = index

    def load(self):
        self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon')
        self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius())
        self.dustCloud = DustCloud.DustCloud(render)
        self.dustCloud.setBillboardPointEye()
        self.collSphere.setTangible(1)
        self.collNode = CollisionNode(self.uniqueName('CannonSphere'))
        self.collNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.nodePath.attachNewNode(self.collNode)
        self.cannon.reparentTo(self.nodePath)
        self.kartColNode = CollisionNode(self.uniqueName('KartColNode'))
        self.kartNode = self.nodePath.attachNewNode(self.kartColNode)
        self.sndCannonMove = base.loader.loadSfx(
            'phase_4/audio/sfx/MG_cannon_adjust.ogg')
        self.sndCannonFire = base.loader.loadSfx(
            'phase_4/audio/sfx/MG_cannon_fire_alt.ogg')
        self.sndHitGround = base.loader.loadSfx(
            'phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndHitChair = base.loader.loadSfx(
            'phase_11/audio/sfx/LB_toon_jury.ogg')
        self.cannon.hide()
        self.flashingLabel = None
        return

    def unload(self):
        if self.cannon:
            self.cannon.removeNode()
            del self.cannon
        if self.dustCloud != None:
            self.dustCloud.destroy()
            del self.dustCloud
        del self.sndCannonMove
        del self.sndCannonFire
        del self.sndHitGround
        del self.sndHitChair
        if self.av:
            self.__resetToon(self.av)
            self.av.loop('neutral')
            self.av.setPlayRate(1.0, 'run')
        if self.toonHead != None:
            self.toonHead.stopBlink()
            self.toonHead.stopLookAroundNow()
            self.toonHead.delete()
            del self.toonHead
        if self.toonModel != None:
            self.toonModel.removeNode()
            del self.toonModel
        if self.jurorToon != None:
            self.jurorToon.delete()
            del self.jurorToon
        del self.toonScale
        return

    def activateCannons(self):
        if not self.cannonsActive:
            self.cannonsActive = 1
            self.onstage()
            self.nodePath.reparentTo(self.getParentNodePath())
            self.accept(self.uniqueName('enterCannonSphere'),
                        self.__handleEnterSphere)

    def onstage(self):
        self.__createCannon()
        self.cannon.reparentTo(self.nodePath)
        self.dustCloud.reparentTo(render)

    def offstage(self):
        if self.cannon:
            self.cannon.reparentTo(hidden)
        if self.dustCloud:
            self.dustCloud.reparentTo(hidden)
            self.dustCloud.stop()

    def __createCannon(self):
        self.barrel = self.cannon.find('**/cannon')
        self.cannonLocation = Point3(0, 0, 0.025)
        self.cannonPosition = [0, CANNON_ANGLE_MIN]
        self.cannon.setPos(self.cannonLocation)
        self.__updateCannonPosition(self.avId)

    def updateCannonPosition(self, avId, zRot, angle):
        if avId != self.localAvId:
            self.cannonPosition = [zRot, angle]
            self.__updateCannonPosition(avId)

    def __updateCannonPosition(self, avId):
        self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0)
        self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0)
        maxP = 90
        newP = self.barrel.getP()
        yScale = 1 - 0.5 * float(newP) / maxP
        shadow = self.cannon.find('**/square_drop_shadow')
        shadow.setScale(1, yScale, 1)

    def __handleEnterSphere(self, collEntry):
        self.d_requestEnter()

    def d_requestEnter(self):
        self.sendUpdate('requestEnter', [])

    def setMovie(self, mode, avId, extraInfo):
        wasLocalToon = self.localToonShooting
        self.avId = avId
        if mode == CannonGlobals.CANNON_MOVIE_CLEAR:
            self.setLanded()
        else:
            if mode == CannonGlobals.CANNON_MOVIE_LANDED:
                self.setLanded()
            else:
                if mode == CannonGlobals.CANNON_MOVIE_FORCE_EXIT:
                    self.exitCannon(self.avId)
                    self.setLanded()
                else:
                    if mode == CannonGlobals.CANNON_MOVIE_LOAD:
                        if self.avId == base.localAvatar.doId:
                            self.cannonBallsLeft = extraInfo
                            base.cr.playGame.getPlace().setState('crane')
                            base.localAvatar.setTeleportAvailable(0)
                            self.localToonShooting = 1
                            self.__makeGui()
                            camera.reparentTo(self.barrel)
                            camera.setPos(0.5, -2, 2.5)
                            camera.setHpr(0, 0, 0)
                            self.boss.toonEnteredCannon(self.avId, self.index)
                        if self.avId in self.cr.doId2do:
                            self.av = self.cr.doId2do[self.avId]
                            self.acceptOnce(self.av.uniqueName('disable'),
                                            self.__avatarGone)
                            self.av.loop('neutral')
                            self.av.stopSmooth()
                            self.__destroyToonModels()
                            self.__createToonModels()
                            self.av.setPosHpr(3, 0, 0, 90, 0, 0)
                            self.av.reparentTo(self.cannon)
                        else:
                            self.notify.warning(
                                'Unknown avatar %d in cannon %d' %
                                (self.avId, self.doId))
                    else:
                        self.notify.warning('unhandled case, mode = %d' % mode)

    def __avatarGone(self):
        self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0)

    def __makeGui(self):
        if self.madeGui:
            return
        NametagGlobals.setMasterArrowsOn(0)
        guiModel = 'phase_4/models/gui/cannon_game_gui'
        cannonGui = loader.loadModel(guiModel)
        self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'),
                                  relief=None,
                                  pos=(0.7, 0, -0.553333),
                                  scale=0.8)
        cannonGui.removeNode()
        self.fireButton = DirectButton(parent=self.aimPad,
                                       image=((guiModel, '**/Fire_Btn_UP'),
                                              (guiModel, '**/Fire_Btn_DN'),
                                              (guiModel, '**/Fire_Btn_RLVR')),
                                       relief=None,
                                       pos=(0.0115741, 0, 0.00505051),
                                       scale=1.0,
                                       command=self.__firePressed)
        self.upButton = DirectButton(parent=self.aimPad,
                                     image=((guiModel, '**/Cannon_Arrow_UP'),
                                            (guiModel, '**/Cannon_Arrow_DN'),
                                            (guiModel,
                                             '**/Cannon_Arrow_RLVR')),
                                     relief=None,
                                     pos=(0.0115741, 0, 0.221717))
        self.downButton = DirectButton(parent=self.aimPad,
                                       image=((guiModel, '**/Cannon_Arrow_UP'),
                                              (guiModel, '**/Cannon_Arrow_DN'),
                                              (guiModel,
                                               '**/Cannon_Arrow_RLVR')),
                                       relief=None,
                                       pos=(0.0136112, 0, -0.210101),
                                       image_hpr=(0, 0, 180))
        self.leftButton = DirectButton(parent=self.aimPad,
                                       image=((guiModel, '**/Cannon_Arrow_UP'),
                                              (guiModel, '**/Cannon_Arrow_DN'),
                                              (guiModel,
                                               '**/Cannon_Arrow_RLVR')),
                                       relief=None,
                                       pos=(-0.199352, 0, -0.000505269),
                                       image_hpr=(0, 0, -90))
        self.rightButton = DirectButton(
            parent=self.aimPad,
            image=((guiModel, '**/Cannon_Arrow_UP'),
                   (guiModel, '**/Cannon_Arrow_DN'), (guiModel,
                                                      '**/Cannon_Arrow_RLVR')),
            relief=None,
            pos=(0.219167, 0, -0.00101024),
            image_hpr=(0, 0, 90))
        guiClose = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui')
        cannonBallText = '%d/%d' % (self.cannonBallsLeft,
                                    ToontownGlobals.LawbotBossCannonBallMax)
        self.cannonBallLabel = DirectLabel(parent=self.aimPad,
                                           text=cannonBallText,
                                           text_fg=VBase4(1, 1, 1, 1),
                                           text_align=TextNode.ACenter,
                                           relief=None,
                                           pos=(0.475, 0.0, -0.35),
                                           scale=0.25)
        if self.cannonBallsLeft < 5:
            if self.flashingLabel:
                self.flashingLabel.stop()
            flashingTrack = Sequence()
            for i in range(10):
                flashingTrack.append(
                    LerpColorScaleInterval(self.cannonBallLabel, 0.5,
                                           VBase4(1, 0, 0, 1)))
                flashingTrack.append(
                    LerpColorScaleInterval(self.cannonBallLabel, 0.5,
                                           VBase4(1, 1, 1, 1)))

            self.flashingLabel = flashingTrack
            self.flashingLabel.start()
        self.aimPad.setColor(1, 1, 1, 0.9)

        def bindButton(button, upHandler, downHandler):
            button.bind(DGG.B1PRESS, lambda x, handler=upHandler: handler())
            button.bind(DGG.B1RELEASE,
                        lambda x, handler=downHandler: handler())

        bindButton(self.upButton, self.__upPressed, self.__upReleased)
        bindButton(self.downButton, self.__downPressed, self.__downReleased)
        bindButton(self.leftButton, self.__leftPressed, self.__leftReleased)
        bindButton(self.rightButton, self.__rightPressed, self.__rightReleased)
        self.__enableAimInterface()
        self.madeGui = 1
        return

    def __unmakeGui(self):
        self.notify.debug('__unmakeGui')
        if not self.madeGui:
            return
        if self.flashingLabel:
            self.flashingLabel.finish()
            self.flashingLabel = None
        NametagGlobals.setMasterArrowsOn(1)
        self.__disableAimInterface()
        self.upButton.unbind(DGG.B1PRESS)
        self.upButton.unbind(DGG.B1RELEASE)
        self.downButton.unbind(DGG.B1PRESS)
        self.downButton.unbind(DGG.B1RELEASE)
        self.leftButton.unbind(DGG.B1PRESS)
        self.leftButton.unbind(DGG.B1RELEASE)
        self.rightButton.unbind(DGG.B1PRESS)
        self.rightButton.unbind(DGG.B1RELEASE)
        self.aimPad.destroy()
        del self.aimPad
        del self.fireButton
        del self.upButton
        del self.downButton
        del self.leftButton
        del self.rightButton
        self.madeGui = 0
        return

    def __enableAimInterface(self):
        self.aimPad.show()
        self.accept(self.FIRE_KEY, self.__fireKeyPressed)
        self.accept(self.UP_KEY, self.__upKeyPressed)
        self.accept(self.DOWN_KEY, self.__downKeyPressed)
        self.accept(self.LEFT_KEY, self.__leftKeyPressed)
        self.accept(self.RIGHT_KEY, self.__rightKeyPressed)
        self.__spawnLocalCannonMoveTask()

    def __disableAimInterface(self):
        self.aimPad.hide()
        self.ignore(self.FIRE_KEY)
        self.ignore(self.UP_KEY)
        self.ignore(self.DOWN_KEY)
        self.ignore(self.LEFT_KEY)
        self.ignore(self.RIGHT_KEY)
        self.ignore(self.FIRE_KEY + '-up')
        self.ignore(self.UP_KEY + '-up')
        self.ignore(self.DOWN_KEY + '-up')
        self.ignore(self.LEFT_KEY + '-up')
        self.ignore(self.RIGHT_KEY + '-up')
        self.__killLocalCannonMoveTask()

    def __fireKeyPressed(self):
        self.ignore(self.FIRE_KEY)
        self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased)
        self.__firePressed()

    def __upKeyPressed(self):
        self.ignore(self.UP_KEY)
        self.accept(self.UP_KEY + '-up', self.__upKeyReleased)
        self.__upPressed()

    def __downKeyPressed(self):
        self.ignore(self.DOWN_KEY)
        self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased)
        self.__downPressed()

    def __leftKeyPressed(self):
        self.ignore(self.LEFT_KEY)
        self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased)
        self.__leftPressed()

    def __rightKeyPressed(self):
        self.ignore(self.RIGHT_KEY)
        self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased)
        self.__rightPressed()

    def __fireKeyReleased(self):
        self.ignore(self.FIRE_KEY + '-up')
        self.accept(self.FIRE_KEY, self.__fireKeyPressed)

    def __leftKeyReleased(self):
        self.ignore(self.LEFT_KEY + '-up')
        self.accept(self.LEFT_KEY, self.__leftKeyPressed)
        self.__leftReleased()

    def __rightKeyReleased(self):
        self.ignore(self.RIGHT_KEY + '-up')
        self.accept(self.RIGHT_KEY, self.__rightKeyPressed)
        self.__rightReleased()

    def __upKeyReleased(self):
        self.ignore(self.UP_KEY + '-up')
        self.accept(self.UP_KEY, self.__upKeyPressed)
        self.__upReleased()

    def __downKeyReleased(self):
        self.ignore(self.DOWN_KEY + '-up')
        self.accept(self.DOWN_KEY, self.__downKeyPressed)
        self.__downReleased()

    def __leaveCannon(self):
        self.notify.debug('__leaveCannon')
        self.sendUpdate('requestLeave')

    def __firePressed(self):
        self.notify.debug('fire pressed')
        if not self.boss.state == 'BattleTwo':
            self.notify.debug('boss is in state=%s, not firing' %
                              self.boss.state)
            return
        self.__broadcastLocalCannonPosition()
        self.__unmakeGui()
        self.sendUpdate('setCannonLit',
                        [self.cannonPosition[0], self.cannonPosition[1]])

    def __upPressed(self):
        self.notify.debug('up pressed')
        self.upPressed = self.__enterControlActive(self.upPressed)

    def __downPressed(self):
        self.notify.debug('down pressed')
        self.downPressed = self.__enterControlActive(self.downPressed)

    def __leftPressed(self):
        self.notify.debug('left pressed')
        self.leftPressed = self.__enterControlActive(self.leftPressed)

    def __rightPressed(self):
        self.notify.debug('right pressed')
        self.rightPressed = self.__enterControlActive(self.rightPressed)

    def __upReleased(self):
        self.notify.debug('up released')
        self.upPressed = self.__exitControlActive(self.upPressed)

    def __downReleased(self):
        self.notify.debug('down released')
        self.downPressed = self.__exitControlActive(self.downPressed)

    def __leftReleased(self):
        self.notify.debug('left released')
        self.leftPressed = self.__exitControlActive(self.leftPressed)

    def __rightReleased(self):
        self.notify.debug('right released')
        self.rightPressed = self.__exitControlActive(self.rightPressed)

    def __enterControlActive(self, control):
        return control + 1

    def __exitControlActive(self, control):
        return max(0, control - 1)

    def __spawnLocalCannonMoveTask(self):
        self.leftPressed = 0
        self.rightPressed = 0
        self.upPressed = 0
        self.downPressed = 0
        self.cannonMoving = 0
        task = Task(self.__localCannonMoveTask)
        task.lastPositionBroadcastTime = 0.0
        taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK)

    def __killLocalCannonMoveTask(self):
        taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK)
        if self.cannonMoving:
            self.sndCannonMove.stop()

    def __localCannonMoveTask(self, task):
        pos = self.cannonPosition
        oldRot = pos[0]
        oldAng = pos[1]
        rotVel = 0
        if self.leftPressed:
            rotVel += CANNON_ROTATION_VEL
        if self.rightPressed:
            rotVel -= CANNON_ROTATION_VEL
        pos[0] += rotVel * globalClock.getDt()
        if pos[0] < CANNON_ROTATION_MIN:
            pos[0] = CANNON_ROTATION_MIN
        else:
            if pos[0] > CANNON_ROTATION_MAX:
                pos[0] = CANNON_ROTATION_MAX
        angVel = 0
        if self.upPressed:
            angVel += CANNON_ANGLE_VEL
        if self.downPressed:
            angVel -= CANNON_ANGLE_VEL
        pos[1] += angVel * globalClock.getDt()
        if pos[1] < CANNON_ANGLE_MIN:
            pos[1] = CANNON_ANGLE_MIN
        else:
            if pos[1] > CANNON_ANGLE_MAX:
                pos[1] = CANNON_ANGLE_MAX
        if oldRot != pos[0] or oldAng != pos[1]:
            if self.cannonMoving == 0:
                self.cannonMoving = 1
                base.playSfx(self.sndCannonMove, looping=1)
            self.__updateCannonPosition(self.localAvId)
            if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ:
                task.lastPositionBroadcastTime = task.time
                self.__broadcastLocalCannonPosition()
        else:
            if self.cannonMoving:
                self.cannonMoving = 0
                self.sndCannonMove.stop()
                self.__broadcastLocalCannonPosition()
        return Task.cont

    def __broadcastLocalCannonPosition(self):
        self.sendUpdate('setCannonPosition',
                        [self.cannonPosition[0], self.cannonPosition[1]])

    def __updateCannonPosition(self, avId):
        self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0)
        self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0)
        maxP = 90
        newP = self.barrel.getP()
        yScale = 1 - 0.5 * float(newP) / maxP
        shadow = self.cannon.find('**/square_drop_shadow')
        shadow.setScale(1, yScale, 1)

    def __createToonModels(self):
        self.model_Created = 1
        self.jurorToon = NPCToons.createLocalNPC(
            ToontownGlobals.LawbotBossBaseJurorNpcId + self.index)
        self.toonScale = self.jurorToon.getScale()
        jurorToonParent = render.attachNewNode('toonOriginChange')
        self.jurorToon.wrtReparentTo(jurorToonParent)
        self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0,
                                 -90, 0)
        self.toonModel = jurorToonParent
        self.toonHead = ToonHead.ToonHead()
        self.toonHead.setupHead(self.jurorToon.style)
        self.toonHead.reparentTo(hidden)
        self.__loadToonInCannon()

    def __destroyToonModels(self):
        if self.toonHead != None:
            self.toonHead.reparentTo(hidden)
            self.toonHead.stopBlink()
            self.toonHead.stopLookAroundNow()
            self.toonHead = None
        if self.toonModel != None:
            self.toonModel.removeNode()
            self.toonModel = None
        if self.jurorToon != None:
            self.jurorToon.delete()
            self.jurorToon = None
        self.model_Created = 0
        return

    def __loadToonInCannon(self):
        self.toonModel.reparentTo(hidden)
        self.toonHead.startBlink()
        self.toonHead.startLookAround()
        self.toonHead.reparentTo(self.barrel)
        self.toonHead.setPosHpr(0, 6, 0, 0, -45, 0)
        sc = self.toonScale
        self.toonHead.setScale(render, sc[0], sc[1], sc[2])

    def exitCannon(self, avId):
        self.__unmakeGui()
        if self.avId == avId:
            self.av.reparentTo(render)
            self.__resetToonToCannon(self.av)

    def __resetToonToCannon(self, avatar):
        pos = None
        if not avatar:
            if self.avId:
                avatar = base.cr.doId2do.get(self.avId, None)
        if avatar:
            if hasattr(self, 'cannon') and self.cannon:
                avatar.reparentTo(self.cannon)
                avatar.setPosHpr(3, 0, 0, 90, 0, 0)
                avatar.wrtReparentTo(render)
            self.__resetToon(avatar)
        return

    def __resetToon(self, avatar, pos=None):
        if avatar:
            self.__stopCollisionHandler(avatar)
            self.__setToonUpright(avatar, pos)
            if self.localToonShooting:
                self.notify.debug('toon setting position to %s' % pos)
                if pos:
                    base.localAvatar.setPos(pos)
                camera.reparentTo(avatar)
                camera.setPos(self.av.cameraPositions[0][0])
                place = base.cr.playGame.getPlace()
                if place:
                    place.setState('finalBattle')
            self.b_setLanded()

    def __stopCollisionHandler(self, avatar):
        if avatar:
            avatar.loop('neutral')
            if self.flyColNode:
                self.flyColNode = None
            if avatar == base.localAvatar:
                avatar.collisionsOn()
            self.flyColSphere = None
            if self.flyColNodePath:
                base.cTrav.removeCollider(self.flyColNodePath)
                self.flyColNodePath.removeNode()
                self.flyColNodePath = None
            self.handler = None
        return

    def __setToonUpright(self, avatar, pos=None):
        if avatar:
            if not pos:
                pos = avatar.getPos(render)
            avatar.setPos(render, pos)
            avatar.loop('neutral')

    def b_setLanded(self):
        self.d_setLanded()

    def d_setLanded(self):
        if self.localToonShooting:
            self.sendUpdate('setLanded', [])

    def setLanded(self):
        self.removeAvFromCannon()

    def removeAvFromCannon(self):
        if self.av != None:
            self.__stopCollisionHandler(self.av)
            self.av.resetLOD()
            place = base.cr.playGame.getPlace()
            if self.av == base.localAvatar:
                if place:
                    place.setState('finalBattle')
            self.av.loop('neutral')
            self.av.setPlayRate(1.0, 'run')
            if self.av.getParent().getName() == 'toonOriginChange':
                self.av.wrtReparentTo(render)
                self.__setToonUpright(self.av)
            if self.av == base.localAvatar:
                self.av.startPosHprBroadcast()
            self.av.startSmooth()
            self.av.setScale(1, 1, 1)
            self.ignore(self.av.uniqueName('disable'))
            self.__destroyToonModels()
        return

    def setCannonWillFire(self, avId, fireTime, zRot, angle, timestamp):
        self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' +
                          str(zRot) + ', angle=' + str(angle) + ', time=' +
                          str(fireTime))
        if not self.model_Created:
            self.notify.warning(
                "We walked into the zone mid-flight, so we won't see it")
            return
        self.cannonPosition[0] = zRot
        self.cannonPosition[1] = angle
        self.__updateCannonPosition(avId)
        task = Task(self.__fireCannonTask)
        task.avId = avId
        ts = globalClockDelta.localElapsedTime(timestamp)
        task.fireTime = fireTime - ts
        if task.fireTime < 0.0:
            task.fireTime = 0.0
        taskMgr.add(task, self.taskName('fireCannon'))

    def __fireCannonTask(self, task):
        launchTime = task.fireTime
        avId = task.avId
        if self.toonHead == None or not self.boss.state == 'BattleTwo':
            return Task.done
        startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat = self.__calcFlightResults(
            avId, launchTime)
        self.notify.debug('start position: ' + str(startPos))
        self.notify.debug('start velocity: ' + str(startVel))
        self.notify.debug('time of launch: ' + str(launchTime))
        self.notify.debug('time of impact: ' + str(timeOfImpact))
        self.notify.debug('location of impact: ' +
                          str(trajectory.getPos(timeOfImpact)))
        head = self.toonHead
        head.stopBlink()
        head.stopLookAroundNow()
        head.reparentTo(hidden)
        juror = self.toonModel
        juror.reparentTo(render)
        juror.setPos(startPos)
        barrelHpr = self.barrel.getHpr(render)
        juror.setHpr(startHpr)
        self.jurorToon.loop('swim')
        self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0,
                                 0, 0)
        info = {}
        info['avId'] = avId
        info['trajectory'] = trajectory
        info['launchTime'] = launchTime
        info['timeOfImpact'] = timeOfImpact
        info['hitWhat'] = hitWhat
        info['toon'] = self.toonModel
        info['hRot'] = self.cannonPosition[0]
        info['haveWhistled'] = 0
        info['maxCamPullback'] = CAMERA_PULLBACK_MIN
        if self.localToonShooting:
            camera.reparentTo(juror)
            camera.setP(45.0)
            camera.setZ(-10.0)
        self.flyColSphere = CollisionSphere(0, 0,
                                            self.av.getHeight() / 2.0, 1.0)
        self.flyColNode = CollisionNode(self.uniqueName('flySphere'))
        self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask
                                       | ToontownGlobals.FloorBitmask
                                       | ToontownGlobals.PieBitmask)
        self.flyColNode.addSolid(self.flyColSphere)
        self.flyColNodePath = self.jurorToon.attachNewNode(self.flyColNode)
        self.flyColNodePath.setColor(1, 0, 0, 1)
        self.handler = CollisionHandlerEvent()
        self.handler.setInPattern(self.uniqueName('cannonHit'))
        base.cTrav.addCollider(self.flyColNodePath, self.handler)
        self.accept(self.uniqueName('cannonHit'), self.__handleCannonHit)
        shootTask = Task(self.__shootTask, self.taskName('shootTask'))
        flyTask = Task(self.__flyTask, self.taskName('flyTask'))
        shootTask.info = info
        flyTask.info = info
        seqTask = Task.sequence(shootTask, flyTask)
        taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(avId))
        self.acceptOnce(self.uniqueName('stopFlyTask'), self.__stopFlyTask)
        return Task.done

    def __toRadians(self, angle):
        return angle * 2.0 * math.pi / 360.0

    def __toDegrees(self, angle):
        return angle * 360.0 / (2.0 * math.pi)

    def __calcFlightResults(self, avId, launchTime):
        head = self.toonHead
        startPos = head.getPos(render)
        startHpr = head.getHpr(render)
        hpr = self.barrel.getHpr(render)
        rotation = self.__toRadians(hpr[0])
        angle = self.__toRadians(hpr[1])
        horizVel = INITIAL_VELOCITY * math.cos(angle)
        xVel = horizVel * -math.sin(rotation)
        yVel = horizVel * math.cos(rotation)
        zVel = INITIAL_VELOCITY * math.sin(angle)
        startVel = Vec3(xVel, yVel, zVel)
        trajectory = Trajectory.Trajectory(launchTime, startPos, startVel)
        self.trajectory = trajectory
        timeOfImpact, hitWhat = self.__calcToonImpact(trajectory)
        return (startPos, startHpr, startVel, trajectory, 3 * timeOfImpact,
                hitWhat)

    def __calcToonImpact(self, trajectory):
        t_groundImpact = trajectory.checkCollisionWithGround(GROUND_PLANE_MIN)
        if t_groundImpact >= trajectory.getStartTime():
            return (t_groundImpact, self.HIT_GROUND)
        self.notify.error('__calcToonImpact: toon never impacts ground?')
        return (0.0, self.HIT_GROUND)

    def __handleCannonHit(self, collisionEntry):
        if self.av == None or self.flyColNode == None:
            return
        interPt = collisionEntry.getSurfacePoint(render)
        hitNode = collisionEntry.getIntoNode().getName()
        fromNodePath = collisionEntry.getFromNodePath()
        intoNodePath = collisionEntry.getIntoNodePath()
        ignoredHits = ['NearBoss']
        for nodeName in ignoredHits:
            if hitNode == nodeName:
                return

        self.__stopFlyTask(self.avId)
        self.__stopCollisionHandler(self.jurorToon)
        if self.localToonShooting:
            camera.wrtReparentTo(render)
        pos = interPt
        hpr = self.jurorToon.getHpr()
        track = Sequence()
        if self.localToonShooting:
            pass
        chairlist = ['trigger-chair']
        for index in range(len(ToontownGlobals.LawbotBossChairPosHprs)):
            chairlist.append('Chair-%s' % index)

        if hitNode in chairlist:
            track.append(Func(self.__hitChair, self.jurorToon, pos))
            track.append(Wait(1.0))
            track.append(Func(self.__setToonUpright, self.av))
            if self.av == base.localAvatar:
                strs = hitNode.split('-')
                chairNum = int(strs[1])
                self.boss.sendUpdate('hitChair', [chairNum, self.index])
        else:
            track.append(Func(self.__hitGround, self.jurorToon, pos))
            track.append(Wait(1.0))
            track.append(Func(self.__setToonUpright, self.av))
        track.append(Func(self.b_setLanded))
        if self.localToonShooting:
            pass
        if self.hitTrack:
            self.hitTrack.finish()
        self.hitTrack = track
        self.hitTrack.start()
        return

    def enterCannonHit(self, collisionEntry):
        pass

    def __shootTask(self, task):
        base.playSfx(self.sndCannonFire)
        return Task.done

    def __flyTask(self, task):
        toon = task.info['toon']
        if toon.isEmpty():
            self.__resetToonToCannon(self.av)
            return Task.done
        curTime = task.time + task.info['launchTime']
        t = min(curTime, task.info['timeOfImpact'])
        self.lastT = self.t
        self.t = t
        deltaT = self.t - self.lastT
        self.deltaT = deltaT
        if t >= task.info['timeOfImpact']:
            self.__resetToonToCannon(self.av)
            return Task.done
        pos = task.info['trajectory'].getPos(t)
        toon.setFluidPos(pos)
        vel = task.info['trajectory'].getVel(t)
        run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1])
        rise = vel[2]
        theta = self.__toDegrees(math.atan(rise / run))
        toon.setHpr(self.cannon.getH(render), -90 + theta, 0)
        view = 2
        lookAt = task.info['toon'].getPos(render)
        hpr = task.info['toon'].getHpr(render)
        if self.localToonShooting:
            if view == 0:
                camera.wrtReparentTo(render)
                camera.lookAt(lookAt)
            elif view == 1:
                camera.reparentTo(render)
                camera.setPos(render, 100, 100, 35.25)
                camera.lookAt(render, lookAt)
            elif view == 2:
                if hpr[1] > -90:
                    camera.setPos(0, 0, -30)
                    if camera.getZ() < lookAt[2]:
                        camera.setZ(render, lookAt[2] + 10)
                    camera.lookAt(Point3(0, 0, 0))
        return Task.cont

    def __stopFlyTask(self, avId):
        taskMgr.remove(self.taskName('flyingToon') + '-' + str(avId))

    def __hitGround(self, avatar, pos, extraArgs=[]):
        hitP = avatar.getPos(render)
        h = self.barrel.getH(render)
        avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0)
        avatar.setHpr(h, -135, 0)
        self.dustCloud.setPos(render, pos[0], pos[1],
                              pos[2] + avatar.getHeight() / 3.0)
        self.dustCloud.setScale(0.35)
        self.dustCloud.play()
        base.playSfx(self.sndHitGround)
        avatar.hide()

    def __hitChair(self, avatar, pos, extraArgs=[]):
        hitP = avatar.getPos(render)
        h = self.barrel.getH(render)
        avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0)
        avatar.setHpr(h, -135, 0)
        self.dustCloud.setPos(render, pos[0], pos[1],
                              pos[2] + avatar.getHeight() / 3.0)
        self.dustCloud.setScale(0.35)
        self.dustCloud.play()
        base.playSfx(self.sndHitGround)
        base.playSfx(self.sndHitChair)
        avatar.hide()

    def generateCannonAppearTrack(self, avatar):
        self.cannon.setScale(0.1)
        self.cannon.show()
        kartTrack = Parallel(
            Sequence(ActorInterval(avatar, 'feedPet'),
                     Func(avatar.loop, 'neutral')),
            Sequence(
                Func(self.cannon.reparentTo, avatar.rightHand), Wait(2.1),
                Func(self.cannon.wrtReparentTo, render),
                Func(self.cannon.setShear, 0, 0, 0),
                Parallel(
                    LerpHprInterval(self.cannon,
                                    hpr=self.nodePath.getHpr(render),
                                    duration=1.2),
                    ProjectileInterval(self.cannon,
                                       endPos=self.nodePath.getPos(render),
                                       duration=1.2,
                                       gravityMult=0.45)), Wait(0.2),
                Sequence(
                    LerpScaleInterval(self.cannon,
                                      scale=Point3(1.1, 1.1, 0.1),
                                      duration=0.2),
                    LerpScaleInterval(self.cannon,
                                      scale=Point3(0.9, 0.9, 0.1),
                                      duration=0.1),
                    LerpScaleInterval(self.cannon,
                                      scale=Point3(1.0, 1.0, 0.1),
                                      duration=0.1),
                    LerpScaleInterval(self.cannon,
                                      scale=Point3(1.0, 1.0, 1.1),
                                      duration=0.2),
                    LerpScaleInterval(self.cannon,
                                      scale=Point3(1.0, 1.0, 0.9),
                                      duration=0.1),
                    LerpScaleInterval(self.cannon,
                                      scale=Point3(1.0, 1.0, 1.0),
                                      duration=0.1),
                    Func(self.cannon.wrtReparentTo, self.nodePath))))
        return kartTrack
Example #38
0
class CameraShyFirstPerson(FirstPerson):
    toonInFocusColor = VBase4(0.25, 1.0, 0.25, 1.0)
    toonOutOfFocusColor = VBase4(1.0, 1.0, 1.0, 1.0)
    fullyChargedState = 5

    def __init__(self, mg):
        self.mg = mg
        self.cameraFocus = None
        self.batteryFrame = None
        self.batteryBg = None
        self.batteryBar = None
        self.rechargeSound = None
        self.fullyChargedSound = None
        self.hasToonInFocus = False
        self.toonToTakePicOf = None
        self.cameraRechargeState = None
        self.cameraRechargingLabel = None
        self.cameraFlashSeq = None
        self.camFSM = ClassicFSM('CameraFSM', [State('off', self.enterOff, self.exitOff), State('ready', self.enterCameraReady, self.exitCameraReady), State('recharge', self.enterCameraRecharge, self.exitCameraRecharge)], 'off', 'off')
        self.camFSM.enterInitialState()
        FirstPerson.__init__(self)
        return

    def movementTask(self, task):
        if not inputState.isSet('jump') and not base.localAvatar.walkControls.isAirborne and inputState.isSet('forward') or inputState.isSet('reverse') or inputState.isSet('slideLeft') or inputState.isSet('slideRight'):
            if base.localAvatar.getAnimState() != 'run':
                base.localAvatar.setAnimState('run')
                base.localAvatar.playMovementSfx('run')
                self.mg.sendUpdate('runningAvatar', [base.localAvatar.doId])
        elif inputState.isSet('jump') or base.localAvatar.walkControls.isAirborne:
            if base.localAvatar.getAnimState() != 'jump':
                base.localAvatar.setAnimState('jump')
                base.localAvatar.playMovementSfx(None)
                self.mg.sendUpdate('jumpingAvatar', [base.localAvatar.doId])
        elif base.localAvatar.getAnimState() != 'neutral':
            base.localAvatar.setAnimState('neutral')
            base.localAvatar.playMovementSfx(None)
            self.mg.sendUpdate('standingAvatar', [base.localAvatar.doId])
        return task.cont

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterCameraReady(self):
        self.acceptOnce('mouse1', self.__mouse1Pressed)

    def stopCameraFlash(self):
        if self.cameraFlashSeq:
            self.cameraFlashSeq.finish()
            self.cameraFlashSeq = None
        return

    def __mouse1Pressed(self):
        self.cameraFlashSeq = Sequence(Func(base.transitions.setFadeColor, 1, 1, 1), Func(base.transitions.fadeOut, 0.1), Wait(0.1), Func(base.transitions.fadeIn, 0.1), Wait(0.1), Func(base.transitions.setFadeColor, 0, 0, 0))
        self.cameraFlashSeq.start()
        self.mg.sendUpdate('remoteAvatarTakePicture', [base.localAvatar.doId])
        self.mg.myRemoteAvatar.takePicture()
        if self.hasToonInFocus and self.toonToTakePicOf:
            self.mg.sendUpdate('tookPictureOfToon', [self.toonToTakePicOf.doId])
        self.camFSM.request('recharge')

    def exitCameraReady(self):
        self.ignore('mouse1')

    def enterCameraRecharge(self):
        self.batteryBar.update(0)
        taskMgr.add(self.__rechargeNextState, 'rechargeCamera')

    def __rechargeNextState(self, task):
        if self.cameraRechargeState == None:
            self.cameraRechargeState = -1
        self.cameraRechargeState += 1
        if self.cameraRechargeState > 0:
            base.playSfx(self.rechargeSound)
        self.batteryBar.update(self.cameraRechargeState)
        if self.cameraRechargeState == self.fullyChargedState:
            base.playSfx(self.fullyChargedSound)
            self.camFSM.request('ready')
            return task.done
        else:
            task.delayTime = 1.0
            return task.again

    def exitCameraRecharge(self):
        taskMgr.remove('rechargeCamera')
        self.cameraRechargeState = None
        return

    def __handleRayInto(self, entry):
        intoNP = entry.getIntoNodePath()
        toonNP = intoNP.getParent()
        for key in base.cr.doId2do.keys():
            obj = base.cr.doId2do[key]
            if obj.__class__.__name__ == 'DistributedToon':
                if obj.getKey() == toonNP.getKey():
                    self.__handleToonInFocus(obj)

    def __handleRayOut(self, entry):
        intoNP = entry.getIntoNodePath()
        toonNP = intoNP.getParent()
        for key in base.cr.doId2do.keys():
            obj = base.cr.doId2do[key]
            if obj.__class__.__name__ == 'DistributedToon':
                if obj.getKey() == toonNP.getKey():
                    self.toonToTakePicOf = None
                    self.hasToonInFocus = False
                    if self.cameraFocus.getColorScale() == self.toonInFocusColor:
                        self.cameraFocus.setColorScale(self.toonOutOfFocusColor)

        return

    def __handleToonInFocus(self, toon):
        if not self.hasToonInFocus or self.toonToTakePicOf is not None or self.toonToTakePicOf.doId != toon.doId:
            self.toonToTakePicOf = toon
            self.hasToonInFocus = True
            self.cameraFocus.setColorScale(self.toonInFocusColor)
        return

    def start(self):
        self.fullyChargedSound = base.loadSfx('phase_4/audio/sfx/MG_pairing_match.mp3')
        self.rechargeSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_blue_arrow.mp3')
        self.batteryFrame = DirectFrame(parent=base.a2dBottomRight, pos=(-0.2, 0, 0.1), scale=(0.8, 0, 1))
        self.batteryBg = OnscreenImage(image='phase_4/maps/battery_charge_frame.png', parent=self.batteryFrame)
        self.batteryBg.setTransparency(1)
        self.batteryBg.setX(0.03)
        self.batteryBg.setScale(0.17, 0, 0.05)
        self.batteryBar = DirectWaitBar(value=0, range=5, barColor=(1, 1, 1, 1), relief=None, scale=(0.12, 0.0, 0.3), parent=self.batteryFrame)
        self.cameraFocus = loader.loadModel('phase_4/models/minigames/photo_game_viewfinder.bam')
        self.cameraFocus.reparentTo(base.aspect2d)
        self.focusCollHandler = CollisionHandlerEvent()
        self.focusCollHandler.setInPattern('%fn-into')
        self.focusCollHandler.setOutPattern('%fn-out')
        self.focusCollNode = CollisionNode('mouseRay')
        self.focusCollNP = base.camera.attachNewNode(self.focusCollNode)
        self.focusCollNode.setCollideMask(BitMask32(0))
        self.focusCollNode.setFromCollideMask(CIGlobals.WallBitmask)
        self.focusRay = CollisionRay()
        self.focusRay.setFromLens(base.camNode, 0.0, 0.0)
        self.focusCollNode.addSolid(self.focusRay)
        base.cTrav.addCollider(self.focusCollNP, self.focusCollHandler)
        base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed, 0.0, CIGlobals.ToonReverseSpeed, CIGlobals.ToonRotateSpeed)
        FirstPerson.start(self)
        return

    def reallyStart(self):
        self.accept('mouseRay-into', self.__handleRayInto)
        self.accept('mouseRay-out', self.__handleRayOut)
        self.camFSM.request('recharge')
        taskMgr.add(self.movementTask, 'movementTask')
        FirstPerson.reallyStart(self)

    def end(self):
        self.camFSM.request('off')
        taskMgr.remove('movementTask')
        self.ignore('mouseRay-into')
        self.ignore('mouseRay-out')
        FirstPerson.end(self)

    def reallyEnd(self):
        self.batteryBar.destroy()
        self.batteryBar = None
        self.batteryBg.destroy()
        self.batteryBg = None
        self.batteryFrame.destroy()
        self.batteryFrame = None
        self.cameraFocus.removeNode()
        self.cameraFocus = None
        self.focusCollHandler = None
        self.focusCollNode = None
        self.focusCollNP.removeNode()
        self.focusCollNP = None
        self.focusRay = None
        self.hasToonInFocus = None
        self.toonToTakePicOf = None
        self.fullyChargedSound = None
        self.rechargeSound = None
        self.stopCameraFlash()
        FirstPerson.reallyEnd(self)
        base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed, CIGlobals.ToonJumpForce, CIGlobals.ToonReverseSpeed, CIGlobals.ToonRotateSpeed)
        return

    def cleanup(self):
        self.camFSM.requestFinalState()
        self.camFSM = None
        FirstPerson.cleanup(self)
        return
Example #39
0
class MyApp(ShowBase, DirectObject.DirectObject):
	def __init__(self):
		ShowBase.__init__(self)

		# generate a new game
		game = Game()
		game.create_player('Player One')
		game.create_player('Player Two')
		game.create_player('Player Three')

		game.initialize_board()

		# place some random cities
		for player in game.players.values():
			# give the player some random resources
			for resource in player.resources:
				player.resources[resource] = random.randint(0,8)
			while True:
				n = random.choice(game.board.network.nodes())
				if game.board.node_available(n):
					game.board.update_building(n, player, 'city')

					# place a random road
					m = random.choice(game.board.network.neighbors(n))
					game.board.network.edge[n][m]['road'] = True
					game.board.network.edge[n][m]['player'] = player
					break

		self.board_renderer = BoardRenderer(self, game.board)
		self.hand_renderer = HandRenderer(self, game.players.values()[0])

		# setup some 3-point lighting for the whole board
		lKey = DirectionalLight('lKey')
		lKey.setColor(VBase4(0.9,0.9,0.9,1))
		lKeyNode = render.attachNewNode(lKey)
		lKeyNode.setH(-63)
		lKeyNode.setP(-60)
		lKeyNode.setR(-30)
		render.setLight(lKeyNode)

		lFill = DirectionalLight('lFill')
		lFill.setColor(VBase4(0.4,0.4,0.4,1))
		lFillNode = render.attachNewNode(lFill)
		lFillNode.setH(27)
		lFillNode.setP(-15)
		lFillNode.setR(-30)
		render.setLight(lFillNode)

		lBack = DirectionalLight('lBack')
		lBack.setColor(VBase4(0.3,0.3,0.3,1))
		lBackNode = render.attachNewNode(lBack)
		lBackNode.setH(177)
		lBackNode.setP(-20)
		lBackNode.setR(0)
		render.setLight(lBackNode)

		lBelow = DirectionalLight('lBelow')
		lBelow.setColor(VBase4(0.4,0.4,0.4,1))
		lBelowNode = render.attachNewNode(lBelow)
		lBelowNode.setH(0)
		lBelowNode.setP(90)
		lBelowNode.setR(0)
		render.setLight(lBelowNode)

		self.accept('a', self.on_toggle_anti_alias)
		self.mouse_controlled = True
		self.on_toggle_mouse_control()
		self.accept('m', self.on_toggle_mouse_control)
		self.accept('q', self.on_quit)

		# onto-board selection collision test
		select_mask = BitMask32(0x100)
		self.select_ray = CollisionRay()
		select_node = CollisionNode('mouseToSurfaceRay')
		select_node.setFromCollideMask(select_mask)
		select_node.addSolid(self.select_ray)
		select_np = self.camera.attachNewNode(select_node)

		self.select_queue = CollisionHandlerQueue()
		self.select_traverser = CollisionTraverser()
		self.select_traverser.addCollider(select_np, self.select_queue)

		# create a plane that only collides with the mouse ray
		select_plane = CollisionPlane(Plane(Vec3(0,0,1), Point3(0,0,0)))

		# add plane to render
		self.select_node = CollisionNode('boardCollisionPlane')
		self.select_node.setCollideMask(select_mask)
		self.select_node.addSolid(select_plane)
		self.select_plane_np = self.render.attachNewNode(self.select_node)

		self.debug_select = draw_debugging_arrow(self, Vec3(0,0,0), Vec3(0,1,0))

		self.taskMgr.add(self.update_mouse_target, "mouseTarget")
		self.taskMgr.add(self.update_debug_arrow, "updateDebugArrow")

	def on_toggle_anti_alias(self):
		if AntialiasAttrib.MNone != render.getAntialias():
			render.setAntialias(AntialiasAttrib.MNone)
			print "anti-aliasing disabled"
		else:
			render.setAntialias(AntialiasAttrib.MAuto)
			print "anti-aliasing enabled"

	def on_toggle_mouse_control(self):
		if self.mouse_controlled:
			self.disableMouse()
			self.taskMgr.add(self.spin_camera_task, "spinCameraTask")
		else: self.enableMouse()

		self.mouse_controlled = not self.mouse_controlled

	def spin_camera_task(self, task):
		height = 9
		distance = 15
		speed = 1./16
		angle = (task.time*speed) * 2 * pi

		self.camera.setPos(distance*cos(angle), distance*-sin(angle), height)
		self.camera.lookAt(0,0,0)

		if self.mouse_controlled: return Task.done
		return Task.cont

	def update_mouse_target(self, task):
		if not base.mouseWatcherNode.hasMouse():
			self.mouse_target = None
			return Task.cont

		# setup ray through camera position and mouse position (on camera plane)
		mouse_pos = base.mouseWatcherNode.getMouse()
		self.select_ray.setFromLens(self.board_renderer.base.camNode, mouse_pos.getX(), mouse_pos.getY())

		self.select_traverser.traverse(self.board_renderer.base.render)

		# abort if there's no collision
		if not self.select_queue.getNumEntries(): return Task.cont

		collision = self.select_queue.getEntry(0)
		self.mouse_board_collision = collision.getSurfacePoint(collision.getIntoNodePath())
		self.mouse_target = 'board'

		return Task.cont

	def update_debug_arrow(self, task):
		if self.mouse_target:
			self.debug_select.setPos(self.mouse_board_collision)
		return Task.cont

	def on_pick(self):
		if not self._update_pick_ray(): return

		# traverse scene graph and determine nearest selection (if pickable)
		self.pick_traverser.traverse(self.board_renderer.base.render)
		self.pick_queue.sortEntries()
		if not self.pick_queue.getNumEntries(): return
		node = self.pick_queue.getEntry(0).getIntoNodePath().findNetTag('pickable')
		if node.isEmpty() or node.getTag('pickable') == 'False': return

		# add some color
		ts = TextureStage('ts')
		ts.setMode(TextureStage.MModulate)
		colors = list(Game.player_colors)
		colors.remove('white')
		node.setTexture(ts, self.board_renderer.tileset.load_texture('textures/player%s.png' % random.choice(colors).capitalize()))

	def on_quit(self):
		sys.exit(0)
class DistributedPartyGate(DistributedObject.DistributedObject):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedPartyGate')

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.publicPartyChooseGuiDoneEvent = 'doneChoosingPublicParty'
        self.publicPartyGui = PublicPartyGui(
            self.publicPartyChooseGuiDoneEvent)
        self.publicPartyGui.stash()
        self.loadClockSounds()
        self.hourSoundInterval = Sequence()
        self.accept('stoppedAsleep', self.handleSleep)

    def loadClockSounds(self):
        self.clockSounds = []
        for i in range(1, 13):
            if i < 10:
                si = '0%d' % i
            else:
                si = '%d' % i
            self.clockSounds.append(
                base.loadSfx('phase_4/audio/sfx/clock%s.mp3' % si))

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        loader = self.cr.playGame.hood.loader
        partyGate = loader.geom.find('**/partyGate_grp')
        if partyGate.isEmpty():
            self.notify.warning('Could not find partyGate_grp in loader.geom')
            return
        self.clockFlat = partyGate.find('**/clock_flat')
        collSphere = CollisionSphere(0, 0, 0, 6.9)
        collSphere.setTangible(1)
        self.partyGateSphere = CollisionNode('PartyGateSphere')
        self.partyGateSphere.addSolid(collSphere)
        self.partyGateCollNodePath = partyGate.find(
            '**/partyGate_stepsLocator').attachNewNode(self.partyGateSphere)
        self.__enableCollisions()
        self.toontownTimeGui = ServerTimeGui(partyGate,
                                             hourCallback=self.hourChange)
        self.toontownTimeGui.setPos(
            partyGate.find('**/clockText_locator').getPos() +
            Point3(0.0, 0.0, -0.2))
        self.toontownTimeGui.setHpr(
            partyGate.find('**/clockText_locator').getHpr())
        self.toontownTimeGui.setScale(12.0, 1.0, 26.0)
        self.toontownTimeGui.amLabel.setPos(-0.035, 0, -0.032)
        self.toontownTimeGui.amLabel.setScale(0.5)
        self.toontownTimeGui.updateTime()
        self.setupSignText()

    def setupSignText(self):
        loader = self.cr.playGame.hood.loader
        partyGate = loader.geom.find('**/partyGateSignGroup')
        if partyGate.isEmpty():
            self.notify.warning('Could not find partyGate_grp in loader.geom')
            return
        gateFont = ToontownGlobals.getMinnieFont()
        leftSign = partyGate.find('**/signTextL_locatorBack')
        signScale = 0.35
        wordWrap = 8
        leftText = DirectLabel.DirectLabel(parent=leftSign,
                                           pos=(0, 0.0, 0.0),
                                           relief=None,
                                           text=TTLocalizer.PartyGateLeftSign,
                                           text_align=TextNode.ACenter,
                                           text_font=gateFont,
                                           text_wordwrap=wordWrap,
                                           text_fg=Vec4(0.7, 0.3, 0.3, 1.0),
                                           scale=signScale)
        rightSign = partyGate.find('**/signTextR_locatorFront')
        rightText = DirectLabel.DirectLabel(
            parent=rightSign,
            pos=(0, 0.0, 0.0),
            relief=None,
            text=TTLocalizer.PartyGateRightSign,
            text_align=TextNode.ACenter,
            text_font=gateFont,
            text_wordwrap=wordWrap,
            text_fg=Vec4(0.7, 0.3, 0.3, 1.0),
            scale=signScale)
        return

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        if ToontownGlobals.dnaMap.has_key(self.zoneId):
            playground = ToontownGlobals.dnaMap[self.zoneId]
        else:
            playground = ToontownGlobals.dnaMap[2000]
        self.toontownTimeGui.hourLabel[
            'text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground]
        self.toontownTimeGui.colonLabel[
            'text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground]
        self.toontownTimeGui.minutesLabel[
            'text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground]
        self.toontownTimeGui.amLabel[
            'text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground]

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        self.__disableCollisions()
        self.toontownTimeGui.ival.finish()
        self.hourSoundInterval.finish()
        if self.publicPartyGui:
            self.publicPartyGui.stash()
            self.publicPartyGui.destroy()
            self.publicPartyGui = None
        return

    def delete(self):
        DistributedObject.DistributedObject.delete(self)
        self.toontownTimeGui.destroy()
        del self.toontownTimeGui
        self.hourSoundInterval.finish()
        del self.hourSoundInterval
        del self.clockFlat
        if self.publicPartyGui:
            self.publicPartyGui.destroy()
            del self.publicPartyGui
        self.partyGateCollNodePath.removeNode()
        del self.partyGateCollNodePath
        self.ignoreAll()

    def showMessage(self, message):
        self.messageDoneEvent = self.uniqueName('messageDoneEvent')
        self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone)
        self.messageGui = TTDialog.TTGlobalDialog(
            doneEvent=self.messageDoneEvent,
            message=message,
            style=TTDialog.Acknowledge)

    def __handleMessageDone(self):
        self.ignore(self.messageDoneEvent)
        self.freeAvatar()
        self.messageGui.cleanup()
        self.messageGui = None
        return

    def __handleAskDone(self):
        DistributedPartyGate.notify.debug('__handleAskDone')
        self.ignore(self.publicPartyChooseGuiDoneEvent)
        doneStatus = self.publicPartyGui.doneStatus
        self.publicPartyGui.stash()
        if doneStatus is None:
            self.freeAvatar()
            return
        self.sendUpdate('partyChoiceRequest',
                        [base.localAvatar.doId, doneStatus[0], doneStatus[1]])
        return

    def partyRequestDenied(self, reason):
        DistributedPartyGate.notify.debug(
            'partyRequestDenied( reason=%s )' %
            PartyGlobals.PartyGateDenialReasons.getString(reason))
        if reason == PartyGlobals.PartyGateDenialReasons.Unavailable:
            self.showMessage(TTLocalizer.PartyGatePartyUnavailable)
        elif reason == PartyGlobals.PartyGateDenialReasons.Full:
            self.showMessage(TTLocalizer.PartyGatePartyFull)

    def setParty(self, partyInfoTuple):
        DistributedPartyGate.notify.debug('setParty')
        self.freeAvatar()
        if partyInfoTuple[0] == 0:
            DistributedPartyGate.notify.debug(
                'Public Party closed before toon could get to it.')
            return
        shardId, zoneId, numberOfGuests, hostName, activityIds, lane = partyInfoTuple
        if base.localAvatar.defaultShard == shardId:
            shardId = None
        base.cr.playGame.getPlace().requestLeave({
            'loader': 'safeZoneLoader',
            'where': 'party',
            'how': 'teleportIn',
            'hoodId': ToontownGlobals.PartyHood,
            'zoneId': zoneId,
            'shardId': shardId,
            'avId': -1
        })
        return

    def freeAvatar(self):
        base.localAvatar.posCamera(0, 0)
        base.cr.playGame.getPlace().setState('walk')

    def hourChange(self, currentHour):
        currentHour = currentHour % 12
        if currentHour == 0:
            currentHour = 12
        self.hourSoundInterval = Parallel()
        seq1 = Sequence()
        for i in range(currentHour):
            seq1.append(SoundInterval(self.clockSounds[i]))
            seq1.append(Wait(0.2))

        timeForEachDeformation = seq1.getDuration() / currentHour
        seq2 = Sequence()
        for i in range(currentHour):
            seq2.append(
                self.clockFlat.scaleInterval(timeForEachDeformation / 2.0,
                                             Vec3(0.9, 1.0, 1.2),
                                             blendType='easeInOut'))
            seq2.append(
                self.clockFlat.scaleInterval(timeForEachDeformation / 2.0,
                                             Vec3(1.2, 1.0, 0.9),
                                             blendType='easeInOut'))

        seq2.append(
            self.clockFlat.scaleInterval(timeForEachDeformation / 2.0,
                                         Vec3(1.0, 1.0, 1.0),
                                         blendType='easeInOut'))
        self.hourSoundInterval.append(seq1)
        self.hourSoundInterval.append(seq2)
        self.hourSoundInterval.start()

    def handleEnterGateSphere(self, collEntry):
        self.notify.debug('Entering steps Sphere....')
        base.cr.playGame.getPlace().fsm.request('stopped')
        self.sendUpdate('getPartyList', [base.localAvatar.doId])

    def listAllPublicParties(self, publicPartyInfo):
        self.notify.debug('listAllPublicParties : publicPartyInfo = %s' %
                          publicPartyInfo)
        self.acceptOnce(self.publicPartyChooseGuiDoneEvent,
                        self.__handleAskDone)
        self.publicPartyGui.refresh(publicPartyInfo)
        self.publicPartyGui.unstash()

    def __enableCollisions(self):
        self.accept('enterPartyGateSphere', self.handleEnterGateSphere)
        self.partyGateSphere.setCollideMask(OTPGlobals.WallBitmask)

    def __disableCollisions(self):
        self.ignore('enterPartyGateSphere')
        self.partyGateSphere.setCollideMask(BitMask32(0))

    def handleSleep(self):
        if hasattr(self, 'messageGui') and self.messageGui:
            self.__handleMessageDone()
Example #41
0
class Avatar(Actor, ShadowCaster):
    notify = DirectNotifyGlobal.directNotify.newCategory('Avatar')

    def __init__(self, other=None):
        Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
        ShadowCaster.__init__(self)
        self.collTube = None
        self.scale = 1.0
        self.height = 0.0
        self.style = None
        self.hpText = None
        self.hpTextGenerator = TextNode('HpTextGenerator')

    def delete(self):
        try:
            self.Avatar_deleted
        except:
            Actor.cleanup(self)
            self.Avatar_deleted = 1
            self.style = None
            self.collTube = None
            self.hpText = None
            self.hpTextGenerator = None
            ShadowCaster.delete(self)
            Actor.delete(self)

    def uniqueName(self, name):
        return 'Avatar-{0}-{1}'.format(id(self), name)

    def getCollisionId(self):
        return self.uniqueName('bodyColl')

    def getAvatarScale(self):
        return self.scale

    def setAvatarScale(self, scale):
        if self.scale != scale:
            self.scale = scale
            self.getGeomNode().setScale(scale)
            self.setHeight(self.height)

    def getHeight(self):
        return self.height

    def setHeight(self, height):
        self.height = height
        if not self.collTube:
            self.initializeBodyCollisions()
        self.collTube.setPointB(0, 0, height - self.getRadius())
        if self.collNodePath:
            self.collNodePath.forceRecomputeBounds()

    def getRadius(self):
        return GameGlobals.AvatarDefaultRadius

    def getStyle(self):
        return self.style

    def setStyle(self, style):
        self.style = style

    def getAirborneHeight(self):
        height = self.getPos(self.shadowPlacer.shadowNodePath)
        return height.getZ() + 0.025

    def initializeBodyCollisions(self):
        self.collTube = CollisionTube(0, 0, 0.5, 0, 0,
                                      self.height - self.getRadius(),
                                      self.getRadius())
        self.collNode = CollisionNode(self.getCollisionId())
        self.collNode.addSolid(self.collTube)
        self.collNodePath = self.attachNewNode(self.collNode)
        self.collNode.setCollideMask(BitmaskGlobals.WallBitmask)

    def showNodePathColl(self):
        self.collNodePath.show()

    def stashBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.stash()

    def unstashBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.unstash()

    def disableBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.removeNode()
            del self.collNodePath
        self.collTube = None

    def showHpText(self, number, bonus=0, scale=1.75):
        if number == 0:
            return

        if self.hpText:
            self.hideHpText()

        self.hpTextGenerator.setFont(GameGlobals.getSignFont())

        if number < 0:
            text = str(number)

            if random.randrange(0, 100) < SILLY_SURGE_CHANCE:
                text += '\n'
                text += random.choice(GameLocalizer.SillySurgeTerms)
        else:
            text = '+' + str(number)

        self.hpTextGenerator.setText(text)
        self.hpTextGenerator.clearShadow()
        self.hpTextGenerator.setAlign(TextNode.ACenter)

        if bonus == 1:
            color = [1, 1, 0, 1]
        elif bonus == 2:
            color = [1, 0.5, 0, 1]
        elif number < 0:
            color = [0.9, 0, 0, 1]
        else:
            color = [0, 0.9, 0, 1]

        self.hpTextGenerator.setTextColor(*color)
        self.hpTextNode = self.hpTextGenerator.generate()
        self.hpText = self.attachNewNode(self.hpTextNode)
        self.hpText.setScale(scale)
        self.hpText.setBillboardPointEye()
        self.hpText.setBin('fixed', 100)

        self.hpText.setPos(0, 0, self.height / 2)

        color[3] = 0

        Sequence(
            self.hpText.posInterval(1.0, (0, 0, self.height + 0.75),
                                    blendType='easeOut'), Wait(0.85),
            self.hpText.colorInterval(0.1, Vec4(*color), 0.1),
            Func(self.hideHpText)).start()

    def hideHpText(self):
        if self.hpText:
            taskMgr.remove(self.uniqueName('hpText'))
            self.hpText.removeNode()
            self.hpText = None
class DistributedPartyTrampolineActivity(DistributedPartyActivity):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyTrampolineActivity')

    def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None):
        DistributedPartyTrampolineActivity.notify.debug('__init__')
        DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTrampoline, PartyGlobals.ActivityTypes.GuestInitiated, wantLever=False, wantRewardGui=True)
        self.doJellyBeans = doJellyBeans
        self.doTricks = doTricks
        self.texture = texture
        self.toon = None
        self.trampHeight = 3.6
        self.trampK = 400.0
        self.normalTrampB = 2.5
        self.leavingTrampB = 8.0
        self.trampB = self.normalTrampB
        self.g = -32.0
        self.jumpBoost = 330.0
        self.beginningBoost = 500.0
        self.beginningBoostThreshold = self.trampHeight + 1.5
        self.earlyJumpThreshold = 75.0
        self.boingThreshold = 300.0
        self.turnFactor = 120.0
        self.stepDT = 0.001
        self.targetCameraPos = Point3(0.0, 40.0, 10.0)
        self.cameraSpeed = 2.0
        self.hopOffPos = Point3(16.0, 0.0, 0.0)
        self.indicatorFactor = 0.0095
        self.dropShadowCutoff = 15.0
        self.minHeightForText = 15.0
        self.heightTextOffset = -0.065
        self.beanOffset = 0.5
        self.guiBeanOffset = -0.02
        self.jumpTextShown = False
        self.toonJumped = False
        self.turnLeft = False
        self.turnRight = False
        self.leavingTrampoline = False
        self.toonVelocity = 0.0
        self.topHeight = 0.0
        self.lastPeak = 0.0
        self.beginRoundInterval = None
        self.hopOnAnim = None
        self.hopOffAnim = None
        self.flashTextInterval = None
        self.numJellyBeans = PartyGlobals.TrampolineNumJellyBeans
        self.jellyBeanBonus = PartyGlobals.TrampolineJellyBeanBonus
        self.jellyBeanStartHeight = 20.0
        self.jellyBeanStopHeight = 90.0
        self.jellyBeanColors = [VBase4(1.0, 0.5, 0.5, 1.0),
         VBase4(0.5, 1.0, 0.5, 1.0),
         VBase4(0.5, 1.0, 1.0, 1.0),
         VBase4(1.0, 1.0, 0.4, 1.0),
         VBase4(0.4, 0.4, 1.0, 1.0),
         VBase4(1.0, 0.5, 1.0, 1.0)]
        delta = (self.jellyBeanStopHeight - self.jellyBeanStartHeight) / (self.numJellyBeans - 1)
        self.jellyBeanPositions = [ self.jellyBeanStartHeight + n * delta for n in xrange(self.numJellyBeans) ]
        self.doSimulateStep = False
        return

    def load(self):
        DistributedPartyTrampolineActivity.notify.debug('load')
        DistributedPartyActivity.load(self)
        self.loadModels()
        self.loadCollision()
        self.loadGUI()
        self.loadSounds()
        self.loadIntervals()
        self.activityFSM = TrampolineActivityFSM(self)
        self.activityFSM.request('Idle')
        self.animFSM = TrampolineAnimFSM(self)
        self.setBestHeightInfo('', 0)

    def loadModels(self):
        self.tramp = self.root.attachNewNode(self.uniqueName('tramp'))
        self.trampActor = Actor('phase_13/models/parties/trampoline_model', {'emptyAnim': 'phase_13/models/parties/trampoline_anim'})
        self.trampActor.reparentTo(self.tramp)
        if self.texture:
            reskinNode = self.tramp.find('**/trampoline/__Actor_modelRoot/-GeomNode')
            reskinNode.setTexture(loader.loadTexture(self.texture), 100)
        self.surface = NodePath(self.uniqueName('trampSurface'))
        self.surface.reparentTo(self.tramp)
        self.surface.setZ(self.trampHeight)
        self.trampActor.controlJoint(self.surface, 'modelRoot', 'trampoline_joint1')
        self.sign.setPos(PartyGlobals.TrampolineSignOffset)
        self.beans = [ loader.loadModelCopy('phase_4/models/props/jellybean4') for i in xrange(self.numJellyBeans) ]
        for bean in self.beans:
            bean.find('**/jellybean').setP(-35.0)
            bean.setScale(3.0)
            bean.setTransparency(True)
            bean.reparentTo(self.tramp)
            bean.stash()

        self.beans[-1].setScale(8.0)

    def loadCollision(self):
        collTube = CollisionTube(0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 5.4)
        collTube.setTangible(True)
        self.trampolineCollision = CollisionNode(self.uniqueName('TrampolineCollision'))
        self.trampolineCollision.addSolid(collTube)
        self.trampolineCollision.setCollideMask(OTPGlobals.CameraBitmask | OTPGlobals.WallBitmask)
        self.trampolineCollisionNP = self.tramp.attachNewNode(self.trampolineCollision)
        collSphere = CollisionSphere(0.0, 0.0, 0.0, 7.0)
        collSphere.setTangible(False)
        self.trampolineTrigger = CollisionNode(self.uniqueName('TrampolineTrigger'))
        self.trampolineTrigger.addSolid(collSphere)
        self.trampolineTrigger.setIntoCollideMask(OTPGlobals.WallBitmask)
        self.trampolineTriggerNP = self.tramp.attachNewNode(self.trampolineTrigger)
        self.accept('enter%s' % self.uniqueName('TrampolineTrigger'), self.onTrampolineTrigger)

    def loadGUI(self):
        self.gui = loader.loadModel('phase_13/models/parties/trampolineGUI')
        self.gui.reparentTo(base.a2dTopLeft)
        self.gui.setPos(0.115, 0, -1)
        self.gui.hide()
        self.toonIndicator = self.gui.find('**/trampolineGUI_MovingBar')
        jumpLineLocator = self.gui.find('**/jumpLine_locator')
        guiBean = self.gui.find('**/trampolineGUI_GreenJellyBean')
        self.gui.find('**/trampolineGUI_GreenJellyBean').stash()
        self.guiBeans = [ guiBean.instanceUnderNode(jumpLineLocator, self.uniqueName('guiBean%d' % i)) for i in xrange(self.numJellyBeans) ]
        self.guiBeans[-1].setScale(1.5)
        heightTextNode = TextNode(self.uniqueName('TrampolineActivity.heightTextNode'))
        heightTextNode.setFont(ToontownGlobals.getSignFont())
        heightTextNode.setAlign(TextNode.ALeft)
        heightTextNode.setText('0.0')
        heightTextNode.setShadow(0.05, 0.05)
        heightTextNode.setShadowColor(0.0, 0.0, 0.0, 1.0)
        heightTextNode.setTextColor(1.0, 1.0, 1.0, 1.0)
        self.heightText = jumpLineLocator.attachNewNode(heightTextNode)
        self.heightText.setX(0.15)
        self.heightText.setScale(0.1)
        self.heightText.setAlphaScale(0.0)
        self.quitEarlyButtonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        quitEarlyUp = self.quitEarlyButtonModels.find('**//InventoryButtonUp')
        quitEarlyDown = self.quitEarlyButtonModels.find('**/InventoryButtonDown')
        quitEarlyRollover = self.quitEarlyButtonModels.find('**/InventoryButtonRollover')
        self.quitEarlyButton = DirectButton(parent=base.a2dTopRight, relief=None, text=TTLocalizer.PartyTrampolineQuitEarlyButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.7, image=(quitEarlyUp, quitEarlyDown, quitEarlyRollover), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(-0.183, 0, -0.4), scale=0.09, command=self.leaveTrampoline)
        self.quitEarlyButton.stash()
        self.flashText = OnscreenText(text='', pos=(0.0, -0.45), scale=0.2, fg=(1.0, 1.0, 0.65, 1.0), align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True)
        self.timer = PartyUtils.getNewToontownTimer()
        self.timer.posInTopRightCorner()
        return

    def loadSounds(self):
        self.jellyBeanSound = base.loader.loadSfx('phase_4/audio/sfx/sparkly.ogg')
        self.boingSound = base.loader.loadSfx('phase_4/audio/sfx/target_trampoline_2.ogg')
        self.whistleSound = base.loader.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg')

    def loadIntervals(self):

        def prepareHeightText():
            self.heightText.node().setText(TTLocalizer.PartyTrampolineGetHeight % int(self.toon.getZ()))
            self.heightText.setZ(self.indicatorFactor * self.toon.getZ() + self.heightTextOffset)

        self.heightTextInterval = Sequence(Func(prepareHeightText), LerpFunc(self.heightText.setAlphaScale, fromData=1.0, toData=0.0, duration=1.0))

    def unload(self):
        DistributedPartyTrampolineActivity.notify.debug('unload')
        if self.hopOnAnim and self.hopOnAnim.isPlaying():
            self.hopOnAnim.finish()
        if self.hopOffAnim and self.hopOffAnim.isPlaying():
            self.hopOffAnim.finish()
        if self.beginRoundInterval and self.beginRoundInterval.isPlaying():
            self.beginRoundInterval.finish()
        if self.flashTextInterval and self.flashTextInterval.isPlaying():
            self.flashTextInterval.finish()
        if self.heightTextInterval and self.heightTextInterval.isPlaying():
            self.heightTextInterval.finish()
        self.timer.stop()
        DistributedPartyActivity.unload(self)
        taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask'))
        taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask'))
        self.ignoreAll()
        del self.heightTextInterval
        del self.beginRoundInterval
        del self.hopOnAnim
        del self.hopOffAnim
        del self.flashTextInterval
        if hasattr(self, 'beanAnims'):
            self.cleanupJellyBeans()
        self.quitEarlyButton.destroy()
        del self.quitEarlyButton
        del self.gui
        del self.activityFSM
        del self.animFSM
        return

    def setBestHeightInfo(self, toonName, height):
        self.bestHeightInfo = (toonName, height)
        DistributedPartyTrampolineActivity.notify.debug('%s has the best height of %d' % (toonName, height))
        if height > 0:
            self.setSignNote(TTLocalizer.PartyTrampolineBestHeight % self.bestHeightInfo)
        else:
            self.setSignNote(TTLocalizer.PartyTrampolineNoHeightYet)

    def leaveTrampoline(self):
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            self._showFlashMessage(TTLocalizer.PartyTrampolineTimesUp)
            self.leavingTrampoline = True
            self.timer.reset()
            self.trampB = self.leavingTrampB
            self.ignore('control')
            self.quitEarlyButton.stash()
            self.gui.hide()
        return

    def requestAnim(self, request):
        self.animFSM.request(request)

    def b_requestAnim(self, request):
        self.requestAnim(request)
        self.sendUpdate('requestAnim', [request])

    def requestAnimEcho(self, request):
        if self.toon != None and self.toon.doId != base.localAvatar.doId:
            self.requestAnim(request)
        return

    def removeBeans(self, beansToRemove):
        for i in beansToRemove:
            height, bean, guiBean, beanAnim = self.beanDetails[i]
            guiBean.stash()
            if i in self.beansToCollect:
                self.beansToCollect.remove(i)
            else:
                self.notify.warning('removeBeans avoided a crash, %d not in self.beansToCollect' % i)
            self.poofBean(bean, beanAnim)

    def b_removeBeans(self, beansToRemove):
        self.removeBeans(beansToRemove)
        self.sendUpdate('removeBeans', [beansToRemove])

    def removeBeansEcho(self, beansToRemove):
        if self.toon != None and self.toon.doId != base.localAvatar.doId:
            self.removeBeans(beansToRemove)
        return

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny)
        base.cr.playGame.getPlace().fsm.request('walk')

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny)

    def setState(self, newState, timestamp):
        DistributedPartyTrampolineActivity.notify.debug('setState( newState=%s, ... )' % newState)
        DistributedPartyActivity.setState(self, newState, timestamp)
        self.activityFSM.request(newState)

    def startIdle(self):
        DistributedPartyTrampolineActivity.notify.debug('startIdle')

    def finishIdle(self):
        DistributedPartyTrampolineActivity.notify.debug('finishIdle')

    def startRules(self):
        DistributedPartyTrampolineActivity.notify.debug('startRules')
        if self.doJellyBeans:
            self.setupJellyBeans()
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            self.acquireToon()
        return

    def startActive(self):
        DistributedPartyTrampolineActivity.notify.debug('startActive')
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            base.setCellsActive(base.bottomCells, True)
            self.accept('arrow_left', self.onLeft)
            self.accept('arrow_left-up', self.onLeftUp)
            self.accept('arrow_right', self.onRight)
            self.accept('arrow_right-up', self.onRightUp)
            self.beginRoundInterval = Sequence(Func(self._showFlashMessage, TTLocalizer.PartyTrampolineReady), Wait(1.2), Func(self.flashMessage, TTLocalizer.PartyTrampolineGo), Func(self.beginRound))
            self.beginRoundInterval.start()
        return

    def finishActive(self):
        DistributedPartyTrampolineActivity.notify.debug('finishActive')
        if self.doJellyBeans:
            self.cleanupJellyBeans()

    def setupJellyBeans(self):
        self.beanAnims = []
        self.beansToCollect = []
        self.beanDetails = []
        self.numBeansCollected = 0
        for i in xrange(self.numJellyBeans):
            bean = self.beans[i]
            guiBean = self.guiBeans[i]
            height = self.jellyBeanPositions[i]
            color = random.choice(self.jellyBeanColors)
            bean.find('**/jellybean').setColor(color)
            if self.toon.doId == base.localAvatar.doId:
                bean.setAlphaScale(1.0)
            else:
                bean.setAlphaScale(0.5)
            guiBean.setColor(color)
            bean.setZ(height + self.toon.getHeight() + self.beanOffset)
            guiBean.setZ(height * self.indicatorFactor + self.guiBeanOffset)
            bean.setH(0.0)
            bean.unstash()
            guiBean.unstash()
            beanAnim = bean.hprInterval(1.5, VBase3((i % 2 * 2 - 1) * 360.0, 0.0, 0.0))
            beanAnim.loop()
            self.beanAnims.append(beanAnim)
            self.beanDetails.append((height,
             bean,
             guiBean,
             beanAnim))

        self.beansToCollect = range(self.numJellyBeans)

    def cleanupJellyBeans(self):
        for bean in self.beans:
            bean.stash()

        for guiBean in self.guiBeans:
            guiBean.stash()

        if hasattr(self, 'beanAnims'):
            for beanAnim in self.beanAnims:
                beanAnim.finish()

            del self.beanAnims
            del self.beansToCollect

    def beginRound(self):
        base.playSfx(self.whistleSound)
        self.timer.setTime(PartyGlobals.TrampolineDuration)
        self.timer.countdown(PartyGlobals.TrampolineDuration)
        self.timer.show()
        self.gui.show()
        self.quitEarlyButton.unstash()
        self.notify.debug('Accepting contorl')
        self.accept('control', self.onJump)
        self.notify.debug('setting simulate step to true')
        self.doSimulateStep = True

    def acquireToon(self):
        self.toon.disableSmartCameraViews()
        self.toon.stopUpdateSmartCamera()
        camera.wrtReparentTo(render)
        self.toon.dropShadow.reparentTo(hidden)
        self.toon.startPosHprBroadcast(period=0.2)
        self.toonAcceleration = 0.0
        self.toonVelocity = 0.0
        self.topHeight = 0.0
        self.trampB = self.normalTrampB
        self.leavingTrampoline = False
        self.hopOnAnim = Sequence(Func(self.toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, self.toon, Point3(0.0, 0.0, self.trampHeight), 5.0, self.tramp), Func(self.postHopOn))
        self.hopOnAnim.start()

    def postHopOn(self):
        self.toon.setH(self.toon.getH() + 90.0)
        self.toon.dropShadow.reparentTo(self.surface)
        self.timeLeftToSimulate = 0.0
        self.doSimulateStep = False
        taskMgr.add(self.updateTask, self.uniqueName('TrampolineActivity.updateTask'))
        base.setCellsActive(base.leftCells, False)
        base.setCellsActive(base.bottomCells, False)
        DistributedPartyActivity.startRules(self)

    def releaseToon(self):
        self._hideFlashMessage()
        self.ignore('arrow_left')
        self.ignore('arrow_left-up')
        self.ignore('arrow_right')
        self.ignore('arrow_right-up')
        taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask'))
        self.hopOffAnim = Sequence(self.toon.hprInterval(0.5, VBase3(-90.0, 0.0, 0.0), other=self.tramp), Func(self.toon.b_setAnimState, 'jump', 1.0), Func(self.toon.dropShadow.reparentTo, hidden), Wait(0.4), PartyUtils.arcPosInterval(0.75, self.toon, self.hopOffPos, 5.0, self.tramp), Func(self.postHopOff))
        self.hopOffAnim.start()

    def postHopOff(self):
        base.setCellsActive(base.leftCells, True)
        self.timer.stop()
        self.timer.hide()
        self.toon.dropShadow.reparentTo(self.toon.getShadowJoint())
        self.toon.dropShadow.setAlphaScale(1.0)
        self.toon.dropShadow.setScale(1.0)
        self.b_requestAnim('Off')
        camera.reparentTo(base.localAvatar)
        base.localAvatar.startUpdateSmartCamera()
        base.localAvatar.enableSmartCameraViews()
        base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
        place = base.cr.playGame.getPlace()
        if self.doJellyBeans:
            self.sendUpdate('awardBeans', [self.numBeansCollected, int(self.topHeight)])
            if int(self.topHeight) > self.bestHeightInfo[1]:
                self.sendUpdate('reportHeightInformation', [int(self.topHeight)])
        self.d_toonExitDemand()

    def onTrampolineTrigger(self, collEntry):
        if self.activityFSM.state_ == 'Idle' and self.toon == None and base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'walk':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        else:
            self.flashMessage(TTLocalizer.PartyTrampolineActivityOccupied, duration=2.0)
        return

    def onJump(self):
        self.notify.debug('got onJump')
        if self.toon != None and self.toon.getZ() < self.trampHeight:
            self.toonJumped = True
            self.b_requestAnim('Jump')
        else:
            self.notify.debug('z is less than tramp height')
        return

    def onLeft(self):
        self.turnLeft = True

    def onLeftUp(self):
        self.turnLeft = False

    def onRight(self):
        self.turnRight = True

    def onRightUp(self):
        self.turnRight = False

    def handleToonJoined(self, toonId):
        DistributedPartyTrampolineActivity.notify.debug('handleToonJoined')
        self.toon = self.getAvatar(toonId)
        if self.toon != None and not self.toon.isEmpty():
            self.oldJumpSquatPlayRate = self.toon.getPlayRate('jump-squat')
            self.oldJumpLandPlayRate = self.toon.getPlayRate('jump-land')
            self.toon.setPlayRate(2.5, 'jump-squat')
            self.toon.setPlayRate(2.0, 'jump-land')
            self.turnLeft = False
            self.turnRight = False
            self.activityFSM.request('Rules')
            if self.toon.doId != base.localAvatar.doId:
                taskMgr.add(self.remoteUpdateTask, self.uniqueName('TrampolineActivity.remoteUpdateTask'))
        else:
            self.notify.warning('handleToonJoined could not get toon %d' % toonId)
        return

    def handleToonExited(self, toonId):
        DistributedPartyTrampolineActivity.notify.debug('handleToonExited')
        if self.toon != None:
            if self.toon.doId != base.localAvatar.doId:
                taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask'))
            self.surface.setZ(self.trampHeight)
            self.toon.setPlayRate(self.oldJumpSquatPlayRate, 'jump-squat')
            self.toon.setPlayRate(self.oldJumpLandPlayRate, 'jump-land')
            self.toon = None
        return

    def handleToonDisabled(self, toonId):
        DistributedPartyTrampolineActivity.notify.debug('handleToonDisabled')
        DistributedPartyTrampolineActivity.notify.debug('avatar ' + str(toonId) + ' disabled')
        if base.localAvatar.doId == toonId:
            self.releaseToon()

    def handleRulesDone(self):
        self.sendUpdate('toonReady')
        self.finishRules()

    def getTitle(self):
        if self.doJellyBeans:
            return TTLocalizer.PartyTrampolineJellyBeanTitle
        elif self.doTricks:
            return TTLocalizer.PartyTrampolineTricksTitle
        else:
            return DistributedPartyActivity.getTitle(self)

    def getInstructions(self):
        return TTLocalizer.PartyTrampolineActivityInstructions

    def updateTask(self, task):
        z = self.toon.getZ()
        dt = globalClock.getDt()
        if self.doSimulateStep:
            self.timeLeftToSimulate += dt
            while self.timeLeftToSimulate >= self.stepDT:
                z, a = self.simulateStep(z)
                self.timeLeftToSimulate -= self.stepDT

        self.toon.setZ(z)
        if z <= self.trampHeight:
            self.surface.setZ(z)
        else:
            self.surface.setZ(self.trampHeight)
        self.toonIndicator.setZ((z - self.trampHeight) * self.indicatorFactor)
        if self.turnLeft:
            self.toon.setH(self.toon.getH() + self.turnFactor * dt)
        if self.turnRight:
            self.toon.setH(self.toon.getH() - self.turnFactor * dt)
        currentPos = base.camera.getPos(self.toon)
        vec = self.targetCameraPos - currentPos
        newPos = currentPos + vec * (dt * self.cameraSpeed)
        base.camera.setPos(self.toon, newPos)
        base.camera.lookAt(self.toon)
        #if z > self.trampHeight:
        #    heightFactor = 1.0 - min(1.0, (z - self.trampHeight) / self.dropShadowCutoff)
        #    self.toon.dropShadow.setAlphaScale(heightFactor)
        #    self.toon.dropShadow.setScale(max(0.1, heightFactor))
        #else:
        #    self.toon.dropShadow.setAlphaScale(1.0)
        #    self.toon.dropShadow.setScale(1.0)
        if self.leavingTrampoline and z < self.trampHeight and abs(a) < 0.1:
            self.releaseToon()
        return Task.cont

    def simulateStep(self, z):
        if z >= self.trampHeight:
            a = self.g
            self.toonJumped = False
        else:
            a = self.g + self.trampK * (self.trampHeight - z) - self.trampB * self.toonVelocity
            if self.toonJumped:
                if self.lastPeak > self.earlyJumpThreshold or self.toonVelocity >= -300000.0:
                    a += self.jumpBoost
                    if self.lastPeak < self.beginningBoostThreshold:
                        a += self.beginningBoost
        lastVelocity = self.toonVelocity
        self.toonVelocity += a * self.stepDT
        if lastVelocity > 0.0 and self.toonVelocity <= 0.0:
            topOfJump = True
            bottomOfJump = False
        elif lastVelocity < 0.0 and self.toonVelocity >= 0.0:
            topOfJump = False
            bottomOfJump = True
        else:
            topOfJump = False
            bottomOfJump = False
        newZ = z + self.toonVelocity * self.stepDT
        if newZ > self.topHeight:
            self.topHeight = newZ
            if self.doJellyBeans:
                self.collectJellyBeans(newZ)
        if topOfJump:
            self.lastPeak = newZ
            if newZ >= self.minHeightForText:
                self.heightTextInterval.start()
        if topOfJump:
            if newZ > self.trampHeight + 20.0:
                self.b_requestAnim('Falling')
            elif self.animFSM.state_ == 'Jump':
                self.b_requestAnim('Falling')
        if newZ <= self.trampHeight and z > self.trampHeight:
            if self.animFSM.state_ == 'Falling':
                self.b_requestAnim('Land')
            elif self.animFSM.state_ != 'Neutral':
                self.b_requestAnim('Neutral')
        if bottomOfJump and a > self.boingThreshold:
            base.playSfx(self.boingSound)
        return (newZ, a)

    def collectJellyBeans(self, z):
        beansToRemove = []
        for i in self.beansToCollect:
            height = self.beanDetails[i][0]
            if height <= z:
                beansToRemove.append(i)

        if len(beansToRemove) > 0:
            base.playSfx(self.jellyBeanSound)
            self.numBeansCollected += len(beansToRemove)
            self.b_removeBeans(beansToRemove)

    def remoteUpdateTask(self, task):
        if self.toon != None and not self.toon.isEmpty():
            z = self.toon.getZ()
            if z <= self.trampHeight:
                self.surface.setZ(z)
            else:
                self.surface.setZ(self.trampHeight)
        return Task.cont

    def poofBean(self, bean, beanAnim):
        if bean == None:
            self.notify.warning('poofBean, returning immediately as bean is None')
            return
        if bean.isEmpty():
            self.notify.warning('poofBean, returning immediately as bean is empty')
            return
        currentAlpha = bean.getColorScale()[3]
        currentScale = bean.getScale()
        poofAnim = Sequence(Parallel(LerpFunc(bean.setAlphaScale, fromData=currentAlpha, toData=0.0, duration=0.25), LerpFunc(bean.setScale, fromData=currentScale, toData=currentScale * 5.0, duration=0.25)), Func(bean.stash), Func(beanAnim.finish), Func(bean.setAlphaScale, currentAlpha), Func(bean.setScale, currentScale))
        poofAnim.start()
        return

    def _showFlashMessage(self, message):
        if self.isDisabled():
            return
        if self.flashTextInterval is not None and self.flashTextInterval.isPlaying():
            self.flashTextInterval.finish()
        self.flashText.setText(message)
        self.flashText.setAlphaScale(1.0)
        self.flashText.unstash()
        return

    def _hideFlashMessage(self, duration = 0.0):
        if self.isDisabled():
            pass
        self.flashTextInterval = Sequence(Wait(duration), LerpFunc(self.flashText.setAlphaScale, fromData=1.0, toData=0.0, duration=1.0), Func(self.flashText.stash))
        self.flashTextInterval.start()

    def flashMessage(self, message, duration = 0.5):
        self._showFlashMessage(message)
        self._hideFlashMessage(duration)
Example #43
0
class DistributedLawbotCannon(DistributedObject.DistributedObject):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotCannon')
    LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask'
    FIRE_KEY = 'control'
    UP_KEY = 'arrow_up'
    DOWN_KEY = 'arrow_down'
    LEFT_KEY = 'arrow_left'
    RIGHT_KEY = 'arrow_right'
    HIT_GROUND = 0

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.index = None
        self.avId = 0
        self.av = None
        self.localToonShooting = 0
        self.cannonsActive = 0
        self.cannonLocation = None
        self.cannonPostion = None
        self.cannon = None
        self.madeGui = 0
        self.jurorToon = None
        self.toonModel = None
        self.toonHead = None
        self.toonScale = None
        self.dustCloud = None
        self.hitBumper = 0
        self.hitTarget = 0
        self.lastPos = Vec3(0, 0, 0)
        self.lastVel = Vec3(0, 0, 0)
        self.vel = Vec3(0, 0, 0)
        self.landingPos = Vec3(0, 0, 0)
        self.t = 0
        self.lastT = 0
        self.deltaT = 0
        self.hitTrack = None
        self.flyColNode = None
        self.flyColNodePath = None
        self.localAvId = base.localAvatar.doId
        self.model_Created = 0
        return

    def disable(self):
        taskMgr.remove(self.uniqueName('fireCannon'))
        taskMgr.remove(self.uniqueName('shootTask'))
        self.__stopFlyTask(self.avId)
        taskMgr.remove(self.uniqueName('flyTask'))
        self.ignoreAll()
        self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0)
        self.nodePath.detachNode()
        self.__unmakeGui()
        if self.hitTrack:
            self.hitTrack.finish()
            del self.hitTrack
            self.hitTrack = None
        DistributedObject.DistributedObject.disable(self)
        return

    def delete(self):
        self.offstage()
        self.unload()
        DistributedObject.DistributedObject.delete(self)

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        self.boss.cannons[self.index] = self

    def generateInit(self):
        DistributedObject.DistributedObject.generateInit(self)
        self.nodePath = NodePath(self.uniqueName('Cannon'))
        self.load()
        self.activateCannons()

    def setPosHpr(self, x, y, z, h, p, r):
        self.nodePath.setPosHpr(x, y, z, h, p, r)

    def setBossCogId(self, bossCogId):
        self.bossCogId = bossCogId
        self.boss = base.cr.doId2do[bossCogId]

    def getSphereRadius(self):
        return 1.5

    def getParentNodePath(self):
        return render

    def setIndex(self, index):
        self.index = index

    def load(self):
        self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon')
        self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius())
        self.dustCloud = DustCloud.DustCloud(render)
        self.dustCloud.setBillboardPointEye()
        self.collSphere.setTangible(1)
        self.collNode = CollisionNode(self.uniqueName('CannonSphere'))
        self.collNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.nodePath.attachNewNode(self.collNode)
        self.cannon.reparentTo(self.nodePath)
        self.kartColNode = CollisionNode(self.uniqueName('KartColNode'))
        self.kartNode = self.nodePath.attachNewNode(self.kartColNode)
        self.sndCannonMove = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg')
        self.sndCannonFire = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg')
        self.sndHitGround = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndHitChair = base.loader.loadSfx('phase_11/audio/sfx/LB_toon_jury.ogg')
        self.cannon.hide()
        self.flashingLabel = None
        return

    def unload(self):
        if self.cannon:
            self.cannon.removeNode()
            del self.cannon
        if self.dustCloud != None:
            self.dustCloud.destroy()
            del self.dustCloud
        del self.sndCannonMove
        del self.sndCannonFire
        del self.sndHitGround
        del self.sndHitChair
        if self.av:
            self.__resetToon(self.av)
            self.av.loop('neutral')
            self.av.setPlayRate(1.0, 'run')
        if self.toonHead != None:
            self.toonHead.stopBlink()
            self.toonHead.stopLookAroundNow()
            self.toonHead.delete()
            del self.toonHead
        if self.toonModel != None:
            self.toonModel.removeNode()
            del self.toonModel
        if self.jurorToon != None:
            self.jurorToon.delete()
            del self.jurorToon
        del self.toonScale
        return

    def activateCannons(self):
        if not self.cannonsActive:
            self.cannonsActive = 1
            self.onstage()
            self.nodePath.reparentTo(self.getParentNodePath())
            self.accept(self.uniqueName('enterCannonSphere'), self.__handleEnterSphere)

    def onstage(self):
        self.__createCannon()
        self.cannon.reparentTo(self.nodePath)
        self.dustCloud.reparentTo(render)

    def offstage(self):
        if self.cannon:
            self.cannon.reparentTo(hidden)
        if self.dustCloud:
            self.dustCloud.reparentTo(hidden)
            self.dustCloud.stop()

    def __createCannon(self):
        self.barrel = self.cannon.find('**/cannon')
        self.cannonLocation = Point3(0, 0, 0.025)
        self.cannonPosition = [0, CANNON_ANGLE_MIN]
        self.cannon.setPos(self.cannonLocation)
        self.__updateCannonPosition(self.avId)

    def updateCannonPosition(self, avId, zRot, angle):
        if avId != self.localAvId:
            self.cannonPosition = [zRot, angle]
            self.__updateCannonPosition(avId)

    def __updateCannonPosition(self, avId):
        self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0)
        self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0)
        maxP = 90
        newP = self.barrel.getP()
        yScale = 1 - 0.5 * float(newP) / maxP
        shadow = self.cannon.find('**/square_drop_shadow')
        shadow.setScale(1, yScale, 1)

    def __handleEnterSphere(self, collEntry):
        self.d_requestEnter()

    def d_requestEnter(self):
        self.sendUpdate('requestEnter', [])

    def setMovie(self, mode, avId, extraInfo):
        wasLocalToon = self.localToonShooting
        self.avId = avId
        if mode == CannonGlobals.CANNON_MOVIE_CLEAR:
            self.setLanded()
        elif mode == CannonGlobals.CANNON_MOVIE_LANDED:
            self.setLanded()
        elif mode == CannonGlobals.CANNON_MOVIE_FORCE_EXIT:
            self.exitCannon(self.avId)
            self.setLanded()
        elif mode == CannonGlobals.CANNON_MOVIE_LOAD:
            if self.avId == base.localAvatar.doId:
                self.cannonBallsLeft = extraInfo
                base.cr.playGame.getPlace().setState('crane')
                base.localAvatar.setTeleportAvailable(0)
                self.localToonShooting = 1
                self.__makeGui()
                camera.reparentTo(self.barrel)
                camera.setPos(0.5, -2, 2.5)
                camera.setHpr(0, 0, 0)
                self.boss.toonEnteredCannon(self.avId, self.index)
            if self.avId in self.cr.doId2do:
                self.av = self.cr.doId2do[self.avId]
                self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone)
                self.av.loop('neutral')
                self.av.stopSmooth()
                self.__destroyToonModels()
                self.__createToonModels()
                self.av.setPosHpr(3, 0, 0, 90, 0, 0)
                self.av.reparentTo(self.cannon)
            else:
                self.notify.warning('Unknown avatar %d in cannon %d' % (self.avId, self.doId))
        else:
            self.notify.warning('unhandled case, mode = %d' % mode)

    def __avatarGone(self):
        self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0)

    def __makeGui(self):
        if self.madeGui:
            return
        NametagGlobals.setWant2dNametags(False)
        guiModel = 'phase_4/models/gui/cannon_game_gui'
        cannonGui = loader.loadModel(guiModel)
        self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8)
        cannonGui.removeNode()
        self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed)
        self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717))
        self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180))
        self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90))
        self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90))
        guiClose = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui')
        cannonBallText = '%d/%d' % (self.cannonBallsLeft, ToontownGlobals.LawbotBossCannonBallMax)
        self.cannonBallLabel = DirectLabel(parent=self.aimPad, text=cannonBallText, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0.475, 0.0, -0.35), scale=0.25)
        if self.cannonBallsLeft < 5:
            if self.flashingLabel:
                self.flashingLabel.stop()
            flashingTrack = Sequence()
            for i in xrange(10):
                flashingTrack.append(LerpColorScaleInterval(self.cannonBallLabel, 0.5, VBase4(1, 0, 0, 1)))
                flashingTrack.append(LerpColorScaleInterval(self.cannonBallLabel, 0.5, VBase4(1, 1, 1, 1)))

            self.flashingLabel = flashingTrack
            self.flashingLabel.start()
        self.aimPad.setColor(1, 1, 1, 0.9)

        def bindButton(button, upHandler, downHandler):
            button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler())
            button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler())

        bindButton(self.upButton, self.__upPressed, self.__upReleased)
        bindButton(self.downButton, self.__downPressed, self.__downReleased)
        bindButton(self.leftButton, self.__leftPressed, self.__leftReleased)
        bindButton(self.rightButton, self.__rightPressed, self.__rightReleased)
        self.__enableAimInterface()
        self.madeGui = 1
        return

    def __unmakeGui(self):
        self.notify.debug('__unmakeGui')
        if not self.madeGui:
            return
        if self.flashingLabel:
            self.flashingLabel.finish()
            self.flashingLabel = None
        NametagGlobals.setWant2dNametags(True)
        self.__disableAimInterface()
        self.upButton.unbind(DGG.B1PRESS)
        self.upButton.unbind(DGG.B1RELEASE)
        self.downButton.unbind(DGG.B1PRESS)
        self.downButton.unbind(DGG.B1RELEASE)
        self.leftButton.unbind(DGG.B1PRESS)
        self.leftButton.unbind(DGG.B1RELEASE)
        self.rightButton.unbind(DGG.B1PRESS)
        self.rightButton.unbind(DGG.B1RELEASE)
        self.aimPad.destroy()
        del self.aimPad
        del self.fireButton
        del self.upButton
        del self.downButton
        del self.leftButton
        del self.rightButton
        self.madeGui = 0
        return

    def __enableAimInterface(self):
        self.aimPad.show()
        self.accept(self.FIRE_KEY, self.__fireKeyPressed)
        self.accept(self.UP_KEY, self.__upKeyPressed)
        self.accept(self.DOWN_KEY, self.__downKeyPressed)
        self.accept(self.LEFT_KEY, self.__leftKeyPressed)
        self.accept(self.RIGHT_KEY, self.__rightKeyPressed)
        self.__spawnLocalCannonMoveTask()

    def __disableAimInterface(self):
        self.aimPad.hide()
        self.ignore(self.FIRE_KEY)
        self.ignore(self.UP_KEY)
        self.ignore(self.DOWN_KEY)
        self.ignore(self.LEFT_KEY)
        self.ignore(self.RIGHT_KEY)
        self.ignore(self.FIRE_KEY + '-up')
        self.ignore(self.UP_KEY + '-up')
        self.ignore(self.DOWN_KEY + '-up')
        self.ignore(self.LEFT_KEY + '-up')
        self.ignore(self.RIGHT_KEY + '-up')
        self.__killLocalCannonMoveTask()

    def __fireKeyPressed(self):
        self.ignore(self.FIRE_KEY)
        self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased)
        self.__firePressed()

    def __upKeyPressed(self):
        self.ignore(self.UP_KEY)
        self.accept(self.UP_KEY + '-up', self.__upKeyReleased)
        self.__upPressed()

    def __downKeyPressed(self):
        self.ignore(self.DOWN_KEY)
        self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased)
        self.__downPressed()

    def __leftKeyPressed(self):
        self.ignore(self.LEFT_KEY)
        self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased)
        self.__leftPressed()

    def __rightKeyPressed(self):
        self.ignore(self.RIGHT_KEY)
        self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased)
        self.__rightPressed()

    def __fireKeyReleased(self):
        self.ignore(self.FIRE_KEY + '-up')
        self.accept(self.FIRE_KEY, self.__fireKeyPressed)

    def __leftKeyReleased(self):
        self.ignore(self.LEFT_KEY + '-up')
        self.accept(self.LEFT_KEY, self.__leftKeyPressed)
        self.__leftReleased()

    def __rightKeyReleased(self):
        self.ignore(self.RIGHT_KEY + '-up')
        self.accept(self.RIGHT_KEY, self.__rightKeyPressed)
        self.__rightReleased()

    def __upKeyReleased(self):
        self.ignore(self.UP_KEY + '-up')
        self.accept(self.UP_KEY, self.__upKeyPressed)
        self.__upReleased()

    def __downKeyReleased(self):
        self.ignore(self.DOWN_KEY + '-up')
        self.accept(self.DOWN_KEY, self.__downKeyPressed)
        self.__downReleased()

    def __leaveCannon(self):
        self.notify.debug('__leaveCannon')
        self.sendUpdate('requestLeave')

    def __firePressed(self):
        self.notify.debug('fire pressed')
        if not self.boss.state == 'BattleTwo':
            self.notify.debug('boss is in state=%s, not firing' % self.boss.state)
            return
        self.__broadcastLocalCannonPosition()
        self.__unmakeGui()
        self.sendUpdate('setCannonLit', [self.cannonPosition[0], self.cannonPosition[1]])

    def __upPressed(self):
        self.notify.debug('up pressed')
        self.upPressed = self.__enterControlActive(self.upPressed)

    def __downPressed(self):
        self.notify.debug('down pressed')
        self.downPressed = self.__enterControlActive(self.downPressed)

    def __leftPressed(self):
        self.notify.debug('left pressed')
        self.leftPressed = self.__enterControlActive(self.leftPressed)

    def __rightPressed(self):
        self.notify.debug('right pressed')
        self.rightPressed = self.__enterControlActive(self.rightPressed)

    def __upReleased(self):
        self.notify.debug('up released')
        self.upPressed = self.__exitControlActive(self.upPressed)

    def __downReleased(self):
        self.notify.debug('down released')
        self.downPressed = self.__exitControlActive(self.downPressed)

    def __leftReleased(self):
        self.notify.debug('left released')
        self.leftPressed = self.__exitControlActive(self.leftPressed)

    def __rightReleased(self):
        self.notify.debug('right released')
        self.rightPressed = self.__exitControlActive(self.rightPressed)

    def __enterControlActive(self, control):
        return control + 1

    def __exitControlActive(self, control):
        return max(0, control - 1)

    def __spawnLocalCannonMoveTask(self):
        self.leftPressed = 0
        self.rightPressed = 0
        self.upPressed = 0
        self.downPressed = 0
        self.cannonMoving = 0
        task = Task(self.__localCannonMoveTask)
        task.lastPositionBroadcastTime = 0.0
        taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK)

    def __killLocalCannonMoveTask(self):
        taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK)
        if self.cannonMoving:
            self.sndCannonMove.stop()

    def __localCannonMoveTask(self, task):
        pos = self.cannonPosition
        oldRot = pos[0]
        oldAng = pos[1]
        rotVel = 0
        if self.leftPressed:
            rotVel += CANNON_ROTATION_VEL
        if self.rightPressed:
            rotVel -= CANNON_ROTATION_VEL
        pos[0] += rotVel * globalClock.getDt()
        if pos[0] < CANNON_ROTATION_MIN:
            pos[0] = CANNON_ROTATION_MIN
        elif pos[0] > CANNON_ROTATION_MAX:
            pos[0] = CANNON_ROTATION_MAX
        angVel = 0
        if self.upPressed:
            angVel += CANNON_ANGLE_VEL
        if self.downPressed:
            angVel -= CANNON_ANGLE_VEL
        pos[1] += angVel * globalClock.getDt()
        if pos[1] < CANNON_ANGLE_MIN:
            pos[1] = CANNON_ANGLE_MIN
        elif pos[1] > CANNON_ANGLE_MAX:
            pos[1] = CANNON_ANGLE_MAX
        if oldRot != pos[0] or oldAng != pos[1]:
            if self.cannonMoving == 0:
                self.cannonMoving = 1
                base.playSfx(self.sndCannonMove, looping=1)
            self.__updateCannonPosition(self.localAvId)
            if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ:
                task.lastPositionBroadcastTime = task.time
                self.__broadcastLocalCannonPosition()
        elif self.cannonMoving:
            self.cannonMoving = 0
            self.sndCannonMove.stop()
            self.__broadcastLocalCannonPosition()
        return Task.cont

    def __broadcastLocalCannonPosition(self):
        self.sendUpdate('setCannonPosition', [self.cannonPosition[0], self.cannonPosition[1]])

    def __updateCannonPosition(self, avId):
        self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0)
        self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0)
        maxP = 90
        newP = self.barrel.getP()
        yScale = 1 - 0.5 * float(newP) / maxP
        shadow = self.cannon.find('**/square_drop_shadow')
        shadow.setScale(1, yScale, 1)

    def __createToonModels(self):
        self.model_Created = 1
        self.jurorToon = NPCToons.createLocalNPC(ToontownGlobals.LawbotBossBaseJurorNpcId + self.index)
        self.toonScale = self.jurorToon.getScale()
        jurorToonParent = render.attachNewNode('toonOriginChange')
        self.jurorToon.wrtReparentTo(jurorToonParent)
        self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0, -90, 0)
        self.toonModel = jurorToonParent
        self.toonHead = ToonHead.ToonHead()
        self.toonHead.setupHead(self.jurorToon.style)
        self.toonHead.reparentTo(hidden)
        self.__loadToonInCannon()

    def __destroyToonModels(self):
        if (0):
            self.av.dropShadow.show()
            if self.dropShadow != None:
                self.dropShadow.removeNode()
                self.dropShadow = None
            self.hitBumper = 0
            self.hitTarget = 0
            self.angularVel = 0
            self.vel = Vec3(0, 0, 0)
            self.lastVel = Vec3(0, 0, 0)
            self.lastPos = Vec3(0, 0, 0)
            self.landingPos = Vec3(0, 0, 0)
            self.t = 0
            self.lastT = 0
            self.deltaT = 0
            self.av = None
            self.lastWakeTime = 0
            self.localToonShooting = 0
        if self.toonHead != None:
            self.toonHead.reparentTo(hidden)
            self.toonHead.stopBlink()
            self.toonHead.stopLookAroundNow()
            self.toonHead = None
        if self.toonModel != None:
            self.toonModel.removeNode()
            self.toonModel = None
        if self.jurorToon != None:
            self.jurorToon.delete()
            self.jurorToon = None
        self.model_Created = 0
        return

    def __loadToonInCannon(self):
        self.toonModel.reparentTo(hidden)
        self.toonHead.startBlink()
        self.toonHead.startLookAround()
        self.toonHead.reparentTo(self.barrel)
        self.toonHead.setPosHpr(0, 6, 0, 0, -45, 0)
        sc = self.toonScale
        self.toonHead.setScale(render, sc[0], sc[1], sc[2])

    def exitCannon(self, avId):
        self.__unmakeGui()
        if self.avId == avId:
            self.av.reparentTo(render)
            self.__resetToonToCannon(self.av)

    def __resetToonToCannon(self, avatar):
        pos = None
        if not avatar:
            if self.avId:
                avatar = base.cr.doId2do.get(self.avId, None)
        if avatar:
            if hasattr(self, 'cannon') and self.cannon:
                avatar.reparentTo(self.cannon)
                avatar.setPosHpr(3, 0, 0, 90, 0, 0)
                avatar.wrtReparentTo(render)
            self.__resetToon(avatar)
        return

    def __resetToon(self, avatar, pos = None):
        if avatar:
            self.__stopCollisionHandler(avatar)
            self.__setToonUpright(avatar, pos)
            if self.localToonShooting:
                self.notify.debug('toon setting position to %s' % pos)
                if pos:
                    base.localAvatar.setPos(pos)
                camera.reparentTo(avatar)
                camera.setPos(self.av.cameraPositions[0][0])
                place = base.cr.playGame.getPlace()
                if place:
                    place.setState('finalBattle')
            self.b_setLanded()

    def __stopCollisionHandler(self, avatar):
        if avatar:
            avatar.loop('neutral')
            if self.flyColNode:
                self.flyColNode = None
            if avatar == base.localAvatar:
                avatar.collisionsOn()
            self.flyColSphere = None
            if self.flyColNodePath:
                base.cTrav.removeCollider(self.flyColNodePath)
                self.flyColNodePath.removeNode()
                self.flyColNodePath = None
            self.handler = None
        return

    def __setToonUpright(self, avatar, pos = None):
        if avatar:
            if not pos:
                pos = avatar.getPos(render)
            avatar.setPos(render, pos)
            avatar.loop('neutral')

    def b_setLanded(self):
        self.d_setLanded()

    def d_setLanded(self):
        if self.localToonShooting:
            self.sendUpdate('setLanded', [])

    def setLanded(self):
        self.removeAvFromCannon()

    def removeAvFromCannon(self):
        if self.av != None:
            self.__stopCollisionHandler(self.av)
            self.av.resetLOD()
            place = base.cr.playGame.getPlace()
            if self.av == base.localAvatar:
                if place:
                    place.setState('finalBattle')
            self.av.loop('neutral')
            self.av.setPlayRate(1.0, 'run')
            if self.av.getParent().getName() == 'toonOriginChange':
                self.av.wrtReparentTo(render)
                self.__setToonUpright(self.av)
            if self.av == base.localAvatar:
                self.av.startPosHprBroadcast()
            self.av.startSmooth()
            self.av.setScale(1, 1, 1)
            self.ignore(self.av.uniqueName('disable'))
            self.__destroyToonModels()
        return

    def setCannonWillFire(self, avId, fireTime, zRot, angle, timestamp):
        self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime))
        if not self.model_Created:
            self.notify.warning("We walked into the zone mid-flight, so we won't see it")
            return
        self.cannonPosition[0] = zRot
        self.cannonPosition[1] = angle
        self.__updateCannonPosition(avId)
        task = Task(self.__fireCannonTask)
        task.avId = avId
        ts = globalClockDelta.localElapsedTime(timestamp)
        task.fireTime = fireTime - ts
        if task.fireTime < 0.0:
            task.fireTime = 0.0
        taskMgr.add(task, self.taskName('fireCannon'))

    def __fireCannonTask(self, task):
        launchTime = task.fireTime
        avId = task.avId
        if self.toonHead == None or not self.boss.state == 'BattleTwo':
            return Task.done
        startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat = self.__calcFlightResults(avId, launchTime)

        self.notify.debug('start position: ' + str(startPos))
        self.notify.debug('start velocity: ' + str(startVel))
        self.notify.debug('time of launch: ' + str(launchTime))
        self.notify.debug('time of impact: ' + str(timeOfImpact))
        self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact)))
        head = self.toonHead
        head.stopBlink()
        head.stopLookAroundNow()
        head.reparentTo(hidden)
        juror = self.toonModel
        juror.reparentTo(render)
        juror.setPos(startPos)
        barrelHpr = self.barrel.getHpr(render)
        juror.setHpr(startHpr)
        self.jurorToon.loop('swim')
        self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0, 0, 0)
        info = {}
        info['avId'] = avId
        info['trajectory'] = trajectory
        info['launchTime'] = launchTime
        info['timeOfImpact'] = timeOfImpact
        info['hitWhat'] = hitWhat
        info['toon'] = self.toonModel
        info['hRot'] = self.cannonPosition[0]
        info['haveWhistled'] = 0
        info['maxCamPullback'] = CAMERA_PULLBACK_MIN
        if self.localToonShooting:
            camera.reparentTo(juror)
            camera.setP(45.0)
            camera.setZ(-10.0)
        self.flyColSphere = CollisionSphere(0, 0, self.av.getHeight() / 2.0, 1.0)
        self.flyColNode = CollisionNode(self.uniqueName('flySphere'))
        self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask | ToontownGlobals.PieBitmask)
        self.flyColNode.addSolid(self.flyColSphere)
        self.flyColNodePath = self.jurorToon.attachNewNode(self.flyColNode)
        self.flyColNodePath.setColor(1, 0, 0, 1)
        self.handler = CollisionHandlerEvent()
        self.handler.setInPattern(self.uniqueName('cannonHit'))
        base.cTrav.addCollider(self.flyColNodePath, self.handler)
        self.accept(self.uniqueName('cannonHit'), self.__handleCannonHit)
        shootTask = Task(self.__shootTask, self.taskName('shootTask'))
        flyTask = Task(self.__flyTask, self.taskName('flyTask'))
        shootTask.info = info
        flyTask.info = info
        seqTask = Task.sequence(shootTask, flyTask)
        taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(avId))
        self.acceptOnce(self.uniqueName('stopFlyTask'), self.__stopFlyTask)
        return Task.done

    def __toRadians(self, angle):
        return angle * 2.0 * math.pi / 360.0

    def __toDegrees(self, angle):
        return angle * 360.0 / (2.0 * math.pi)

    def __calcFlightResults(self, avId, launchTime):
        head = self.toonHead
        startPos = head.getPos(render)
        startHpr = head.getHpr(render)
        hpr = self.barrel.getHpr(render)
        rotation = self.__toRadians(hpr[0])
        angle = self.__toRadians(hpr[1])
        horizVel = INITIAL_VELOCITY * math.cos(angle)
        xVel = horizVel * -math.sin(rotation)
        yVel = horizVel * math.cos(rotation)
        zVel = INITIAL_VELOCITY * math.sin(angle)
        startVel = Vec3(xVel, yVel, zVel)
        trajectory = Trajectory.Trajectory(launchTime, startPos, startVel)
        self.trajectory = trajectory
        timeOfImpact, hitWhat = self.__calcToonImpact(trajectory)
        return startPos, startHpr, startVel, trajectory, 3 * timeOfImpact, hitWhat

    def __calcToonImpact(self, trajectory):
        t_groundImpact = trajectory.checkCollisionWithGround(GROUND_PLANE_MIN)
        if t_groundImpact >= trajectory.getStartTime():
            return (t_groundImpact, self.HIT_GROUND)
        else:
            self.notify.error('__calcToonImpact: toon never impacts ground?')
            return (0.0, self.HIT_GROUND)

    def __handleCannonHit(self, collisionEntry):
        if self.av == None or self.flyColNode == None:
            return
        interPt = collisionEntry.getSurfacePoint(render)
        hitNode = collisionEntry.getIntoNode().getName()
        fromNodePath = collisionEntry.getFromNodePath()
        intoNodePath = collisionEntry.getIntoNodePath()
        ignoredHits = ['NearBoss']
        for nodeName in ignoredHits:
            if hitNode == nodeName:
                return

        self.__stopFlyTask(self.avId)
        self.__stopCollisionHandler(self.jurorToon)
        if self.localToonShooting:
            camera.wrtReparentTo(render)
        pos = interPt
        hpr = self.jurorToon.getHpr()
        track = Sequence()
        if self.localToonShooting:
            pass
        chairlist = ['trigger-chair']
        for index in xrange(len(ToontownGlobals.LawbotBossChairPosHprs)):
            chairlist.append('Chair-%s' % index)

        if hitNode in chairlist:
            track.append(Func(self.__hitChair, self.jurorToon, pos))
            track.append(Wait(1.0))
            track.append(Func(self.__setToonUpright, self.av))
            if self.av == base.localAvatar:
                strs = hitNode.split('-')
                chairNum = int(strs[1])
                self.boss.sendUpdate('hitChair', [chairNum, self.index])
        else:
            track.append(Func(self.__hitGround, self.jurorToon, pos))
            track.append(Wait(1.0))
            track.append(Func(self.__setToonUpright, self.av))
        track.append(Func(self.b_setLanded))
        if self.localToonShooting:
            pass
        if self.hitTrack:
            self.hitTrack.finish()
        self.hitTrack = track
        self.hitTrack.start()
        return

    def enterCannonHit(self, collisionEntry):
        pass

    def __shootTask(self, task):
        base.playSfx(self.sndCannonFire)
        return Task.done

    def __flyTask(self, task):
        toon = task.info['toon']
        if toon.isEmpty():
            self.__resetToonToCannon(self.av)
            return Task.done
        curTime = task.time + task.info['launchTime']
        t = min(curTime, task.info['timeOfImpact'])
        self.lastT = self.t
        self.t = t
        deltaT = self.t - self.lastT
        self.deltaT = deltaT
        if t >= task.info['timeOfImpact']:
            self.__resetToonToCannon(self.av)
            return Task.done
        pos = task.info['trajectory'].getPos(t)
        toon.setFluidPos(pos)
        vel = task.info['trajectory'].getVel(t)
        run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1])
        rise = vel[2]
        theta = self.__toDegrees(math.atan(rise / run))
        toon.setHpr(self.cannon.getH(render), -90 + theta, 0)
        view = 2
        lookAt = task.info['toon'].getPos(render)
        hpr = task.info['toon'].getHpr(render)
        if self.localToonShooting:
            if view == 0:
                camera.wrtReparentTo(render)
                camera.lookAt(lookAt)
            elif view == 1:
                camera.reparentTo(render)
                camera.setPos(render, 100, 100, 35.25)
                camera.lookAt(render, lookAt)
            elif view == 2:
                if hpr[1] > -90:
                    camera.setPos(0, 0, -30)
                    if camera.getZ() < lookAt[2]:
                        camera.setZ(render, lookAt[2] + 10)
                    camera.lookAt(Point3(0, 0, 0))
        return Task.cont

    def __stopFlyTask(self, avId):
        taskMgr.remove(self.taskName('flyingToon') + '-' + str(avId))

    def __hitGround(self, avatar, pos, extraArgs = []):
        hitP = avatar.getPos(render)
        h = self.barrel.getH(render)
        avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0)
        avatar.setHpr(h, -135, 0)
        self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0)
        self.dustCloud.setScale(0.35)
        self.dustCloud.play()
        base.playSfx(self.sndHitGround)
        avatar.hide()

    def __hitChair(self, avatar, pos, extraArgs = []):
        hitP = avatar.getPos(render)
        h = self.barrel.getH(render)
        avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0)
        avatar.setHpr(h, -135, 0)
        self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0)
        self.dustCloud.setScale(0.35)
        self.dustCloud.play()
        base.playSfx(self.sndHitGround)
        base.playSfx(self.sndHitChair)
        avatar.hide()

    def generateCannonAppearTrack(self, avatar):
        self.cannon.setScale(0.1)
        self.cannon.show()
        kartTrack = Parallel(Sequence(ActorInterval(avatar, 'feedPet'), Func(avatar.loop, 'neutral')), Sequence(Func(self.cannon.reparentTo, avatar.rightHand), Wait(2.1), Func(self.cannon.wrtReparentTo, render), Func(self.cannon.setShear, 0, 0, 0), Parallel(LerpHprInterval(self.cannon, hpr=self.nodePath.getHpr(render), duration=1.2), ProjectileInterval(self.cannon, endPos=self.nodePath.getPos(render), duration=1.2, gravityMult=0.45)), Wait(0.2), Sequence(LerpScaleInterval(self.cannon, scale=Point3(1.1, 1.1, 0.1), duration=0.2), LerpScaleInterval(self.cannon, scale=Point3(0.9, 0.9, 0.1), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 0.1), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 1.1), duration=0.2), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 0.9), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 1.0), duration=0.1), Func(self.cannon.wrtReparentTo, self.nodePath))))
        return kartTrack
class DistributedGunGameCapturePoint(DistributedNode):
    notify = directNotify.newCategory('DistributedGunGameCapturePoint')
    
    def __init__(self, cr):
        DistributedNode.__init__(self, cr)
        NodePath.__init__(self, 'capture_point')
        self.capturePoint = None
        self.captureCircle = None
        self.aoogahSfx = None
        self.circleTrack = None
        self.circleTrackPlayRate = 1.0
        self.circleTrackDirection = 0

        self.team = None
        self.collNP = None
        self.pointCollNode = None
        
        self.neutralCapTexture = None
        self.redCapTexture = None
        self.blueCapTexture = None
        self.defCapTexture = None
        
    def delete(self):
        self.ignoreAll()
        if self.collNP:
            self.collNP.removeNode()
        if self.capturePoint:
            self.capturePoint.removeNode()
        if self.captureCircle:
            self.captureCircle.removeNode()
        if self.aoogahSfx:
            self.aoogahSfx.stop()
        if self.circleTrack:
            self.circleTrack.pause()
        del self.collNP
        del self.pointCollNode
        del self.capturePoint
        del self.captureCircle
        del self.aoogahSfx
        del self.circleTrack
        del self.circleTrackPlayRate
        del self.circleTrackDirection
        del self.team
        del self.neutralCapTexture
        del self.redCapTexture
        del self.blueCapTexture
        del self.defCapTexture
        self.removeNode()
        DistributedNode.delete(self)
        
    def getCircleScaleAnim(self, startScale, scale, duration = 1.5):
        return Sequence(
            LerpScaleInterval(self.captureCircle, startScale = startScale, scale = scale, duration = duration),
            Func(self.captureCircle.setColorScale, 0.976, 0, 0, 0.5), 
            Wait(0.25), 
        Func(self.captureCircle.setColorScale, 1, 1, 1, 0.5))
        
    def startCircleAnim(self, direction, timestamp):
        timestamp = 0
        # Begins the circle track in a certain direction.
        if self.circleTrack:
            self.circleTrack.pause()
            self.circleTrack = None

        self.circleTrackPlayRate = 1.0
        self.circleTrackDirection = direction
        
        if self.circleTrackDirection == 3:
            self.captureCircle.hide()
            return
        
        self.circleTrack = Sequence(Func(self.captureCircle.show))
            
        # Let's do the growing animation.
        if direction == 0:
            self.circleTrack = Sequence(Func(self.captureCircle.show),
                Func(self.captureCircle.setColorScale, 1, 1, 1, 0.5),
                LerpScaleInterval(self.captureCircle, startScale = 0.75, scale = 3.75, duration = 7.5),
                Func(self.captureCircle.setColorScale, 0.976, 0, 0, 0.5), 
                Wait(0.25), 
                Func(self.captureCircle.setColorScale, 1, 1, 1, 0.5),
                Wait(0.25), 
            Func(self.captureCircle.hide))
            self.circleTrack.start(timestamp)
            return
        elif direction == 1:
            # Let's do the shrinking animation.
            self.circleTrack = Sequence(
                Func(self.captureCircle.show),
                Func(self.captureCircle.setColorScale, 0.976, 0, 0, 0.5), 
                Wait(0.25), 
            Func(self.captureCircle.setColorScale, 1, 1, 1, 0.5))
            
            self.circleTrack.append(self.getCircleScaleAnim(3.75, 3.25))
            self.circleTrack.append(self.getCircleScaleAnim(3.25, 2.75))
            self.circleTrack.append(self.getCircleScaleAnim(2.75, 2.25))
            self.circleTrack.append(self.getCircleScaleAnim(2.25, 1.75))
            self.circleTrack.append(self.getCircleScaleAnim(1.75, 1.25))
            self.circleTrack.append(self.getCircleScaleAnim(1.25, 0.75))
        elif direction == 2:
            # Let's do the reset animation when a new toon fails to capture it.
            self.circleTrack = Sequence()
            self.circleTrack.append(self.getCircleScaleAnim(self.captureCircle.getScale(), 0.75, 1.0))
            self.circleTrack.append(Sequence(Wait(0.25), Func(self.captureCircle.hide)))
            self.circleTrack.start()
            return
        
        self.circleTrack.append(Sequence(Wait(0.25), Func(self.captureCircle.hide)))
        self.circleTrack.start()
        
    def handleContesters(self, contesters):
        if self.circleTrack:
            if self.circleTrackDirection == 1:
                self.circleTrack.pause()
                self.circleTrackPlayRate = self.circleTrackPlayRate + contesters
                self.circleTrack.setPlayRate(self.circleTrackPlayRate)
                self.circleTrack.resume()
            else:
                if contesters > 0 and not self.team:
                    self.circleTrack.pause()
                else:
                    self.circleTrack.resume()
                    
    def updateStatus(self, status, avId):
        if avId != 0:
            avatar = base.cr.doId2do.get(avId)

        if status == 0:
            base.minigame.showAlert('The hill is being contested!')
            self.aoogahSfx.play()
        elif status == 1:
            base.minigame.showAlert('%s has captured the hill!' % avatar.getName())
        elif status == 2:
            base.minigame.showAlert('The hill has been reset!')
        
    def announceGenerate(self):
        DistributedNode.announceGenerate(self)
        self.capturePoint = loader.loadModel('phase_4/models/props/capture_point.bam')
        self.capturePoint.setScale(2.5)
        self.capturePoint.reparentTo(self)
        
        self.captureCircle = loader.loadModel('phase_4/models/minigames/ice_game_score_circle.bam')
        self.captureCircle.setAlphaScale(0.5)
        self.captureCircle.setTransparency(TransparencyAttrib.MAlpha)
        self.captureCircle.reparentTo(self)
        self.captureCircle.setPos(self.capturePoint.getPos(render))
        self.captureCircle.setX(self.captureCircle.getX() - 0.03)
        self.captureCircle.setY(self.captureCircle.getY() - 0.1)
        self.captureCircle.setZ(self.captureCircle.getZ() + 2.4)
        self.captureCircle.hide()
        self.captureCircle.setTextureOff(1)
        
        self.aoogahSfx = loader.loadSfx('phase_5/audio/sfx/AA_sound_aoogah.ogg')
        
        sphere = CollisionSphere(0, 0, 0, 4)
        sphere.setTangible(0)
        self.pointCollNode = CollisionNode(self.uniqueName('coll_node'))
        self.pointCollNode.addSolid(sphere)
        self.pointCollNode.setCollideMask(GGG.HILL_BITMASK)
        self.collNP = self.capturePoint.attachNewNode(self.pointCollNode)
        
        self.neutralCapTexture = 'phase_4/maps/neutral_capture_point.mat'
        self.redCapTexture = 'phase_4/maps/red_capture_point.mat'
        self.blueCapTexture = 'phase_4/maps/blue_capture_point.mat'
        self.defCapTexture = 'phase_4/maps/captured_capture_point.mat'
        
        self.reparentTo(render)
        
    def startListening(self):
        self.accept('enter' + self.uniqueName('coll_node'), self.requestEnter)
        self.accept('exit' + self.uniqueName('coll_node'), self.requestExit)
        
    def setCaptured(self, teamId):
        textureSection = self.capturePoint.find('**/capture_point')
        if (teamId - 2) in GGG.TeamNameById.values():
            self.team = (teamId - 2)
            
            if self.team == GGG.RED:
                textureSection.setBSPMaterial(self.redCapTexture, 1)
            elif self.team == GGG.BLUE:
                textureSection.setBSPMaterial(self.blueCapTexture, 1)
        elif (teamId - 2) == -2:
            self.team = None
            textureSection.setBSPMaterial(self.neutralCapTexture, 1)
        elif (teamId - 2) == -1:
            self.team = None
            textureSection.setBSPMaterial(self.defCapTexture, 1)
        self.captureCircle.setTextureOff(1)
            
    def getCaptured(self):
        return self.team
        
    def requestEnter(self, entry):
        if hasattr(self, 'capturePoint') and self.capturePoint:
            self.sendUpdate('requestEnter')
        
    def requestExit(self, entry):
        if hasattr(self, 'capturePoint') and self.capturePoint:
            self.sendUpdate('requestExit')
Example #45
0
    def getSprayTrack(self,
                      origin,
                      target,
                      scaleUp,
                      hold,
                      scaleDown,
                      horizScale=1.0,
                      vertScale=1.0):
        if self.sprayJoint.isEmpty():
            self.build()
            self.origin = self.getSprayStartPos()
        base.localAvatar.stop(self.toonAnim)
        self.lastFrame = self.avatar.getCurrentFrame(self.toonAnim)
        track = Sequence()
        sprayProp = loader.loadModel(GagGlobals.SPRAY_MDL)
        sprayProp.setTwoSided(1)
        sprayScale = hidden.attachNewNode('spray-parent')
        sprayRot = hidden.attachNewNode('spray-rotate')
        sprayRot.setColor(GagGlobals.WATER_SPRAY_COLOR)
        sprayRot.setTransparency(1)

        collNode = CollisionNode('Collision')
        spraySphere = CollisionSphere(0, 0, 0, 1)
        spraySphere.setTangible(0)
        collNode.addSolid(spraySphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        sprayNP = sprayRot.attachNewNode(collNode)
        sprayNP.setY(1)
        self.sprayNP = sprayNP
        event = CollisionHandlerEvent()
        event.set_in_pattern("%fn-into")
        event.set_out_pattern("%fn-out")
        base.cTrav.add_collider(sprayNP, event)
        self.avatar.acceptOnce(sprayNP.node().getName() + '-into',
                               self.onCollision)

        def showSpray(sprayScale, sprayProp, origin, target):
            objects = [sprayRot, sprayScale, sprayProp]
            for item in objects:
                index = objects.index(item)
                if index == 0:
                    item.reparentTo(self.sprayJoint)
                    item.setPos(0, 0, 0)
                    item.setHpr(self.sprayRotation)
                    item.wrtReparentTo(render)
                else:
                    item.reparentTo(objects[index - 1])

        track.append(Func(showSpray, sprayScale, sprayProp, origin, target))
        self.spray = sprayRot

        def calcTargetScale():
            distance = Vec3(target - origin).length()
            yScale = distance / GagGlobals.SPRAY_LEN
            targetScale = Point3(yScale * horizScale, yScale,
                                 yScale * vertScale)
            return targetScale

        track.append(
            Parallel(
                LerpScaleInterval(sprayScale,
                                  scaleUp,
                                  calcTargetScale,
                                  startScale=GagGlobals.PNT3NEAR0),
                sprayNP.posInterval(
                    0.25,
                    self.spray.getPos(render) + Point3(0, 50, 0),
                    startPos=self.spray.getPos(render) + Point3(0, 5, 0))))
        track.append(Wait(hold))
        track.append(Func(self.handleMiss))
        track.append(LerpScaleInterval(sprayScale, 0.75, GagGlobals.PNT3NEAR0))

        def hideSpray():
            lambda prop: prop.removeNode(), [sprayProp, sprayRot, sprayScale]

        track.append(Func(hideSpray))
        track.append(Func(self.completeSquirt))
        return track
class DistributedPartyCatchActivity(DistributedPartyActivity,
                                    DistributedPartyCatchActivityBase):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedPartyCatchActivity')
    DropTaskName = 'dropSomething'
    DropObjectPlurals = {
        'apple': TTLocalizer.PartyCatchActivityApples,
        'orange': TTLocalizer.PartyCatchActivityOranges,
        'pear': TTLocalizer.PartyCatchActivityPears,
        'coconut': TTLocalizer.PartyCatchActivityCoconuts,
        'watermelon': TTLocalizer.PartyCatchActivityWatermelons,
        'pineapple': TTLocalizer.PartyCatchActivityPineapples,
        'anvil': TTLocalizer.PartyCatchActivityAnvils
    }

    class Generation:
        def __init__(self, generation, startTime, startNetworkTime,
                     numPlayers):
            self.generation = generation
            self.startTime = startTime
            self.startNetworkTime = startNetworkTime
            self.numPlayers = numPlayers
            self.hasBeenScheduled = False
            self.droppedObjNames = []
            self.dropSchedule = []
            self.numItemsDropped = 0
            self.droppedObjCaught = {}

    def __init__(self, cr):
        DistributedPartyActivity.__init__(
            self,
            cr,
            PartyGlobals.ActivityIds.PartyCatch,
            PartyGlobals.ActivityTypes.HostInitiated,
            wantRewardGui=True)
        self.setUsesSmoothing()
        self.setUsesLookAround()
        self._sNumGen = SerialNumGen()

    def getTitle(self):
        return TTLocalizer.PartyCatchActivityTitle

    def getInstructions(self):
        return TTLocalizer.PartyCatchActivityInstructions % {
            'badThing': self.DropObjectPlurals['anvil']
        }

    def generate(self):
        DistributedPartyActivity.generate(self)
        self.notify.info('localAvatar doId: %s' % base.localAvatar.doId)
        self.notify.info('generate()')
        self._generateFrame = globalClock.getFrameCount()
        self._id2gen = {}
        self._orderedGenerations = []
        self._orderedGenerationIndex = None
        rng = RandomNumGen(self.doId)
        self._generationSeedBase = rng.randrange(1000)
        self._lastDropTime = 0.0
        return

    def getCurGeneration(self):
        if self._orderedGenerationIndex is None:
            return
        return self._orderedGenerations[self._orderedGenerationIndex]

    def _addGeneration(self, generation, startTime, startNetworkTime,
                       numPlayers):
        self._id2gen[generation] = self.Generation(generation, startTime,
                                                   startNetworkTime,
                                                   numPlayers)
        i = 0
        while 1:
            if i >= len(self._orderedGenerations):
                break
            gen = self._orderedGenerations[i]
            startNetT = self._id2gen[gen].startTime
            genId = self._id2gen[gen].generation
            if startNetT > startNetworkTime:
                break
            if startNetT == startNetworkTime and genId > generation:
                break
            i += 1
        self._orderedGenerations = self._orderedGenerations[:i] + [
            generation
        ] + self._orderedGenerations[i:]
        if self._orderedGenerationIndex is not None:
            if self._orderedGenerationIndex >= i:
                self._orderedGenerationIndex += 1

    def _removeGeneration(self, generation):
        del self._id2gen[generation]
        i = self._orderedGenerations.index(generation)
        self._orderedGenerations = self._orderedGenerations[:
                                                            i] + self._orderedGenerations[
                                                                i + 1:]
        if self._orderedGenerationIndex is not None:
            if len(self._orderedGenerations):
                if self._orderedGenerationIndex >= i:
                    self._orderedGenerationIndex -= 1
            else:
                self._orderedGenerationIndex = None
        return

    def announceGenerate(self):
        self.notify.info('announceGenerate()')
        self.catchTreeZoneEvent = 'fence_floor'
        DistributedPartyActivity.announceGenerate(self)

    def load(self, loadModels=1, arenaModel='partyCatchTree'):
        self.notify.info('load()')
        DistributedPartyCatchActivity.notify.debug('PartyCatch: load')
        self.activityFSM = CatchActivityFSM(self)
        if __dev__:
            for o in range(3):
                print({
                    0:
                    'SPOTS PER PLAYER',
                    1:
                    'DROPS PER MINUTE PER SPOT DURING NORMAL DROP PERIOD',
                    2:
                    'DROPS PER MINUTE PER PLAYER DURING NORMAL DROP PERIOD'
                }[o])
                for i in range(1, self.FallRateCap_Players + 10):
                    self.defineConstants(forceNumPlayers=i)
                    numDropLocations = self.DropRows * self.DropColumns
                    numDropsPerMin = 60.0 / self.DropPeriod
                    if o == 0:
                        spotsPerPlayer = numDropLocations / float(i)
                        print('%2d PLAYERS: %s' % (i, spotsPerPlayer))
                    elif o == 1:
                        numDropsPerMinPerSpot = numDropsPerMin / numDropLocations
                        print('%2d PLAYERS: %s' % (i, numDropsPerMinPerSpot))
                    elif i > 0:
                        numDropsPerMinPerPlayer = numDropsPerMin / i
                        print('%2d PLAYERS: %s' % (i, numDropsPerMinPerPlayer))

        self.defineConstants()
        self.treesAndFence = loader.loadModel('phase_13/models/parties/%s' %
                                              arenaModel)
        self.treesAndFence.setScale(0.9)
        self.treesAndFence.find('**/fence_floor').setPos(0.0, 0.0, 0.1)
        self.treesAndFence.reparentTo(self.root)
        ground = self.treesAndFence.find('**/groundPlane')
        ground.setBin('ground', 1)
        DistributedPartyActivity.load(self)
        exitText = TextNode('PartyCatchExitText')
        exitText.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
        exitText.setCardDecal(True)
        exitText.setCardColor(1.0, 1.0, 1.0, 0.0)
        exitText.setText(TTLocalizer.PartyCatchActivityExit)
        exitText.setTextColor(0.0, 8.0, 0.0, 0.9)
        exitText.setAlign(exitText.ACenter)
        exitText.setFont(ToontownGlobals.getBuildingNametagFont())
        exitText.setShadowColor(0, 0, 0, 1)
        exitText.setBin('fixed')
        if TTLocalizer.BuildingNametagShadow:
            exitText.setShadow(*TTLocalizer.BuildingNametagShadow)
        exitTextLoc = self.treesAndFence.find('**/loc_exitSignText')
        exitTextNp = exitTextLoc.attachNewNode(exitText)
        exitTextNp.setDepthWrite(0)
        exitTextNp.setScale(4)
        exitTextNp.setZ(-.5)
        self.sign.reparentTo(self.treesAndFence.find('**/loc_eventSign'))
        self.sign.wrtReparentTo(self.root)
        self.avatarNodePath = NodePath('PartyCatchAvatarNodePath')
        self.avatarNodePath.reparentTo(self.root)
        self._avatarNodePathParentToken = 3
        base.cr.parentMgr.registerParent(self._avatarNodePathParentToken,
                                         self.avatarNodePath)
        self.toonSDs = {}
        self.dropShadow = loader.loadModelOnce(
            'phase_3/models/props/drop_shadow')
        self.dropObjModels = {}
        if loadModels:
            self.__loadDropModels()
        self.sndGoodCatch = base.loader.loadSfx(
            'phase_4/audio/sfx/SZ_DD_treasure.ogg')
        self.sndOof = base.loader.loadSfx(
            'phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndAnvilLand = base.loader.loadSfx(
            'phase_4/audio/sfx/AA_drop_anvil_miss.ogg')
        self.sndPerfect = base.loader.loadSfx(
            'phase_4/audio/sfx/ring_perfect.ogg')
        self.__textGen = TextNode('partyCatchActivity')
        self.__textGen.setFont(ToontownGlobals.getSignFont())
        self.__textGen.setAlign(TextNode.ACenter)
        self.activityFSM.request('Idle')

    def __loadDropModels(self):
        for objType in PartyGlobals.DropObjectTypes:
            model = loader.loadModel(objType.modelPath)
            self.dropObjModels[objType.name] = model
            modelScales = {
                'apple': 0.7,
                'orange': 0.7,
                'pear': 0.5,
                'coconut': 0.7,
                'watermelon': 0.6,
                'pineapple': 0.45
            }
            if objType.name in modelScales:
                model.setScale(modelScales[objType.name])
            if objType == PartyGlobals.Name2DropObjectType['pear']:
                model.setZ(-.6)
            if objType == PartyGlobals.Name2DropObjectType['coconut']:
                model.setP(180)
            if objType == PartyGlobals.Name2DropObjectType['watermelon']:
                model.setH(135)
                model.setZ(-.5)
            if objType == PartyGlobals.Name2DropObjectType['pineapple']:
                model.setZ(-1.7)
            if objType == PartyGlobals.Name2DropObjectType['anvil']:
                model.setZ(-self.ObjRadius)
            model.flattenStrong()

    def unload(self):
        DistributedPartyCatchActivity.notify.debug('unload')
        self.finishAllDropIntervals()
        self.destroyOrthoWalk()
        DistributedPartyActivity.unload(self)
        self.stopDropTask()
        del self.activityFSM
        del self.__textGen
        for avId in list(self.toonSDs.keys()):
            if avId in self.toonSDs:
                toonSD = self.toonSDs[avId]
                toonSD.unload()

        del self.toonSDs
        self.treesAndFence.removeNode()
        del self.treesAndFence
        self.dropShadow.removeNode()
        del self.dropShadow
        base.cr.parentMgr.unregisterParent(self._avatarNodePathParentToken)
        for model in list(self.dropObjModels.values()):
            model.removeNode()

        del self.dropObjModels
        del self.sndGoodCatch
        del self.sndOof
        del self.sndAnvilLand
        del self.sndPerfect

    def setStartTimestamp(self, timestamp32):
        self.notify.info('setStartTimestamp(%s)' % (timestamp32, ))
        self._startTimestamp = globalClockDelta.networkToLocalTime(timestamp32,
                                                                   bits=32)

    def getCurrentCatchActivityTime(self):
        return globalClock.getFrameTime() - self._startTimestamp

    def getObjModel(self, objName):
        return self.dropObjModels[objName].copyTo(hidden)

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        base.cr.playGame.getPlace().fsm.request('walk')

    def handleToonJoined(self, toonId):
        if toonId not in self.toonSDs:
            toonSD = PartyCatchActivityToonSD(toonId, self)
            self.toonSDs[toonId] = toonSD
            toonSD.load()
        self.notify.debug('handleToonJoined : currentState = %s' %
                          self.activityFSM.state)
        self.cr.doId2do[toonId].useLOD(500)
        if self.activityFSM.state == 'Active':
            if toonId in self.toonSDs:
                self.toonSDs[toonId].enter()
            if base.localAvatar.doId == toonId:
                base.localAvatar.b_setParent(self._avatarNodePathParentToken)
                self.putLocalAvatarInActivity()
            if toonId in self.toonSDs:
                self.toonSDs[toonId].fsm.request('rules')

    def handleToonExited(self, toonId):
        self.notify.debug('handleToonExited( toonId=%s )' % toonId)
        if toonId in self.cr.doId2do:
            self.cr.doId2do[toonId].resetLOD()
            if toonId in self.toonSDs:
                self.toonSDs[toonId].fsm.request('notPlaying')
                self.toonSDs[toonId].exit()
                self.toonSDs[toonId].unload()
                del self.toonSDs[toonId]
            if base.localAvatar.doId == toonId:
                base.localAvatar.b_setParent(ToontownGlobals.SPRender)

    def takeLocalAvatarOutOfActivity(self):
        self.notify.debug('localToon has left the circle')
        camera.reparentTo(base.localAvatar)
        base.localAvatar.startUpdateSmartCamera()
        base.localAvatar.enableSmartCameraViews()
        base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
        DistributedSmoothNode.activateSmoothing(1, 0)

    def _enableCollisions(self):
        DistributedPartyActivity._enableCollisions(self)
        self._enteredTree = False
        self.accept('enter' + self.catchTreeZoneEvent,
                    self._toonMayHaveEnteredTree)
        self.accept('again' + self.catchTreeZoneEvent,
                    self._toonMayHaveEnteredTree)
        self.accept('exit' + self.catchTreeZoneEvent, self._toonExitedTree)
        self.accept(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT,
                    self._handleCannonLanded)

    def _disableCollisions(self):
        self.ignore(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT)
        self.ignore('enter' + self.catchTreeZoneEvent)
        self.ignore('again' + self.catchTreeZoneEvent)
        self.ignore('exit' + self.catchTreeZoneEvent)
        DistributedPartyActivity._disableCollisions(self)

    def _handleCannonLanded(self):
        x = base.localAvatar.getX()
        y = base.localAvatar.getY()
        if x > self.x - self.StageHalfWidth and x < self.x + self.StageHalfWidth and y > self.y - self.StageHalfHeight and y < self.y + self.StageHalfHeight:
            self._toonEnteredTree(None)
        return

    def _toonMayHaveEnteredTree(self, collEntry):
        if self._enteredTree:
            return
        if base.localAvatar.controlManager.currentControls.getIsAirborne():
            return
        self._toonEnteredTree(collEntry)

    def _toonEnteredTree(self, collEntry):
        self.notify.debug('_toonEnteredTree : avid = %s' %
                          base.localAvatar.doId)
        self.notify.debug('_toonEnteredTree : currentState = %s' %
                          self.activityFSM.state)
        if self.isLocalToonInActivity():
            return
        if self.activityFSM.state == 'Active':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        elif self.activityFSM.state == 'Idle':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        self._enteredTree = True

    def _toonExitedTree(self, collEntry):
        self.notify.debug('_toonExitedTree : avid = %s' %
                          base.localAvatar.doId)
        self._enteredTree = False
        if hasattr(
                base.cr.playGame.getPlace(), 'fsm'
        ) and self.activityFSM.state == 'Active' and self.isLocalToonInActivity(
        ):
            if base.localAvatar.doId in self.toonSDs:
                self.takeLocalAvatarOutOfActivity()
                self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying')
            self.d_toonExitDemand()

    def setToonsPlaying(self, toonIds):
        self.notify.info('setToonsPlaying(%s)' % (toonIds, ))
        DistributedPartyActivity.setToonsPlaying(self, toonIds)
        if self.isLocalToonInActivity(
        ) and base.localAvatar.doId not in toonIds:
            if base.localAvatar.doId in self.toonSDs:
                self.takeLocalAvatarOutOfActivity()
                self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying')

    def __genText(self, text):
        self.__textGen.setText(text)
        return self.__textGen.generate()

    def getNumPlayers(self):
        return len(self.toonIds)

    def defineConstants(self, forceNumPlayers=None):
        DistributedPartyCatchActivity.notify.debug('defineConstants')
        self.ShowObjSpheres = 0
        self.ShowToonSpheres = 0
        self.useGravity = True
        self.trickShadows = True
        if forceNumPlayers is None:
            numPlayers = self.getNumPlayers()
        else:
            numPlayers = forceNumPlayers
        self.calcDifficultyConstants(numPlayers)
        DistributedPartyCatchActivity.notify.debug('ToonSpeed: %s' %
                                                   self.ToonSpeed)
        DistributedPartyCatchActivity.notify.debug('total drops: %s' %
                                                   self.totalDrops)
        DistributedPartyCatchActivity.notify.debug('numFruits: %s' %
                                                   self.numFruits)
        DistributedPartyCatchActivity.notify.debug('numAnvils: %s' %
                                                   self.numAnvils)
        self.ObjRadius = 1.0
        dropRegionTable = PartyRegionDropPlacer.getDropRegionTable(numPlayers)
        self.DropRows, self.DropColumns = len(dropRegionTable), len(
            dropRegionTable[0])
        for objType in PartyGlobals.DropObjectTypes:
            DistributedPartyCatchActivity.notify.debug('*** Object Type: %s' %
                                                       objType.name)
            objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration
            DistributedPartyCatchActivity.notify.debug(
                'onscreenDuration=%s' % objType.onscreenDuration)
            v_0 = 0.0
            t = objType.onscreenDuration
            x_0 = self.MinOffscreenHeight
            x = 0.0
            g = 2.0 * (x - x_0 - v_0 * t) / (t * t)
            DistributedPartyCatchActivity.notify.debug('gravity=%s' % g)
            objType.trajectory = Trajectory(0,
                                            Vec3(0, 0, x_0),
                                            Vec3(0, 0, v_0),
                                            gravMult=abs(g /
                                                         Trajectory.gravity))
            objType.fallDuration = objType.onscreenDuration + self.OffscreenTime

        return

    def grid2world(self, column, row):
        x = column / float(self.DropColumns - 1)
        y = row / float(self.DropRows - 1)
        x = x * 2.0 - 1.0
        y = y * 2.0 - 1.0
        x *= self.StageHalfWidth
        y *= self.StageHalfHeight
        return (x, y)

    def showPosts(self):
        self.hidePosts()
        self.posts = [Toon.Toon(), Toon.Toon(), Toon.Toon(), Toon.Toon()]
        for i in range(len(self.posts)):
            tree = self.posts[i]
            tree.reparentTo(render)
            x = self.StageHalfWidth
            y = self.StageHalfHeight
            if i > 1:
                x = -x
            if i % 2:
                y = -y
            tree.setPos(x + self.x, y + self.y, 0)

    def hidePosts(self):
        if hasattr(self, 'posts'):
            for tree in self.posts:
                tree.removeNode()

            del self.posts

    def showDropGrid(self):
        self.hideDropGrid()
        self.dropMarkers = []
        for row in range(self.DropRows):
            self.dropMarkers.append([])
            rowList = self.dropMarkers[row]
            for column in range(self.DropColumns):
                toon = Toon.Toon()
                toon.setDNA(base.localAvatar.getStyle())
                toon.reparentTo(self.root)
                toon.setScale(1.0 / 3)
                x, y = self.grid2world(column, row)
                toon.setPos(x, y, 0)
                rowList.append(toon)

    def hideDropGrid(self):
        if hasattr(self, 'dropMarkers'):
            for row in self.dropMarkers:
                for marker in row:
                    marker.removeNode()

            del self.dropMarkers

    def handleToonDisabled(self, avId):
        DistributedPartyCatchActivity.notify.debug('handleToonDisabled')
        DistributedPartyCatchActivity.notify.debug('avatar ' + str(avId) +
                                                   ' disabled')
        if avId in self.toonSDs:
            self.toonSDs[avId].exit(unexpectedExit=True)
        del self.toonSDs[avId]

    def turnOffSmoothingOnGuests(self):
        pass

    def setState(self, newState, timestamp):
        self.notify.info('setState(%s, %s)' % (newState, timestamp))
        DistributedPartyCatchActivity.notify.debug(
            'setState( newState=%s, ... )' % newState)
        DistributedPartyActivity.setState(self, newState, timestamp)
        self.activityFSM.request(newState)
        if newState == 'Active':
            if base.localAvatar.doId != self.party.partyInfo.hostId:
                if globalClock.getFrameCount() > self._generateFrame:
                    if base.localAvatar.getX(
                    ) > self.x - self.StageHalfWidth and base.localAvatar.getX(
                    ) < self.x + self.StageHalfWidth and base.localAvatar.getY(
                    ) > self.y - self.StageHalfHeight and base.localAvatar.getY(
                    ) < self.y + self.StageHalfHeight:
                        self._toonEnteredTree(None)
        return

    def putLocalAvatarInActivity(self):
        if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(),
                                                   'fsm'):
            base.cr.playGame.getPlace().fsm.request('activity', [False])
        else:
            self.notify.info(
                "Avoided crash: toontown.parties.DistributedPartyCatchActivity:632, toontown.parties.DistributedPartyCatchActivity:1198, toontown.parties.activityFSMMixins:49, direct.fsm.FSM:423, AttributeError: 'NoneType' object has no attribute 'fsm'"
            )
        base.localAvatar.stopUpdateSmartCamera()
        camera.reparentTo(self.treesAndFence)
        camera.setPosHpr(0.0, -63.0, 30.0, 0.0, -20.0, 0.0)
        if not hasattr(self, 'ltLegsCollNode'):
            self.createCatchCollisions()

    def createCatchCollisions(self):
        radius = 0.7
        handler = CollisionHandlerEvent()
        handler.setInPattern('ltCatch%in')
        self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
        self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
        self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
        self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
        self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode)
        legsCollNodepath.hide()
        head = base.localAvatar.getHeadParts().getPath(2)
        headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
        headCollNodepath.hide()
        lHand = base.localAvatar.getLeftHands()[0]
        lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
        lHandCollNodepath.hide()
        rHand = base.localAvatar.getRightHands()[0]
        rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
        rHandCollNodepath.hide()
        base.localAvatar.cTrav.addCollider(legsCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(headCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
        if self.ShowToonSpheres:
            legsCollNodepath.show()
            headCollNodepath.show()
            lHandCollNodepath.show()
            rHandCollNodepath.show()
        self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
        self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
        self.ltLHandCollNode.addSolid(
            CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.ltRHandCollNode.addSolid(
            CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.toonCollNodes = [
            legsCollNodepath, headCollNodepath, lHandCollNodepath,
            rHandCollNodepath
        ]

    def destroyCatchCollisions(self):
        if not hasattr(self, 'ltLegsCollNode'):
            return
        for collNode in self.toonCollNodes:
            while collNode.node().getNumSolids():
                collNode.node().removeSolid(0)

            base.localAvatar.cTrav.removeCollider(collNode)

        del self.toonCollNodes
        del self.ltLegsCollNode
        del self.ltHeadCollNode
        del self.ltLHandCollNode
        del self.ltRHandCollNode

    def timerExpired(self):
        pass

    def __handleCatch(self, generation, objNum):
        DistributedPartyCatchActivity.notify.debug('catch: %s' %
                                                   [generation, objNum])
        if base.localAvatar.doId not in self.toonIds:
            return
        self.showCatch(base.localAvatar.doId, generation, objNum)
        objName = self._id2gen[generation].droppedObjNames[objNum]
        objTypeId = PartyGlobals.Name2DOTypeId[objName]
        self.sendUpdate('claimCatch', [generation, objNum, objTypeId])
        self.finishDropInterval(generation, objNum)

    def showCatch(self, avId, generation, objNum):
        if avId not in self.toonSDs:
            return
        isLocal = avId == base.localAvatar.doId
        if generation not in self._id2gen:
            return
        if not self._id2gen[generation].hasBeenScheduled:
            return
        objName = self._id2gen[generation].droppedObjNames[objNum]
        objType = PartyGlobals.Name2DropObjectType[objName]
        if objType.good:
            if objNum not in self._id2gen[generation].droppedObjCaught:
                if isLocal:
                    base.playSfx(self.sndGoodCatch)
                fruit = self.getObjModel(objName)
                toon = self.getAvatar(avId)
                rHand = toon.getRightHands()[1]
                self.toonSDs[avId].eatFruit(fruit, rHand)
        else:
            self.toonSDs[avId].fsm.request('fallForward')
        self._id2gen[generation].droppedObjCaught[objNum] = 1

    def setObjectCaught(self, avId, generation, objNum):
        self.notify.info('setObjectCaught(%s, %s, %s)' %
                         (avId, generation, objNum))
        if self.activityFSM.state != 'Active':
            DistributedPartyCatchActivity.notify.warning(
                'ignoring msg: object %s caught by %s' % (objNum, avId))
            return
        isLocal = avId == base.localAvatar.doId
        if not isLocal:
            DistributedPartyCatchActivity.notify.debug(
                'AI: avatar %s caught %s' % (avId, objNum))
            self.finishDropInterval(generation, objNum)
            self.showCatch(avId, generation, objNum)
        self._scheduleGenerations()
        gen = self._id2gen[generation]
        if gen.hasBeenScheduled:
            objName = gen.droppedObjNames[objNum]
            if PartyGlobals.Name2DropObjectType[objName].good:
                if hasattr(self, 'fruitsCaught'):
                    self.fruitsCaught += 1

    def finishDropInterval(self, generation, objNum):
        if hasattr(self, 'dropIntervals'):
            if (generation, objNum) in self.dropIntervals:
                self.dropIntervals[generation, objNum].finish()

    def finishAllDropIntervals(self):
        if hasattr(self, 'dropIntervals'):
            for dropInterval in list(self.dropIntervals.values()):
                dropInterval.finish()

    def setGenerations(self, generations):
        self.notify.info('setGenerations(%s)' % (generations, ))
        gen2t = {}
        gen2nt = {}
        gen2np = {}
        for id, timestamp32, numPlayers in generations:
            gen2t[id] = globalClockDelta.networkToLocalTime(
                timestamp32, bits=32) - self._startTimestamp
            gen2nt[id] = timestamp32
            gen2np[id] = numPlayers

        ids = list(self._id2gen.keys())
        for id in ids:
            if id not in gen2t:
                self._removeGeneration(id)

        for id in gen2t:
            if id not in self._id2gen:
                self._addGeneration(id, gen2t[id], gen2nt[id], gen2np[id])

    def scheduleDrops(self, genId=None):
        if genId is None:
            genId = self.getCurGeneration()
        gen = self._id2gen[genId]
        if gen.hasBeenScheduled:
            return
        fruitIndex = int((gen.startTime + 0.5 * self.DropPeriod) /
                         PartyGlobals.CatchActivityDuration)
        fruitNames = [
            'apple', 'orange', 'pear', 'coconut', 'watermelon', 'pineapple'
        ]
        fruitName = fruitNames[fruitIndex % len(fruitNames)]
        rng = RandomNumGen(genId + self._generationSeedBase)
        gen.droppedObjNames = [fruitName
                               ] * self.numFruits + ['anvil'] * self.numAnvils
        rng.shuffle(gen.droppedObjNames)
        dropPlacer = PartyRegionDropPlacer(self,
                                           gen.numPlayers,
                                           genId,
                                           gen.droppedObjNames,
                                           startTime=gen.startTime)
        gen.numItemsDropped = 0
        tIndex = gen.startTime % PartyGlobals.CatchActivityDuration
        tPercent = float(tIndex) / PartyGlobals.CatchActivityDuration
        gen.numItemsDropped += dropPlacer.skipPercent(tPercent)
        while not dropPlacer.doneDropping(continuous=True):
            nextDrop = dropPlacer.getNextDrop()
            gen.dropSchedule.append(nextDrop)

        gen.hasBeenScheduled = True
        return

    def startDropTask(self):
        taskMgr.add(self.dropTask, self.DropTaskName)

    def stopDropTask(self):
        taskMgr.remove(self.DropTaskName)

    def _scheduleGenerations(self):
        curT = self.getCurrentCatchActivityTime()
        genIndex = self._orderedGenerationIndex
        newGenIndex = genIndex
        while genIndex is None or genIndex < len(self._orderedGenerations) - 1:
            if genIndex is None:
                nextGenIndex = 0
            else:
                nextGenIndex = genIndex + 1
            nextGenId = self._orderedGenerations[nextGenIndex]
            nextGen = self._id2gen[nextGenId]
            startT = nextGen.startTime
            if curT >= startT:
                newGenIndex = nextGenIndex
            if not nextGen.hasBeenScheduled:
                self.defineConstants(forceNumPlayers=nextGen.numPlayers)
                self.scheduleDrops(
                    genId=self._orderedGenerations[nextGenIndex])
            genIndex = nextGenIndex

        self._orderedGenerationIndex = newGenIndex
        return

    def dropTask(self, task):
        self._scheduleGenerations()
        curT = self.getCurrentCatchActivityTime()
        if self._orderedGenerationIndex is not None:
            i = self._orderedGenerationIndex
            genIndex = self._orderedGenerations[i]
            gen = self._id2gen[genIndex]
            while len(gen.dropSchedule) > 0 and gen.dropSchedule[0][0] < curT:
                drop = gen.dropSchedule[0]
                gen.dropSchedule = gen.dropSchedule[1:]
                dropTime, objName, dropCoords = drop
                objNum = gen.numItemsDropped
                x, y = self.grid2world(*dropCoords)
                dropIval = self.getDropIval(x, y, objName, genIndex, objNum)

                def cleanup(generation, objNum, self=self):
                    del self.dropIntervals[generation, objNum]

                dropIval.append(Func(Functor(cleanup, genIndex, objNum)))
                self.dropIntervals[genIndex, objNum] = dropIval
                gen.numItemsDropped += 1
                dropIval.start(curT - dropTime)
                self._lastDropTime = dropTime

        return Task.cont

    def getDropIval(self, x, y, dropObjName, generation, num):
        objType = PartyGlobals.Name2DropObjectType[dropObjName]
        id = (generation, num)
        dropNode = hidden.attachNewNode('catchDropNode%s' % (id, ))
        dropNode.setPos(x, y, 0)
        shadow = self.dropShadow.copyTo(dropNode)
        shadow.setZ(PartyGlobals.CatchDropShadowHeight)
        shadow.setColor(1, 1, 1, 1)
        object = self.getObjModel(dropObjName)
        object.reparentTo(hidden)
        if dropObjName in ['watermelon', 'anvil']:
            objH = object.getH()
            absDelta = {'watermelon': 12, 'anvil': 15}[dropObjName]
            delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta
            newH = objH + delta
        else:
            newH = self.randomNumGen.random() * 360.0
        object.setH(newH)
        sphereName = 'FallObj%s' % (id, )
        radius = self.ObjRadius
        if objType.good:
            radius *= lerp(1.0, 1.3, 0.5)
        collSphere = CollisionSphere(0, 0, 0, radius)
        collSphere.setTangible(0)
        collNode = CollisionNode(sphereName)
        collNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        collNode.addSolid(collSphere)
        collNodePath = object.attachNewNode(collNode)
        collNodePath.hide()
        if self.ShowObjSpheres:
            collNodePath.show()
        catchEventName = 'ltCatch' + sphereName

        def eatCollEntry(forward, collEntry):
            forward()

        self.accept(
            catchEventName,
            Functor(eatCollEntry, Functor(self.__handleCatch, id[0], id[1])))

        def cleanup(self=self, dropNode=dropNode, id=id, event=catchEventName):
            self.ignore(event)
            dropNode.removeNode()

        duration = objType.fallDuration
        onscreenDuration = objType.onscreenDuration
        targetShadowScale = 0.3
        if self.trickShadows:
            intermedScale = targetShadowScale * (self.OffscreenTime /
                                                 self.BaselineDropDuration)
            shadowScaleIval = Sequence(
                LerpScaleInterval(shadow,
                                  self.OffscreenTime,
                                  intermedScale,
                                  startScale=0))
            shadowScaleIval.append(
                LerpScaleInterval(shadow,
                                  duration - self.OffscreenTime,
                                  targetShadowScale,
                                  startScale=intermedScale))
        else:
            shadowScaleIval = LerpScaleInterval(shadow,
                                                duration,
                                                targetShadowScale,
                                                startScale=0)
        targetShadowAlpha = 0.4
        shadowAlphaIval = LerpColorScaleInterval(
            shadow,
            self.OffscreenTime,
            Point4(1, 1, 1, targetShadowAlpha),
            startColorScale=Point4(1, 1, 1, 0))
        shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
        if self.useGravity:

            def setObjPos(t, objType=objType, object=object):
                z = objType.trajectory.calcZ(t)
                object.setZ(z)

            setObjPos(0)
            dropIval = LerpFunctionInterval(setObjPos,
                                            fromData=0,
                                            toData=onscreenDuration,
                                            duration=onscreenDuration)
        else:
            startPos = Point3(0, 0, self.MinOffscreenHeight)
            object.setPos(startPos)
            dropIval = LerpPosInterval(object,
                                       onscreenDuration,
                                       Point3(0, 0, 0),
                                       startPos=startPos,
                                       blendType='easeIn')
        ival = Sequence(Func(Functor(dropNode.reparentTo, self.root)),
                        Parallel(
                            Sequence(
                                WaitInterval(self.OffscreenTime),
                                Func(Functor(object.reparentTo, dropNode)),
                                dropIval), shadowIval),
                        Func(cleanup),
                        name='drop%s' % (id, ))
        if objType == PartyGlobals.Name2DropObjectType['anvil']:
            ival.append(Func(self.playAnvil))
        return ival

    def playAnvil(self):
        if base.localAvatar.doId in self.toonIds:
            base.playSfx(self.sndAnvilLand)

    def initOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self=self):
            x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
            y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def destroyOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('destroyOrthoWalk')
        if hasattr(self, 'orthoWalk'):
            self.orthoWalk.stop()
            self.orthoWalk.destroy()
            del self.orthoWalk

    def startIdle(self):
        DistributedPartyCatchActivity.notify.debug('startIdle')

    def finishIdle(self):
        DistributedPartyCatchActivity.notify.debug('finishIdle')

    def startActive(self):
        DistributedPartyCatchActivity.notify.debug('startActive')
        for avId in self.toonIds:
            if avId in self.toonSDs:
                toonSD = self.toonSDs[avId]
                toonSD.enter()
                toonSD.fsm.request('normal')

        self.fruitsCaught = 0
        self.dropIntervals = {}
        self.startDropTask()
        if base.localAvatar.doId in self.toonIds:
            self.putLocalAvatarInActivity()

    def finishActive(self):
        DistributedPartyCatchActivity.notify.debug('finishActive')
        self.stopDropTask()
        if hasattr(self, 'finishIval'):
            self.finishIval.pause()
            del self.finishIval
        if base.localAvatar.doId in self.toonIds:
            self.takeLocalAvatarOutOfActivity()
        for ival in list(self.dropIntervals.values()):
            ival.finish()

        del self.dropIntervals

    def startConclusion(self):
        DistributedPartyCatchActivity.notify.debug('startConclusion')
        for avId in self.toonIds:
            if avId in self.toonSDs:
                toonSD = self.toonSDs[avId]
                toonSD.fsm.request('notPlaying')

        self.destroyCatchCollisions()
        if base.localAvatar.doId not in self.toonIds:
            return
        else:
            self.localToonExiting()
        if self.fruitsCaught >= self.numFruits:
            finishText = TTLocalizer.PartyCatchActivityFinishPerfect
        else:
            finishText = TTLocalizer.PartyCatchActivityFinish
        perfectTextSubnode = hidden.attachNewNode(self.__genText(finishText))
        perfectText = hidden.attachNewNode('perfectText')
        perfectTextSubnode.reparentTo(perfectText)
        frame = self.__textGen.getCardActual()
        offsetY = -abs(frame[2] + frame[3]) / 2.0
        perfectTextSubnode.setPos(0, 0, offsetY)
        perfectText.setColor(1, 0.1, 0.1, 1)

        def fadeFunc(t, text=perfectText):
            text.setColorScale(1, 1, 1, t)

        def destroyText(text=perfectText):
            text.removeNode()

        textTrack = Sequence(
            Func(perfectText.reparentTo, aspect2d),
            Parallel(
                LerpScaleInterval(perfectText,
                                  duration=0.5,
                                  scale=0.3,
                                  startScale=0.0),
                LerpFunctionInterval(fadeFunc,
                                     fromData=0.0,
                                     toData=1.0,
                                     duration=0.5)), Wait(2.0),
            Parallel(
                LerpScaleInterval(perfectText, duration=0.5, scale=1.0),
                LerpFunctionInterval(fadeFunc,
                                     fromData=1.0,
                                     toData=0.0,
                                     duration=0.5,
                                     blendType='easeIn')), Func(destroyText),
            WaitInterval(0.5))
        soundTrack = SoundInterval(self.sndPerfect)
        self.finishIval = Parallel(textTrack, soundTrack)
        self.finishIval.start()

    def finishConclusion(self):
        DistributedPartyCatchActivity.notify.debug('finishConclusion')
        if base.localAvatar.doId in self.toonIds:
            self.takeLocalAvatarOutOfActivity()
            base.cr.playGame.getPlace().fsm.request('walk')

    def showJellybeanReward(self, earnedAmount, jarAmount, message):
        if earnedAmount > 0:
            DistributedPartyActivity.showJellybeanReward(
                self, earnedAmount, jarAmount, message)
        else:
            base.cr.playGame.getPlace().fsm.request('walk')
Example #47
0
class DistributedToonFestTrampolineActivity(DistributedPartyActivity):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedToonFestTrampolineActivity')

    def __init__(self, cr, doJellyBeans=True, doTricks=False, texture=None):
        DistributedToonFestTrampolineActivity.notify.debug('__init__')
        DistributedPartyActivity.__init__(
            self,
            cr,
            PartyGlobals.ActivityIds.PartyTrampoline,
            PartyGlobals.ActivityTypes.GuestInitiated,
            wantLever=False,
            wantRewardGui=True)
        self.doJellyBeans = doJellyBeans
        self.doTricks = doTricks
        self.texture = texture
        self.toon = None
        self.trampHeight = 3.6
        self.trampK = 400.0
        self.normalTrampB = 2.5
        self.leavingTrampB = 8.0
        self.trampB = self.normalTrampB
        self.g = -32.0
        self.jumpBoost = 330.0
        self.beginningBoost = 500.0
        self.beginningBoostThreshold = self.trampHeight + 1.5
        self.earlyJumpThreshold = 75.0
        self.boingThreshold = 300.0
        self.turnFactor = 120.0
        self.stepDT = 0.001
        self.targetCameraPos = Point3(0.0, 40.0, 10.0)
        self.cameraSpeed = 2.0
        self.hopOffPos = Point3(16.0, 0.0, 0.0)
        self.indicatorFactor = 0.0095
        self.dropShadowCutoff = 15.0
        self.minHeightForText = 15.0
        self.heightTextOffset = -0.065
        self.beanOffset = 0.5
        self.guiBeanOffset = -0.02
        self.jumpTextShown = False
        self.toonJumped = False
        self.turnLeft = False
        self.turnRight = False
        self.leavingTrampoline = False
        self.toonVelocity = 0.0
        self.topHeight = 0.0
        self.lastPeak = 0.0
        self.beginRoundInterval = None
        self.hopOnAnim = None
        self.hopOffAnim = None
        self.flashTextInterval = None
        self.numJellyBeans = PartyGlobals.TrampolineNumJellyBeans
        self.jellyBeanBonus = PartyGlobals.TrampolineJellyBeanBonus
        self.jellyBeanStartHeight = 20.0
        self.jellyBeanStopHeight = 90.0
        self.jellyBeanColors = [
            VBase4(1.0, 0.5, 0.5, 1.0),
            VBase4(0.5, 1.0, 0.5, 1.0),
            VBase4(0.5, 1.0, 1.0, 1.0),
            VBase4(1.0, 1.0, 0.4, 1.0),
            VBase4(0.4, 0.4, 1.0, 1.0),
            VBase4(1.0, 0.5, 1.0, 1.0)
        ]
        delta = (self.jellyBeanStopHeight -
                 self.jellyBeanStartHeight) / (self.numJellyBeans - 1)
        self.jellyBeanPositions = [
            self.jellyBeanStartHeight + n * delta
            for n in range(self.numJellyBeans)
        ]
        self.doSimulateStep = False
        return

    def load(self):
        DistributedToonFestTrampolineActivity.notify.debug('load')
        DistributedPartyActivity.load(self)
        self.loadModels()
        self.loadCollision()
        self.loadGUI()
        self.loadSounds()
        self.loadIntervals()
        self.activityFSM = TrampolineActivityFSM(self)
        self.activityFSM.request('Idle')
        self.animFSM = TrampolineAnimFSM(self)
        self.setBestHeightInfo('', 0)

    def loadModels(self):
        self.tramp = self.root.attachNewNode(self.uniqueName('tramp'))
        self.trampActor = Actor(
            'phase_13/models/parties/trampoline_model',
            {'emptyAnim': 'phase_13/models/parties/trampoline_anim'})
        self.trampActor.reparentTo(self.tramp)
        if self.texture:
            reskinNode = self.tramp.find(
                '**/trampoline/__Actor_modelRoot/-GeomNode')
            reskinNode.setTexture(loader.loadTexture(self.texture), 100)
        self.surface = NodePath(self.uniqueName('trampSurface'))
        self.surface.reparentTo(self.tramp)
        self.surface.setZ(3.6)
        self.trampHeight = self.z + 3.6
        self.beginningBoostThreshold = self.trampHeight + 1.5
        self.trampActor.controlJoint(self.surface, 'modelRoot',
                                     'trampoline_joint1')
        self.sign.setPos(PartyGlobals.TrampolineSignOffset)
        self.beans = [
            loader.loadModel('phase_6/models/events/ttr_tf_m_token')
            for i in range(self.numJellyBeans)
        ]
        for bean in self.beans:
            bean.setTransparency(True)
            bean.reparentTo(self.tramp)
            bean.stash()

        self.beans[(-1)].setScale(8.0)

    def loadCollision(self):
        collTube = CollisionTube(0.0, 0.0, self.z, 0.0, 0.0, self.z, 5.4)
        collTube.setTangible(True)
        self.trampolineCollision = CollisionNode(
            self.uniqueName('TrampolineCollision'))
        self.trampolineCollision.addSolid(collTube)
        self.trampolineCollision.setCollideMask(OTPGlobals.CameraBitmask
                                                | OTPGlobals.WallBitmask)
        self.trampolineCollisionNP = self.tramp.attachNewNode(
            self.trampolineCollision)
        collSphere = CollisionSphere(0.0, 0.0, self.z, 7.0)
        collSphere.setTangible(False)
        self.trampolineTrigger = CollisionNode(
            self.uniqueName('TrampolineTrigger'))
        self.trampolineTrigger.addSolid(collSphere)
        self.trampolineTrigger.setIntoCollideMask(OTPGlobals.WallBitmask)
        self.trampolineTriggerNP = self.tramp.attachNewNode(
            self.trampolineTrigger)
        self.accept('enter%s' % self.uniqueName('TrampolineTrigger'),
                    self.onTrampolineTrigger)

    def loadGUI(self):
        self.gui = loader.loadModel('phase_13/models/parties/trampolineGUI')
        self.gui.reparentTo(base.a2dTopLeft)
        self.gui.setPos(0.115, 0, -1)
        self.gui.hide()
        self.toonIndicator = self.gui.find('**/trampolineGUI_MovingBar')
        self.gui.find('**/trampolineGUI_GreenJellyBean').hide()
        self.token_gui = loader.loadModel(
            'phase_6/models/gui/ttr_m_tf_gui_tokens')
        jumpLineLocator = self.gui.find('**/jumpLine_locator')
        self.guiBean = self.token_gui.find('**/token')
        self.token_gui.find('**/token').stash()
        self.guiBean.setScale(0.1)
        self.guiBeans = [
            self.guiBean.instanceUnderNode(jumpLineLocator,
                                           self.uniqueName('guiBean%d' % i))
            for i in range(self.numJellyBeans)
        ]
        self.guiBeans[(-1)].setScale(1.5)
        heightTextNode = TextNode(
            self.uniqueName('TrampolineActivity.heightTextNode'))
        heightTextNode.setFont(ToontownGlobals.getSignFont())
        heightTextNode.setAlign(TextNode.ALeft)
        heightTextNode.setText('0.0')
        heightTextNode.setShadow(0.05, 0.05)
        heightTextNode.setShadowColor(0.0, 0.0, 0.0, 1.0)
        heightTextNode.setTextColor(1.0, 1.0, 1.0, 1.0)
        self.heightText = jumpLineLocator.attachNewNode(heightTextNode)
        self.heightText.setX(0.15)
        self.heightText.setScale(0.1)
        self.heightText.setAlphaScale(0.0)
        self.quitEarlyButtonModels = loader.loadModel(
            'phase_3.5/models/gui/inventory_gui')
        quitEarlyUp = self.quitEarlyButtonModels.find('**//InventoryButtonUp')
        quitEarlyDown = self.quitEarlyButtonModels.find(
            '**/InventoryButtonDown')
        quitEarlyRollover = self.quitEarlyButtonModels.find(
            '**/InventoryButtonRollover')
        self.quitEarlyButton = DirectButton(
            parent=base.a2dTopRight,
            relief=None,
            text=TTLocalizer.PartyTrampolineQuitEarlyButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -0.23),
            text_scale=0.7,
            image=(quitEarlyUp, quitEarlyDown, quitEarlyRollover),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(-0.183, 0, -0.4),
            scale=0.09,
            command=self.leaveTrampoline)
        self.quitEarlyButton.stash()
        self.flashText = OnscreenText(text='',
                                      pos=(0.0, -0.45),
                                      scale=0.2,
                                      fg=(1.0, 1.0, 0.65, 1.0),
                                      align=TextNode.ACenter,
                                      font=ToontownGlobals.getSignFont(),
                                      mayChange=True)
        self.timer = PartyUtils.getNewToontownTimer()
        self.timer.posInTopRightCorner()
        return

    def loadSounds(self):
        self.jellyBeanSound = base.loader.loadSfx(
            'phase_4/audio/sfx/sparkly.ogg')
        self.boingSound = base.loader.loadSfx(
            'phase_4/audio/sfx/target_trampoline_2.ogg')
        self.whistleSound = base.loader.loadSfx(
            'phase_4/audio/sfx/AA_sound_whistle.ogg')

    def loadIntervals(self):
        def prepareHeightText():
            self.heightText.node().setText(
                TTLocalizer.PartyTrampolineGetHeight % int(self.toon.getZ()))
            self.heightText.setZ(self.indicatorFactor * self.toon.getZ() +
                                 self.heightTextOffset)

        self.heightTextInterval = Sequence(
            Func(prepareHeightText),
            LerpFunc(self.heightText.setAlphaScale,
                     fromData=1.0,
                     toData=0.0,
                     duration=1.0))

    def unload(self):
        DistributedToonFestTrampolineActivity.notify.debug('unload')
        if self.hopOnAnim and self.hopOnAnim.isPlaying():
            self.hopOnAnim.finish()
        if self.hopOffAnim and self.hopOffAnim.isPlaying():
            self.hopOffAnim.finish()
        if self.beginRoundInterval and self.beginRoundInterval.isPlaying():
            self.beginRoundInterval.finish()
        if self.flashTextInterval and self.flashTextInterval.isPlaying():
            self.flashTextInterval.finish()
        if self.heightTextInterval and self.heightTextInterval.isPlaying():
            self.heightTextInterval.finish()
        self.timer.stop()
        DistributedPartyActivity.unload(self)
        taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask'))
        taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask'))
        self.ignoreAll()
        del self.heightTextInterval
        del self.beginRoundInterval
        del self.hopOnAnim
        del self.hopOffAnim
        del self.flashTextInterval
        if hasattr(self, 'beanAnims'):
            self.cleanupJellyBeans()
        self.quitEarlyButton.destroy()
        del self.quitEarlyButton
        del self.gui
        del self.activityFSM
        del self.animFSM

    def setBestHeightInfo(self, toonName, height):
        self.bestHeightInfo = (toonName, height)
        DistributedToonFestTrampolineActivity.notify.debug(
            '%s has the best height of %d' % (toonName, height))
        if height > 0:
            self.setSignNote(TTLocalizer.PartyTrampolineBestHeight %
                             self.bestHeightInfo)
        else:
            self.setSignNote(TTLocalizer.PartyTrampolineNoHeightYet)

    def leaveTrampoline(self):
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            self._showFlashMessage(TTLocalizer.PartyTrampolineTimesUp)
            self.leavingTrampoline = True
            self.timer.reset()
            self.trampB = self.leavingTrampB
            self.ignore('control')
            self.quitEarlyButton.stash()
            self.gui.hide()
        return

    def requestAnim(self, request):
        self.animFSM.request(request)

    def b_requestAnim(self, request):
        self.requestAnim(request)
        self.sendUpdate('requestAnim', [request])

    def requestAnimEcho(self, request):
        if self.toon != None and self.toon.doId != base.localAvatar.doId:
            self.requestAnim(request)
        return

    def removeBeans(self, beansToRemove):
        for i in beansToRemove:
            height, bean, guiBean, beanAnim = self.beanDetails[i]
            guiBean.stash()
            if i in self.beansToCollect:
                self.beansToCollect.remove(i)
            else:
                self.notify.warning(
                    'removeBeans avoided a crash, %d not in self.beansToCollect'
                    % i)
            self.poofBean(bean, beanAnim)

    def b_removeBeans(self, beansToRemove):
        self.removeBeans(beansToRemove)
        self.sendUpdate('removeBeans', [beansToRemove])

    def removeBeansEcho(self, beansToRemove):
        if self.toon != None and self.toon.doId != base.localAvatar.doId:
            self.removeBeans(beansToRemove)
        return

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny)
        base.cr.playGame.getPlace().fsm.request('walk')

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny)

    def setState(self, newState, timestamp):
        DistributedToonFestTrampolineActivity.notify.debug(
            'setState( newState=%s, ... )' % newState)
        DistributedPartyActivity.setState(self, newState, timestamp)
        self.activityFSM.request(newState)

    def startIdle(self):
        DistributedToonFestTrampolineActivity.notify.debug('startIdle')

    def finishIdle(self):
        DistributedToonFestTrampolineActivity.notify.debug('finishIdle')

    def startRules(self):
        DistributedToonFestTrampolineActivity.notify.debug('startRules')
        if self.doJellyBeans:
            self.setupJellyBeans()
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            self.acquireToon()
        return

    def startActive(self):
        DistributedToonFestTrampolineActivity.notify.debug('startActive')
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            base.setCellsAvailable(base.bottomCells, True)
            self.accept('arrow_left', self.onLeft)
            self.accept('arrow_left-up', self.onLeftUp)
            self.accept('arrow_right', self.onRight)
            self.accept('arrow_right-up', self.onRightUp)
            self.beginRoundInterval = Sequence(
                Func(self._showFlashMessage, TTLocalizer.PartyTrampolineReady),
                Wait(1.2),
                Func(self.flashMessage, TTLocalizer.PartyTrampolineGo),
                Func(self.beginRound))
            self.beginRoundInterval.start()
        return

    def finishActive(self):
        DistributedToonFestTrampolineActivity.notify.debug('finishActive')
        if self.doJellyBeans:
            self.cleanupJellyBeans()

    def setupJellyBeans(self):
        self.beanAnims = []
        self.beansToCollect = []
        self.beanDetails = []
        self.numBeansCollected = 0
        for i in range(self.numJellyBeans):
            bean = self.beans[i]
            guiBean = self.guiBeans[i]
            height = self.jellyBeanPositions[i]
            if self.toon:
                avHeight = self.toon.getHeight()
            else:
                avHeight = 3
            bean.setZ(height + self.toon.getHeight() + self.beanOffset)
            guiBean.setZ(height * self.indicatorFactor + self.guiBeanOffset)
            bean.setH(0.0)
            bean.unstash()
            guiBean.unstash()
            beanAnim = bean.hprInterval(
                1.5, VBase3((i % 2 * 2 - 1) * 360.0, 0.0, 0.0))
            beanAnim.loop()
            self.beanAnims.append(beanAnim)
            self.beanDetails.append((height, bean, guiBean, beanAnim))

        self.beansToCollect = range(self.numJellyBeans)

    def cleanupJellyBeans(self):
        for bean in self.beans:
            bean.stash()

        for guiBean in self.guiBeans:
            guiBean.stash()

        if hasattr(self, 'beanAnims'):
            for beanAnim in self.beanAnims:
                beanAnim.finish()

            del self.beanAnims
            del self.beansToCollect

    def beginRound(self):
        base.playSfx(self.whistleSound)
        self.timer.setTime(PartyGlobals.TrampolineDuration)
        self.timer.countdown(PartyGlobals.TrampolineDuration)
        self.timer.show()
        self.gui.show()
        self.quitEarlyButton.unstash()
        self.notify.debug('Accepting contorl')
        self.accept('control', self.onJump)
        self.notify.debug('setting simulate step to true')
        self.doSimulateStep = True

    def acquireToon(self):
        self.toon.disableSmartCameraViews()
        self.toon.stopUpdateSmartCamera()
        camera.wrtReparentTo(render)
        self.toon.dropShadow.reparentTo(hidden)
        self.toon.startPosHprBroadcast(period=0.2)
        self.toonAcceleration = 0.0
        self.toonVelocity = 0.0
        self.topHeight = 0.0
        self.trampB = self.normalTrampB
        self.leavingTrampoline = False
        self.hopOnAnim = Sequence(
            Func(self.toon.b_setAnimState, 'jump', 1.0), Wait(0.4),
            PartyUtils.arcPosInterval(0.75, self.toon, Point3(0.0, 0.0, 3.6),
                                      5.0, self.tramp), Func(self.postHopOn))
        self.hopOnAnim.start()

    def postHopOn(self):
        self.toon.setH(self.toon.getH() + 90.0)
        self.toon.dropShadow.reparentTo(self.surface)
        self.timeLeftToSimulate = 0.0
        self.doSimulateStep = False
        taskMgr.add(self.updateTask,
                    self.uniqueName('TrampolineActivity.updateTask'))
        base.setCellsAvailable(base.leftCells, False)
        base.setCellsAvailable(base.bottomCells, False)
        DistributedPartyActivity.startRules(self)

    def releaseToon(self):
        self._hideFlashMessage()
        self.ignore('arrow_left')
        self.ignore('arrow_left-up')
        self.ignore('arrow_right')
        self.ignore('arrow_right-up')
        taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask'))
        self.hopOffAnim = Sequence(
            self.toon.hprInterval(0.5,
                                  VBase3(-90.0, 0.0, 0.0),
                                  other=self.tramp),
            Func(self.toon.b_setAnimState, 'jump', 1.0),
            Func(self.toon.dropShadow.reparentTo, hidden), Wait(0.4),
            PartyUtils.arcPosInterval(0.75, self.toon, self.hopOffPos, 5.0,
                                      self.tramp), Func(self.postHopOff))
        self.hopOffAnim.start()

    def postHopOff(self):
        base.setCellsAvailable(base.leftCells, True)
        self.timer.stop()
        self.timer.hide()
        self.toon.dropShadow.reparentTo(self.toon.getShadowJoint())
        self.toon.dropShadow.setAlphaScale(1.0)
        self.toon.dropShadow.setScale(1.0)
        self.b_requestAnim('Off')
        camera.reparentTo(base.localAvatar)
        base.localAvatar.startUpdateSmartCamera()
        base.localAvatar.enableSmartCameraViews()
        base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
        place = base.cr.playGame.getPlace()
        if self.doJellyBeans:
            self.sendUpdate('awardTokens',
                            [self.numBeansCollected,
                             int(self.topHeight)])
            if int(self.topHeight) > self.bestHeightInfo[1]:
                self.sendUpdate('reportHeightInformation',
                                [int(self.topHeight)])
        self.d_toonExitDemand()

    def onTrampolineTrigger(self, collEntry):
        if self.activityFSM.state == 'Idle' and self.toon == None and base.cr.playGame.getPlace(
        ).fsm.getCurrentState().getName() == 'walk':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        else:
            self.flashMessage(TTLocalizer.PartyTrampolineActivityOccupied,
                              duration=2.0)
        return

    def onJump(self):
        self.notify.debug('got onJump')
        if self.toon != None and self.toon.getZ() < self.trampHeight:
            self.toonJumped = True
            self.b_requestAnim('Jump')
        else:
            self.notify.debug('z is less than tramp height')
        return

    def onLeft(self):
        self.turnLeft = True

    def onLeftUp(self):
        self.turnLeft = False

    def onRight(self):
        self.turnRight = True

    def onRightUp(self):
        self.turnRight = False

    def handleToonJoined(self, toonId):
        DistributedToonFestTrampolineActivity.notify.debug('handleToonJoined')
        self.toon = self.getAvatar(toonId)
        if self.toon != None and not self.toon.isEmpty():
            self.oldJumpSquatPlayRate = self.toon.getPlayRate('jump-squat')
            self.oldJumpLandPlayRate = self.toon.getPlayRate('jump-land')
            self.toon.setPlayRate(2.5, 'jump-squat')
            self.toon.setPlayRate(2.0, 'jump-land')
            self.turnLeft = False
            self.turnRight = False
            self.activityFSM.request('Rules')
            if self.toon.doId != base.localAvatar.doId:
                taskMgr.add(
                    self.remoteUpdateTask,
                    self.uniqueName('TrampolineActivity.remoteUpdateTask'))
        else:
            self.notify.warning('handleToonJoined could not get toon %d' %
                                toonId)
        return

    def handleToonExited(self, toonId):
        DistributedToonFestTrampolineActivity.notify.debug('handleToonExited')
        if self.toon != None:
            if self.toon.doId != base.localAvatar.doId:
                taskMgr.remove(
                    self.uniqueName('TrampolineActivity.remoteUpdateTask'))
            self.surface.setZ(3.6)
            self.toon.setPlayRate(self.oldJumpSquatPlayRate, 'jump-squat')
            self.toon.setPlayRate(self.oldJumpLandPlayRate, 'jump-land')
            self.toon = None
        return

    def handleToonDisabled(self, toonId):
        DistributedToonFestTrampolineActivity.notify.debug(
            'handleToonDisabled')
        DistributedToonFestTrampolineActivity.notify.debug('avatar ' +
                                                           str(toonId) +
                                                           ' disabled')
        if base.localAvatar.doId == toonId:
            self.releaseToon()

    def handleRulesDone(self):
        self.sendUpdate('toonReady')
        self.finishRules()

    def getTitle(self):
        return TTLocalizer.ToonFestTrampolineTokenTitle

    def getInstructions(self):
        return TTLocalizer.ToonFestTrampolineActivityInstructions

    def updateTask(self, task):
        z = self.toon.getZ()
        dt = globalClock.getDt()
        if self.doSimulateStep:
            self.timeLeftToSimulate += dt
            while self.timeLeftToSimulate >= self.stepDT:
                z, a = self.simulateStep(z)
                self.timeLeftToSimulate -= self.stepDT

        self.toon.setZ(z)
        if z <= self.trampHeight:
            self.surface.setZ(3.6)
        else:
            self.surface.setZ(3.6)
        self.toonIndicator.setZ((z - self.trampHeight) * self.indicatorFactor)
        if self.turnLeft:
            self.toon.setH(self.toon.getH() + self.turnFactor * dt)
        if self.turnRight:
            self.toon.setH(self.toon.getH() - self.turnFactor * dt)
        currentPos = base.camera.getPos(self.toon)
        vec = self.targetCameraPos - currentPos
        newPos = currentPos + vec * (dt * self.cameraSpeed)
        base.camera.setPos(self.toon, newPos)
        base.camera.lookAt(self.toon)
        if self.leavingTrampoline and z < self.trampHeight and abs(a) < 0.1:
            self.releaseToon()
        return Task.cont

    def simulateStep(self, z):
        if z >= self.trampHeight:
            a = self.g
            self.toonJumped = False
        else:
            a = self.g + self.trampK * (self.trampHeight -
                                        z) - self.trampB * self.toonVelocity
            if self.toonJumped:
                if self.lastPeak > self.earlyJumpThreshold or self.toonVelocity >= -300000.0:
                    a += self.jumpBoost
                    if self.lastPeak < self.beginningBoostThreshold:
                        a += self.beginningBoost
        lastVelocity = self.toonVelocity
        self.toonVelocity += a * self.stepDT
        if lastVelocity > 0.0 and self.toonVelocity <= 0.0:
            topOfJump = True
            bottomOfJump = False
        else:
            if lastVelocity < 0.0 and self.toonVelocity >= 0.0:
                topOfJump = False
                bottomOfJump = True
            else:
                topOfJump = False
                bottomOfJump = False
        newZ = z + self.toonVelocity * self.stepDT
        if newZ > self.topHeight:
            self.topHeight = newZ
            if self.doJellyBeans:
                self.collectJellyBeans(newZ)
        if topOfJump:
            self.lastPeak = newZ
            if newZ >= self.minHeightForText:
                self.heightTextInterval.start()
        if topOfJump:
            if newZ > self.trampHeight + 20.0:
                self.b_requestAnim('Falling')
            elif self.animFSM.state == 'Jump':
                self.b_requestAnim('Falling')
        if newZ <= self.trampHeight and z > self.trampHeight:
            if self.animFSM.state == 'Falling':
                self.b_requestAnim('Land')
            elif self.animFSM.state != 'Neutral':
                self.b_requestAnim('Neutral')
        if bottomOfJump and a > self.boingThreshold:
            base.playSfx(self.boingSound)
        return (newZ, a)

    def collectJellyBeans(self, z):
        beansToRemove = []
        for i in self.beansToCollect:
            height = self.beanDetails[i][0]
            if height <= z:
                beansToRemove.append(i)

        if len(beansToRemove) > 0:
            base.playSfx(self.jellyBeanSound)
            self.numBeansCollected += len(beansToRemove)
            self.b_removeBeans(beansToRemove)

    def remoteUpdateTask(self, task):
        if self.toon != None and not self.toon.isEmpty():
            z = self.toon.getZ()
            if z <= self.trampHeight:
                self.surface.setZ(3.6)
            else:
                self.surface.setZ(3.6)
        return Task.cont

    def poofBean(self, bean, beanAnim):
        if bean == None:
            self.notify.warning(
                'poofBean, returning immediately as bean is None')
            return
        if bean.isEmpty():
            self.notify.warning(
                'poofBean, returning immediately as bean is empty')
            return
        currentAlpha = bean.getColorScale()[3]
        currentScale = bean.getScale()
        poofAnim = Sequence(
            Parallel(
                LerpFunc(bean.setAlphaScale,
                         fromData=currentAlpha,
                         toData=0.0,
                         duration=0.25),
                LerpFunc(bean.setScale,
                         fromData=currentScale,
                         toData=currentScale * 5.0,
                         duration=0.25)), Func(bean.stash),
            Func(beanAnim.finish), Func(bean.setAlphaScale, currentAlpha),
            Func(bean.setScale, currentScale))
        poofAnim.start()
        return

    def _showFlashMessage(self, message):
        if self.isDisabled():
            return
        if self.flashTextInterval is not None and self.flashTextInterval.isPlaying(
        ):
            self.flashTextInterval.finish()
        self.flashText.setText(message)
        self.flashText.setAlphaScale(1.0)
        self.flashText.unstash()
        return

    def _hideFlashMessage(self, duration=0.0):
        if self.isDisabled():
            pass
        self.flashTextInterval = Sequence(
            Wait(duration),
            LerpFunc(self.flashText.setAlphaScale,
                     fromData=1.0,
                     toData=0.0,
                     duration=1.0), Func(self.flashText.stash))
        self.flashTextInterval.start()

    def flashMessage(self, message, duration=0.5):
        self._showFlashMessage(message)
        self._hideFlashMessage(duration)
    def getDropIval(self, x, y, dropObjName, generation, num):
        objType = PartyGlobals.Name2DropObjectType[dropObjName]
        id = (generation, num)
        dropNode = hidden.attachNewNode('catchDropNode%s' % (id, ))
        dropNode.setPos(x, y, 0)
        shadow = self.dropShadow.copyTo(dropNode)
        shadow.setZ(PartyGlobals.CatchDropShadowHeight)
        shadow.setColor(1, 1, 1, 1)
        object = self.getObjModel(dropObjName)
        object.reparentTo(hidden)
        if dropObjName in ['watermelon', 'anvil']:
            objH = object.getH()
            absDelta = {'watermelon': 12, 'anvil': 15}[dropObjName]
            delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta
            newH = objH + delta
        else:
            newH = self.randomNumGen.random() * 360.0
        object.setH(newH)
        sphereName = 'FallObj%s' % (id, )
        radius = self.ObjRadius
        if objType.good:
            radius *= lerp(1.0, 1.3, 0.5)
        collSphere = CollisionSphere(0, 0, 0, radius)
        collSphere.setTangible(0)
        collNode = CollisionNode(sphereName)
        collNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        collNode.addSolid(collSphere)
        collNodePath = object.attachNewNode(collNode)
        collNodePath.hide()
        if self.ShowObjSpheres:
            collNodePath.show()
        catchEventName = 'ltCatch' + sphereName

        def eatCollEntry(forward, collEntry):
            forward()

        self.accept(
            catchEventName,
            Functor(eatCollEntry, Functor(self.__handleCatch, id[0], id[1])))

        def cleanup(self=self, dropNode=dropNode, id=id, event=catchEventName):
            self.ignore(event)
            dropNode.removeNode()

        duration = objType.fallDuration
        onscreenDuration = objType.onscreenDuration
        targetShadowScale = 0.3
        if self.trickShadows:
            intermedScale = targetShadowScale * (self.OffscreenTime /
                                                 self.BaselineDropDuration)
            shadowScaleIval = Sequence(
                LerpScaleInterval(shadow,
                                  self.OffscreenTime,
                                  intermedScale,
                                  startScale=0))
            shadowScaleIval.append(
                LerpScaleInterval(shadow,
                                  duration - self.OffscreenTime,
                                  targetShadowScale,
                                  startScale=intermedScale))
        else:
            shadowScaleIval = LerpScaleInterval(shadow,
                                                duration,
                                                targetShadowScale,
                                                startScale=0)
        targetShadowAlpha = 0.4
        shadowAlphaIval = LerpColorScaleInterval(
            shadow,
            self.OffscreenTime,
            Point4(1, 1, 1, targetShadowAlpha),
            startColorScale=Point4(1, 1, 1, 0))
        shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
        if self.useGravity:

            def setObjPos(t, objType=objType, object=object):
                z = objType.trajectory.calcZ(t)
                object.setZ(z)

            setObjPos(0)
            dropIval = LerpFunctionInterval(setObjPos,
                                            fromData=0,
                                            toData=onscreenDuration,
                                            duration=onscreenDuration)
        else:
            startPos = Point3(0, 0, self.MinOffscreenHeight)
            object.setPos(startPos)
            dropIval = LerpPosInterval(object,
                                       onscreenDuration,
                                       Point3(0, 0, 0),
                                       startPos=startPos,
                                       blendType='easeIn')
        ival = Sequence(Func(Functor(dropNode.reparentTo, self.root)),
                        Parallel(
                            Sequence(
                                WaitInterval(self.OffscreenTime),
                                Func(Functor(object.reparentTo, dropNode)),
                                dropIval), shadowIval),
                        Func(cleanup),
                        name='drop%s' % (id, ))
        if objType == PartyGlobals.Name2DropObjectType['anvil']:
            ival.append(Func(self.playAnvil))
        return ival
Example #49
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')
        return

    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)
        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)
        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.ogg')
        self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.ogg')
        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
        return

    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':
            downAnimControl = self.actor.getAnimControl('down')
            self.actor.pose('down', downAnimControl.getNumFrames() - 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)
        return None

    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)
        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.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):
        if self.netTimeSentToStartByHit < timestamp:
            self.__showSplat(position, direction, hot)
            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            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()
        return

    def __showSplat(self, position, direction, hot=False):
        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(PartyGlobals.CogActivitySplatColors[0])
            if self.currentFacing > 0.0:
                facing = 'HitFront'
            else:
                facing = 'HitBack'
        else:
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1])
            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,
                                 fromData=1.0,
                                 toData=0.0,
                                 blendType='easeOut'))),
                Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return

    def showHitScore(self, number, scale=1):
        if number <= 0:
            return
        if self.hpText:
            self.hideHitScore()
        self.HpTextGenerator.setFont(ToontownGlobals.getSignFont())
        if number < 0:
            self.HpTextGenerator.setText(str(number))
        else:
            self.HpTextGenerator.setText('+' + str(number))
        self.HpTextGenerator.clearShadow()
        self.HpTextGenerator.setAlign(TextNode.ACenter)
        r = 1
        g = 1
        b = 0
        a = 1
        self.HpTextGenerator.setTextColor(r, g, b, a)
        self.hpTextNode = self.HpTextGenerator.generate()
        self.hpText = render.attachNewNode(self.hpTextNode)
        self.hpText.setScale(scale)
        self.hpText.setBillboardPointEye()
        self.hpText.setBin('fixed', 100)
        self.hpText.setPos(self.root, 0, 0, self.height / 2)
        seq = Sequence(
            self.hpText.posInterval(
                0.25,
                Point3(self.root.getX(render), self.root.getY(render),
                       self.root.getZ(render) + self.height + 1.0),
                blendType='easeOut'), Wait(0.25),
            self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)),
            Func(self.hideHitScore))
        seq.start()

    def hideHitScore(self):
        if self.hpText:
            taskMgr.remove('PartyCogHpText' + str(self.id))
            self.hpText.removeNode()
            self.hpText = None
        return

    def getHeadLocation(self):
        self.actor.getJoints(jointName='head')[0].getNetTransform(
            self.temp_transform)
        self.head_locator.setMat(self.temp_transform)
        return self.head_locator.getZ(self.root)