def loadAssets(self):
     self.beltModel = NodePath('beltModel')
     self.beltModel.reparentTo(self.boss.geom)
     self.startLocator = self.boss.geom.find('**/conveyer_belt_start_%d' % (self.index + 1))
     self.endLocator = self.boss.geom.find('**/conveyer_belt_end_%d' % (self.index + 1))
     center = (self.startLocator.getPos() + self.endLocator.getPos()) / 2.0
     self.beltHeight = center.getZ()
     self.beltHeight += 0.1
     center.setZ(0)
     self.beltLength = (self.endLocator.getPos() - self.startLocator.getPos()).length()
     self.distBetweenFoodNodes = self.beltLength / self.NumFoodNodes
     self.notify.debug('setting beltModelPos to %s' % center)
     self.beltModel.setPos(center)
     self.setupFoodNodes()
     self.setupFoodIvals()
     self.setupToonupIvals()
     if self.index == 0:
         self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt1_model')
     else:
         self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt2_model')
     if self.beltActorModel:
         self.beltActor = Actor.Actor(self.beltActorModel)
         if self.index == 0:
             self.beltActor.loadAnims({'idle': 'phase_12/models/bossbotHQ/food_belt1'})
         else:
             self.beltActor.loadAnims({'idle': 'phase_12/models/bossbotHQ/food_belt2'})
         self.beltActor.reparentTo(render)
         self.beltActor.setPlayRate(self.BeltActorPlayRate, 'idle')
         mesh = self.beltActor.find('**/mesh_tide1')
         joint = self.beltActor.find('**/uvj_WakeWhiteTide1')
         mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.beltActor)
         self.beltActor.setPos(self.startLocator.getPos())
     self.beltSound = base.loadSfx('phase_12/audio/sfx/CHQ_FACT_conveyor_belt.wav')
     self.beltSound.setLoop(1)
     self.beltSoundInterval = SoundInterval(self.beltSound, node=self.beltModel, listenerNode=base.localAvatar, seamlessLoop=True, volume=0.25, cutOff=100)
 def loadAssets(self):
     self.tableGroup = loader.loadModel("phase_12/models/bossbotHQ/BanquetTableChairs")
     tableLocator = self.boss.geom.find("**/TableLocator_%d" % (self.index + 1))
     if tableLocator.isEmpty():
         self.tableGroup.reparentTo(render)
         self.tableGroup.setPos(0, 75, 0)
     else:
         self.tableGroup.reparentTo(tableLocator)
     self.tableGeom = self.tableGroup.find("**/Geometry")
     self.setupDiners()
     self.setupChairCols()
     self.squirtSfx = loader.loadSfx("phase_4/audio/sfx/AA_squirt_seltzer_miss.mp3")
     self.hitBossSfx = loader.loadSfx("phase_5/audio/sfx/SA_watercooler_spray_only.mp3")
     self.hitBossSoundInterval = SoundInterval(self.hitBossSfx, node=self.boss, volume=1.0)
     self.serveFoodSfx = loader.loadSfx("phase_4/audio/sfx/MG_sfx_travel_game_bell_for_trolley.mp3")
     self.pitcherMoveSfx = base.loadSfx("phase_4/audio/sfx/MG_cannon_adjust.mp3")
Beispiel #3
0
 def showSplat(self):
     if self.kaboomTrack and self.kaboomTrack.isPlaying():
         self.kaboomTrack.finish()
     self.splat.reparentTo(render)
     self.splat.setPos(self.suit.getPos())
     self.splat.setZ(3)
     self.kaboomTrack = Parallel(
         SoundInterval(self.pieHitSound, volume=1.0),
         Sequence(
             Func(self.splat.showThrough),
             LerpScaleInterval(self.splat,
                               duration=0.5,
                               scale=1.75,
                               startScale=Point3(0.1, 0.1, 0.1),
                               blendType='easeOut'), Func(self.splat.hide)))
     self.kaboomTrack.start()
Beispiel #4
0
 def startEntity(self):
     if self.entityTrack:
         self.entityTrack.pause()
         self.entityTrack = None
     if self.getLocation():
         x, y, z = self.getLocation()
         self.gag.setPos(x, y, z - 0.45)
     if not self.gag:
         self.build()
     self.gag.setScale(GagGlobals.PNT3NEAR0)
     self.gag.reparentTo(render)
     LerpScaleInterval(self.gag, 1.2, Point3(1.7, 1.7, 1.7)).start()
     track = Sequence(Wait(0.7))
     if self.isSafeLocation(self.gag):
         self.entities.append(self.gag)
         # Let's suck the closest suit into our trap.
         suits = []
         for obj in base.cr.doId2do.values():
             if obj.__class__.__name__ == "DistributedSuit":
                 if obj.getPlace() == base.localAvatar.zoneId:
                     suits.append(obj)
         nearestCog = self.getClosestObject(self.gag, suits)
         suits = []
         if nearestCog and nearestCog.getDistance(
                 self.gag) <= self.minSafeDistance:
             self.onActivate(self.gag, nearestCog)
         else:
             track.append(
                 SoundInterval(self.hitSfx, duration=0.5, node=self.gag))
             base.taskMgr.doMethodLater(self.lifeTime,
                                        self.__clearEntity,
                                        'Clear Entity',
                                        extraArgs=[self.gag],
                                        appendTask=True)
             self.gag = None
     else:
         # We're too close to another trap, let's clear them out.
         self.cleanupGag()
         for ent in [
                 self.gag,
                 self.getClosestObject(self.gag, self.entities)
         ]:
             if ent:
                 self.__doDustClear(ent)
     track.append(Func(self.completeTrap))
     track.start()
     self.entityTrack = track
 def serveFood(self, food, chairIndex):
     self.removeFoodModel(chairIndex)
     serviceLoc = self.serviceLocs.get(chairIndex)
     if not food or food.isEmpty():
         foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood')
         foodModel.setScale(ToontownGlobals.BossbotFoodModelScale)
         foodModel.reparentTo(serviceLoc)
     else:
         food.wrtReparentTo(serviceLoc)
         tray = food.find('**/tray')
         if not tray.isEmpty():
             tray.hide()
         ivalDuration = 1.5
         foodMoveIval = Parallel(SoundInterval(self.serveFoodSfx, node=food), ProjectileInterval(food, duration=ivalDuration, startPos=food.getPos(serviceLoc), endPos=serviceLoc.getPos(serviceLoc)), LerpHprInterval(food, ivalDuration, Point3(0, -360, 0)))
         intervalName = 'serveFood-%d-%d' % (self.index, chairIndex)
         foodMoveIval.start()
         self.activeIntervals[intervalName] = foodMoveIval
Beispiel #6
0
 def showGrab(self):
     self.nodePath.hide()
     self.collNodePath.hide()
     # disable collisions
     self.collNode.setIntoCollideMask(BitMask32(0))
     if self.penalty:
         self.track = Parallel(
             SoundInterval(self.penaltyGrabSound),
             Sequence(
                 Func(self.kaboom.showThrough),
                 LerpScaleInterval(self.kaboom,
                                   duration=0.5,
                                   scale=Point3(10, 10, 10),
                                   blendType='easeOut'),
                 Func(self.kaboom.hide),
             ))
         self.track.start()
Beispiel #7
0
    def startEntity(self, entity, cog):
        geyserHold = 1.5
        scaleUpPoint = Point3(1.8, 1.8, 1.8)
        geyserWater = entity[0]
        geyserMound = entity[1]

        def showEntity(entity, cog):
            entity.reparentTo(render)
            entity.setPos(cog.getPos())

        def __getGeyserTrack():
            track = Sequence(Func(showEntity, geyserMound, cog), Func(showEntity, geyserWater, cog), LerpScaleInterval(geyserWater, 1.0, scaleUpPoint, startScale=GagGlobals.PNT3NEAR0), Wait(0.5 * geyserHold), LerpScaleInterval(geyserWater, 0.5, GagGlobals.PNT3NEAR0, startScale=scaleUpPoint), LerpScaleInterval(geyserMound, 0.5, GagGlobals.PNT3NEAR0), Func(geyserWater.removeNode), Func(geyserMound.removeNode), Func(self.removeEntity, entity))
            return track

        def __getCogTrack():

            def handleHit():
                if self.isLocal():
                    cog.sendUpdate('hitByGag', [self.getID()])

            startPos = cog.getPos(render)
            cogFloat = Point3(0, 0, 14)
            cogEndPos = Point3(startPos[0] + cogFloat[0], startPos[1] + cogFloat[1], startPos[2] + cogFloat[2])
            suitType = cog.suitPlan.getSuitType()
            if suitType == 'A':
                startFlailFrame = 16
                endFlailFrame = 16
            else:
                startFlailFrame = 15
                endFlailFrame = 15
            track = Sequence()
            track.append(Func(cog.d_disableMovement))
            track.append(Wait(0.5))
            slipIval = Sequence(ActorInterval(cog, 'slip-backward', playRate=0.5, startFrame=0, endFrame=startFlailFrame - 1), Func(cog.pingpong, 'slip-backward', fromFrame=startFlailFrame, toFrame=endFlailFrame), Wait(0.5), Parallel(ActorInterval(cog, 'slip-backward', playRate=1.0, startFrame=endFlailFrame), Func(cog.startRay), Func(handleHit)))
            slipUp = LerpPosInterval(cog, 1.1, cogEndPos, startPos=startPos, fluid=1)
            slipDn = LerpPosInterval(cog, 0.6, startPos, startPos=cogEndPos, fluid=1)
            geyserMotion = Sequence(slipUp, slipDn)
            track.append(Parallel(slipIval, geyserMotion))
            if cog.getHealth() - self.getDamage() <= 0:
                track.append(Func(cog.d_enableMovement))
            return track

        if entity and cog:
            track = Sequence()
            track.append(Parallel(SoundInterval(self.hitSfx, node=self.avatar), Parallel(__getGeyserTrack(), __getCogTrack())))
            track.start()
Beispiel #8
0
 def fireWater(self, origin, target):
     color = VBase4(0.75, 0.75, 1, 0.8)
     dScaleUp = 0.1
     dHold = 0.3
     dScaleDown = 0.1
     horizScale = 0.1
     vertScale = 0.1
     sprayTrack = self.getSprayTrack(color, origin, target, dScaleUp, dHold,
                                     dScaleDown, horizScale, vertScale)
     duration = self.squirtSfx.length()
     if sprayTrack.getDuration() < duration:
         duration = sprayTrack.getDuration()
     soundTrack = SoundInterval(self.squirtSfx,
                                node=self.waterPitcherModel,
                                duration=duration)
     self.fireTrack = Parallel(sprayTrack, soundTrack)
     self.fireTrack.start()
 def showKaboom(self):
     if self.kaboomTrack and self.kaboomTrack.isPlaying():
         self.kaboomTrack.finish()
     self.kaboom.reparentTo(render)
     self.kaboom.setPos(self.suit.getPos())
     self.kaboom.setZ(3)
     self.kaboomTrack = Parallel(
         SoundInterval(self.kaboomSound, volume=0.5),
         Sequence(
             Func(self.kaboom.showThrough),
             LerpScaleInterval(self.kaboom,
                               duration=0.5,
                               scale=Point3(10, 10, 10),
                               startScale=Point3(1, 1, 1),
                               blendType='easeOut'),
             Func(self.kaboom.hide)))
     self.kaboomTrack.start()
 def release(self):
     if self.isLocal():
         self.startTimeout()
         self.resetCrashEffect()
     LocationGag.release(self)
     self.build()
     self.isDropping = True
     actorTrack = LocationGag.getActorTrack(self)
     self.fallSoundInterval = LocationGag.getSoundTrack(self)
     if actorTrack:
         actorTrack.append(Func(self.startDrop))
         actorTrack.start()
         self.fallSoundInterval.append(
             Parallel(SoundInterval(self.fallSfx, node=self.avatar)))
         self.fallSoundInterval.start()
     if self.isLocal():
         base.localAvatar.sendUpdate('usedGag', [self.id])
    def createOnePlayerSequence(self, index, duration):
        numVotes = self.votes[index]
        direction = self.directions[index]

        def ticketTicker(t, label = self.entryList[index][direction + 1], startVotes = 0, endVotes = numVotes):
            label['text'] = str(int(t * endVotes + startVotes))

        track = Parallel()
        startVotes = 0
        for prev in range(index):
            if self.directions[prev] == direction:
                startVotes += self.votes[prev]

        def totalTicker(t, label = self.totalVotesLabels[direction], startVotes = startVotes, additionalVotes = numVotes):
            label['text'] = str(int(t * additionalVotes + startVotes))

        track.append(LerpFunc(totalTicker, duration=duration, name='countTotal %d' % index))
        if self.avVotesLabel.has_key(index):

            def avVotesTicker(t, label = self.avVotesLabel[index], startVotes = 0, endVotes = numVotes, direction = direction):
                oldValue = label['text']
                newValue = int(t * endVotes + startVotes)
                label['text'] = str(newValue)
                if not oldValue == label['text']:
                    if newValue:
                        if direction == 0:
                            self.upArrowSfx[self.curArrowSfxIndex].play()
                        else:
                            self.downArrowSfx[self.curArrowSfxIndex].play()
                            self.curArrowSfxIndex += 1
                        if self.curArrowSfxIndex >= len(self.upArrowSfx):
                            self.curArrowSfxIndex = 0

            label = self.avVotesLabel[index]
            track.append(Func(self.avVotesLabel[index].show, name='showName %d' % index))
            if self.avArrows.has_key(index):
                track.append(Func(self.avArrows[index].show, name='showArrow %d' % index))
            if direction == 0 and numVotes:
                pass
            elif direction == 1 and numVotes:
                pass
            else:
                track.append(SoundInterval(self.noVoteSfx))
            track.append(LerpFunc(avVotesTicker, duration=duration, name='countAvVotes %d' % index))
        return track
Beispiel #12
0
    def say(self, sentence, volume=0.5):
        self.stopSpeech()

        words = sentence.split(" ")
        self.speechTrack = Sequence()
        for i in range(len(words)):
            word = words[i].lower()

            if word == ",":
                word = "_comma"
            elif word == ".":
                word = "_period"

            if word in self.words:
                self.speechTrack.append(
                    SoundInterval(self.words[word], volume=volume))

        self.speechTrack.start()
Beispiel #13
0
 def release(self):
     if self.isLocal():
         self.startTimeout()
         self.resetCrashEffect()
     LocationGag.release(self)
     entity = self.build()
     self.isDropping = True
     self.buildTracks()
     actorTrack = LocationGag.getActorTrack(self)
     soundTrack = LocationGag.getSoundTrack(self)
     if actorTrack:
         actorTrack.append(Func(self.startDrop, entity))
         actorTrack.start()
         soundTrack.append(Parallel(SoundInterval(self.fallSfx)))
         soundTrack.start()
     entity.setPythonTag(SoundTrackName, soundTrack)
     if self.isLocal():
         base.localAvatar.sendUpdate('usedGag', [self.id])
Beispiel #14
0
    def turnDown(self, doInterval=DoIntervalDefault):
        """Turn up the card.

        doInterval -- if true do a sound and flip up animation
        
        """
        self.faceUp = 0
        if doInterval:
            self.clearFlipIval()
            self.flipIval = Parallel(
                LerpHprInterval(self, self.FlipTime, Vec3(0, 0, 180)),
                SoundInterval(self.turnDownSound,
                              node=self,
                              listenerNode=base.localAvatar,
                              cutOff=240))
            self.flipIval.start()
        else:
            self.setR(180)
Beispiel #15
0
    def turnUp(self, doInterval=DoIntervalDefault):
        """Turn up the card.

        doInterval -- if true do a sound and flip up animation
        
        """
        assert self.value != PlayingCardGlobals.Unknown
        self.faceUp = 1
        if doInterval:
            self.clearFlipIval()
            self.flipIval = Parallel(
                LerpHprInterval(self, self.FlipTime, Vec3(0, 0, 0)),
                SoundInterval(self.turnUpSound,
                              node=self,
                              listenerNode=base.localAvatar,
                              cutOff=240))
            self.flipIval.start()
        else:
            self.setR(0)
 def exitDoorEnterClosing(self, ts):
     bldgActor = self.getBuildingActor()
     leftDoor = bldgActor.controlJoint(None, 'modelRoot', 'def_left_door')
     if self.leftSwing:
         h = -100
     else:
         h = 100
     if not leftDoor.isEmpty():
         otherNP = self.getDoorNodePath()
         trackName = 'doorExitTrack-%d' % self.doId
         self.finishDoorExitTrack()
         self.doorExitTrack = Sequence(
             LerpHprInterval(
                 nodePath=leftDoor, duration=1.0, hpr=VBase3(
                     0, 0, 0), startHpr=VBase3(
                     h, 0, 0), blendType='easeInOut'), SoundInterval(
                 self.closeSfx, node=leftDoor), name=trackName)
         self.doorExitTrack.start(ts)
     return
    def damageCogsNearby(self, radius=None):
        if self.avatar.doId != base.localAvatar.doId:
            return
        if not radius:
            radius = self.soundRange
        suits = []
        for obj in base.cr.doId2do.values():
            if obj.__class__.__name__ in CIGlobals.SuitClasses:
                if obj.getPlace() == base.localAvatar.zoneId:
                    if obj.getDistance(self.avatar) <= radius:
                        if self.avatar.doId == base.localAvatar.doId:
                            suits.append(obj)

        def shouldContinue(suit, track):
            if suit.isDead():
                track.finish()

        for suit in suits:
            if self.name != CIGlobals.Opera:
                suit.sendUpdate('hitByGag', [self.getID()])
            else:
                breakEffect = ParticleEffect()
                breakEffect.loadConfig('phase_5/etc/soundBreak.ptf')
                breakEffect.setDepthWrite(0)
                breakEffect.setDepthTest(0)
                breakEffect.setTwoSided(1)
                suitTrack = Sequence()
                if suit.isDead():
                    return
                suitTrack.append(Wait(2.5))
                delayTime = random.random()
                suitTrack.append(Wait(delayTime + 2.0))
                suitTrack.append(Func(shouldContinue, suit, suitTrack))
                suitTrack.append(
                    Func(self.setPosFromOther, breakEffect, suit,
                         Point3(0, 0, 0)))
                suitTrack.append(SoundInterval(self.hitSfx, node=suit))
                suitTrack.append(
                    Func(suit.sendUpdate, 'hitByGag', [self.getID()]))
                suitTrack.start()

        suits = None
        return
Beispiel #18
0
    def __init__(self):
        x_coor, side = random.choice(
            ((0.553, "left"), (-0.553, "right"), (0, "top")))

        self._model = Actor(address("rocket1"))
        self._model.reparentTo(base.train.model)  # noqa: F821
        self._model.setPos(x_coor, -7, 0.5)

        self._smoke = ParticleEffect()
        self._smoke.loadConfig("effects/smoke_tail.ptf")
        self._smoke.start(self._model, render)  # noqa: F821

        path = Mopath.Mopath(objectToLoad=loader.loadModel(  # noqa: F821
            address("rocket_{}_path".format(side))))
        path.fFaceForward = True

        self._hiss_snd = base.sound_mgr.loadSfx(
            "sounds/rocket_fly.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(self._hiss_snd,
                                           self._model)  # noqa: F821

        self._hiss_snd2 = base.sound_mgr.loadSfx(
            "sounds/rocket_hiss.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(self._hiss_snd2,
                                           self._model)  # noqa: F821

        self._hiss_snd.play()
        seq = Sequence(
            LerpPosInterval(self._model,
                            7, (x_coor, -0.627, 0.561),
                            blendType="easeOut"),
            Wait(0.7),
            Parallel(
                SoundInterval(self._hiss_snd2),
                MopathInterval(path,
                               self._model,
                               duration=0.5,
                               name="rocket_current_path"),
            ),
            Func(self._explode, side),
        )
        seq.start()
Beispiel #19
0
    def enterOpening(self, ts):
        #if( __debug__ ):
        #    import pdb
        #    pdb.set_trace()
        assert(self.debugPrint("enterOpening()"))

        # Right door:
        bldgActor = self.getBuildingActor()
        rightDoor = bldgActor.controlJoint(None, "modelRoot", "def_right_door")
        if (rightDoor.isEmpty()):
            self.notify.warning("enterOpening(): did not find rightDoor")
            return
        # Open the door:
        otherNP=self.getDoorNodePath()
        trackName = "doorOpen-%d" % (self.doId)
        if self.rightSwing:
            h = 100
        else:
            h = -100
        # Stop animation:
        self.finishDoorTrack()
        self.doorTrack=Parallel(
            SoundInterval(self.openSfx, node=rightDoor),
            Sequence(
                HprInterval(
                    rightDoor,
                    VBase3(0, 0, 0),
                    #other=otherNP,
                    ),
                Wait(0.4),
                #Func(rightDoor.show),
                #Func(doorFrameHoleRight.show),
                LerpHprInterval(
                    nodePath=rightDoor,
                    duration=0.6,
                    hpr=VBase3(h, 0, 0),
                    startHpr=VBase3(0, 0, 0),
                    #other=otherNP,
                    blendType="easeInOut")),
            name = trackName)
        # Start the tracks:
        self.doorTrack.start(ts)
    def hourChange(self, currentHour):
        currentHour = currentHour % 12
        if currentHour == 0:
            currentHour = 12
        self.hourSoundInterval = Parallel()
        seq1 = Sequence()
        for i in xrange(currentHour):
            seq1.append(SoundInterval(self.clockSounds[i]))
            seq1.append(Wait(0.2))

        timeForEachDeformation = seq1.getDuration() / currentHour
        seq2 = Sequence()
        for i in xrange(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 setEveryoneDone(self):
        if not self.hasLocalToon:
            return None
        
        if self.gameFSM.getCurrentState().getName() != 'play':
            self.notify.warning('ignoring setEveryoneDone msg')
            return None
        
        self.notify.debug('setEveryoneDone')
        
        def endGame(task, self = self):
            if not PairingGameGlobals.EndlessGame:
                self.gameOver()
            
            return Task.done

        self.timer.hide()
        self.bonusGlow.hide()
        if len(self.inactiveList) == len(self.cards):
            self.notify.debug('perfect game!')
            perfectTextSubnode = hidden.attachNewNode(self._DistributedPairingGame__genText(TTLocalizer.PairingGamePerfect))
            perfectText = hidden.attachNewNode('perfectText')
            perfectTextSubnode.reparentTo(perfectText)
            frame = self._DistributedPairingGame__textGen.getCardActual()
            offsetY = -abs(frame[2] + frame[3]) / 2.0
            perfectTextSubnode.setPos(0, 0, offsetY)
            perfectText.setColor(1, 0.10000000000000001, 0.10000000000000001, 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.29999999999999999, 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), Func(endGame, None))
            soundTrack = SoundInterval(self.sndPerfect)
            self.perfectIval = Parallel(textTrack, soundTrack)
            self.perfectIval.start()
        else:
            taskMgr.doMethodLater(1, endGame, self.EndGameTaskName)
    def __tickNearbyCogs(self):
        self.__selectNearbyCogs()
        tickTrack = Parallel()
        tickDuration = 0.4

        for cog in self.selectedCogs:
            if not cog.isDead():
                base.audio3d.attachSoundToObject(self.tickSfx, cog)
                tickTrack.append(
                    Parallel(
                        Sequence(
                            LerpColorScaleInterval(cog, tickDuration,
                                                   VBase4(1, 0, 0, 1)),
                            Func(cog.clearColorScale),
                            Func(cog.d_disableMovement)),
                        SoundInterval(self.tickSfx,
                                      duration=tickDuration,
                                      node=cog)))
            else:
                self.selectedCogs.remove(cog)
        return tickTrack
 def setMovie(self, mode, avId):
     isLocalToon = avId == base.localAvatar.doId
     if isLocalToon:
         DistributedMailbox.notify.debug('setMovie( mode=%d, avId=%d ) called on a local toon' % (mode, avId))
     else:
         DistributedMailbox.notify.debug('setMovie( mode=%d, avId=%d ) called on a non-local toon' % (mode, avId))
     if mode == MailboxGlobals.MAILBOX_MOVIE_CLEAR:
         DistributedMailbox.notify.debug('setMovie: clear')
         return
     elif mode == MailboxGlobals.MAILBOX_MOVIE_EXIT:
         if random.random() < 0.5:
             sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_close_1.ogg')
         else:
             sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_close_2.ogg')
         sfxTrack = SoundInterval(sfx, node=self.model)
         sfxTrack.start()
         DistributedMailbox.notify.debug('setMovie: exit')
         return
     elif mode == MailboxGlobals.MAILBOX_MOVIE_EMPTY:
         DistributedMailbox.notify.debug('setMovie: empty')
         if isLocalToon:
             self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxEmpty', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxEmpty, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog)
         return
     elif mode == MailboxGlobals.MAILBOX_MOVIE_WAITING:
         DistributedMailbox.notify.debug('setMovie: waiting')
         if isLocalToon:
             self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxWaiting', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxWaiting, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog)
         return
     elif mode == MailboxGlobals.MAILBOX_MOVIE_READY:
         DistributedMailbox.notify.debug('setMovie: ready')
         if random.random() < 0.5:
             sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_open_1.ogg')
         else:
             sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_open_2.ogg')
         sfxTrack = SoundInterval(sfx, node=self.model)
         sfxTrack.start()
         if isLocalToon:
             self.mailboxGui = MailboxScreen.MailboxScreen(self, base.localAvatar, self.mailboxGuiDoneEvent)
             self.mailboxGui.show()
             self.accept(self.mailboxGuiDoneEvent, self.__handleMailboxDone)
         return
     elif mode == MailboxGlobals.MAILBOX_MOVIE_NOT_OWNER:
         DistributedMailbox.notify.debug('setMovie: not owner')
         if isLocalToon:
             self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxNotOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxNotOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog)
         return
     else:
         DistributedMailbox.notify.warning('unknown mode in setMovie: %s' % mode)
    def _DistributedCogThiefGame__updateRewardCountdown(self, task):
        curTime = self.getCurrentGameTime()
        if self.clockStopTime is not None:
            if self.clockStopTime < curTime:
                self.notify.debug('self.clockStopTime < curTime %s %s' %
                                  (self.clockStopTime, curTime))
                self._DistributedCogThiefGame__killRewardCountdown()
                curTime = self.clockStopTime

        if curTime > CTGG.GameTime:
            curTime = CTGG.GameTime

        score = int(self.scoreMult * CTGG.calcScore(curTime) + 0.5)
        if not hasattr(task, 'curScore'):
            task.curScore = score

        result = Task.cont
        if hasattr(self, 'rewardPanel'):
            self.rewardPanel['text'] = str(score)
            if task.curScore != score:
                if hasattr(self, 'jarIval'):
                    self.jarIval.finish()

                s = self.rewardPanel.getScale()
                self.jarIval = Parallel(Sequence(
                    self.rewardPanel.scaleInterval(0.14999999999999999,
                                                   s * 3.0 / 4.0,
                                                   blendType='easeOut'),
                    self.rewardPanel.scaleInterval(0.14999999999999999,
                                                   s,
                                                   blendType='easeIn')),
                                        SoundInterval(self.sndRewardTick),
                                        name='cogThiefGameRewardJarThrob')
                self.jarIval.start()

            task.curScore = score
        else:
            result = Task.done
        return result
    def startBallPlayback(self, power, angle, sequenceNum):
        flyBall = self.ballModel.copyTo(NodePath())
        flyBall.setScale(1.0)
        flyBallBubble = self.getFlyBallBubble().instanceTo(NodePath())
        flyBallBubble.reparentTo(flyBall)
        flyBall.setTag('pieSequence', str(sequenceNum))
        flyBall.setTag('throwerId', str(self.avId))
        t = power / 100.0
        t = 1.0 - t
        dist = 300 - 200 * t
        time = 1.5 + 0.5 * t
        proj = ProjectileInterval(None, startPos = Point3(0, 0, 0), endPos = Point3(0, dist, 0), duration = time)
        relVel = proj.startVel
        
        def getVelocity(root = self.root, relVel = relVel):
            return render.getRelativeVector(root, relVel)

        fly = Sequence(Func(flyBall.reparentTo, render), Func(flyBall.setPosHpr, self.root, 0, 0, 0, 0, 0, 0), Func(base.cTrav.addCollider, flyBallBubble, self.flyBallHandler), ProjectileInterval(flyBall, startVel = getVelocity, duration = 3), Func(flyBall.detachNode), Func(base.cTrav.removeCollider, flyBallBubble), Func(self.notify.debug, 'removed collider'), Func(self.flyBallFinishedFlying, sequenceNum))
        flyWithSound = Parallel(fly, SoundInterval(self.hitBallSfx, node = self.root), name = 'flyWithSound')
        self.notify.debug('starting flyball track')
        flyWithSound.start()
        self.flyBallTracks[sequenceNum] = flyWithSound
Beispiel #26
0
 def enterSquish(self, ts=0, callback=None, extraArgs=[]):
     self.playingAnim = 'squish'
     sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg')
     lerpTime = 0.1
     node = self.getGeomNode().getChild(0)
     origScale = node.getScale()
     if hasattr(self, 'uniqueName'):
         name = self.uniqueName('getSquished')
     else:
         name = 'getSquished'
     self.track = Sequence(LerpScaleInterval(node,
                                             lerpTime,
                                             VBase3(2, 2, 0.025),
                                             blendType='easeInOut'),
                           Wait(1.0),
                           Parallel(
                               Sequence(
                                   Wait(0.4),
                                   LerpScaleInterval(node,
                                                     lerpTime,
                                                     VBase3(1.4, 1.4, 1.4),
                                                     blendType='easeInOut'),
                                   LerpScaleInterval(node,
                                                     lerpTime / 2.0,
                                                     VBase3(0.8, 0.8, 0.8),
                                                     blendType='easeInOut'),
                                   LerpScaleInterval(
                                       node,
                                       lerpTime / 3.0,
                                       origScale,
                                       blendType='easeInOut')),
                               ActorInterval(self, 'happy', startTime=0.2),
                               SoundInterval(sound)),
                           name=name)
     self.track.setDoneEvent(self.track.getName())
     self.acceptOnce(self.track.getDoneEvent(), self.squishDone,
                     [callback, extraArgs])
     self.track.delayDelete = DelayDelete.DelayDelete(self, name)
     self.track.start(ts)
Beispiel #27
0
    def _shot(self):
        """Make a cluster howitzer shot."""
        self.change_state()
        base.ignore("3")  # noqa: F82

        rocket = loader.loadModel(address("cluster_rocket"))  # noqa: F82
        rocket.reparentTo(self._model)
        rocket.setPos(0, -0.325, 0.3)
        rocket.setP(20)

        smoke = ParticleEffect()
        smoke.loadConfig("effects/smoke_tail.ptf")
        smoke.start(rocket, render)  # noqa: F821

        hiss_snd = base.sound_mgr.loadSfx("sounds/rocket_fly.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(hiss_snd, rocket)  # noqa: F821
        hiss_snd.play()

        open_snd = base.sound_mgr.loadSfx("sounds/cluster_open.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(open_snd, rocket)  # noqa: F821

        Sequence(
            Parallel(
                LerpPosInterval(rocket, 2, (0, 1.8, 3)),
                LerpHprInterval(rocket, 2, (0, 80, 0)),
            ),
            SoundInterval(open_snd),
            Func(self._clear_rocket, rocket, smoke, hiss_snd),
            Func(self._bombs_down),
        ).start()

        taskMgr.doMethodLater(  # noqa: F82
            45,
            base.accept,  # noqa: F82
            "unblock_cluster_launcher",
            extraArgs=["3", self.change_state],
        )
        base.train.make_shot("Cluster Howitzer")  # noqa: F82
Beispiel #28
0
    def start(self):
        SoundGag.start(self)
        INSTRUMENT_SCALE_MODIFIER = 0.5
        tracks = Parallel()
        instrMin = Vec3(0.001, 0.001, 0.001)
        instrMax = Vec3(0.4, 0.4, 0.4)
        instrMax *= INSTRUMENT_SCALE_MODIFIER
        instrStretch = Vec3(0.5, 0.5, 0.5)
        instrStretch *= INSTRUMENT_SCALE_MODIFIER

        def setInstrumentStats():
            self.gag.setPos(-1.3, -1.4, 0.1)
            self.gag.setHpr(145, 0, 85)
            self.gag.setScale(instrMin)

        def longshake(models, num):
            inShake = self.getScaleBlendIntervals(models, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeInOut')
            outShake = self.getScaleBlendIntervals(models, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeInOut')
            i = 1
            seq = Sequence()
            while i < num:
                if i % 2 == 0:
                    seq.append(inShake)
                else:
                    seq.append(outShake)
                i += 1

            seq.start()
        megaphoneShow = Sequence(Func(self.placeProp, self.handJoint, self.megaphone), Func(self.placeProp, self.handJoint, self.gag), Func(setInstrumentStats))
        grow = self.getScaleBlendIntervals(self.gag, duration=1, startScale=instrMin, endScale=instrMax, blendType='easeInOut')
        instrumentshrink = self.getScaleIntervals(self.gag, duration=0.1, startScale=instrMax, endScale=instrMin)
        instrumentAppear = Sequence(grow, Wait(0), Func(longshake, self.gag, 5))
        megaphoneTrack = Parallel(Sequence(Wait(1.7), SoundInterval(self.soundSfx, node=self.avatar)), Sequence(megaphoneShow, Wait(1.7), instrumentAppear, Wait(1),
                                           Func(self.damageCogsNearby), instrumentshrink, Wait(0.4), Func(self.finish)))
        tracks.append(megaphoneTrack)
        tracks.append(self.getSingularAnimTrack('sound'))
        tracks.start()
        self.tracks = tracks
Beispiel #29
0
 def enterFlyDown(self, ts = 0):
     self.disableRay()
     self.enableShadowRay()
     if not self.propeller:
         self.generatePropeller()
     sfx = self.propellerSounds['in']
     base.playSfx(sfx, node = self)
     groundF = 28
     dur = self.getDuration('land')
     fr = self.getFrameRate('land')
     if fr:
         animTimeInAir = groundF / fr
     else:
         animTimeInAir = groundF
     impactLength = dur - animTimeInAir
     timeTillLanding = 6.5 - impactLength
     waitTime = timeTillLanding - animTimeInAir
     lastSpinFrame = 8
     propDur = self.propeller.getDuration('chan')
     fr = self.propeller.getFrameRate('chan')
     spinTime = lastSpinFrame / fr
     openTime = (lastSpinFrame + 1) / fr
     if hasattr(self, 'uniqueName'):
         name = self.uniqueName('enterFlyDown')
     else:
         name = 'enterFlyDown'
     animTrack = Sequence(Func(self.pose, 'land', 0), Wait(waitTime), ActorInterval(self, 'land', duration=dur))
     propTrack = Parallel(SoundInterval(sfx, duration=waitTime + dur, node=self), Sequence(ActorInterval(self.propeller, 'chan', constrainedLoop=1, duration=waitTime + spinTime, startTime=0.0, endTime=spinTime), ActorInterval(self.propeller, 'chan', duration=propDur - openTime, startTime=openTime)))
     self.suitTrack = Parallel(animTrack, propTrack, name=self.taskName('flyDown'))
     if not self.hasSpawned:
         self.show()
         fadeInTrack = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Func(self.clearColorScale), Func(self.clearTransparency))
         self.hasSpawned = True
         self.suitTrack.append(fadeInTrack)
     self.suitTrack.setDoneEvent(self.suitTrack.getName())
     self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyDown)
     self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
     self.suitTrack.start(ts)
Beispiel #30
0
 def freeze(self):
     if self.isFrozen:
         return 0
     self.clearFreezeTracks()
     self.avatar.animFSM.request('off')
     self.avatar.stop()
     self.freezeUpTrack = Parallel(
         SoundInterval(self.iceCubeForm, node=self.iceCube),
         LerpColorScaleInterval(
             self.iceCube,
             duration=0.5,
             colorScale=VBase4(0.76, 0.76, 1.0, 1.0),
             startColorScale=self.iceCube.getColorScale(),
             blendType='easeInOut'),
         LerpColorScaleInterval(
             self.avatar.getGeomNode(),
             duration=0.5,
             colorScale=VBase4(0.5, 0.5, 1.0, 1.0),
             startColorScale=base.localAvatar.getGeomNode().getColorScale(),
             blendType='easeOut'))
     self.freezeUpTrack.start()
     self.isFrozen = True
     return 1
    def changeDinerToDead(self, chairIndex):

        def removeDeathSuit(suit, deathSuit):
            if not deathSuit.isEmpty():
                deathSuit.detachNode()
                suit.cleanupLoseActor()

        self.removeFoodModel(chairIndex)
        indicator = self.dinerStatusIndicators.get(chairIndex)
        if indicator:
            indicator.request('Dead')
        diner = self.diners[chairIndex]
        deathSuit = diner
        locator = self.tableGroup.find('**/chair_%d' % (chairIndex + 1))
        deathSuit = diner.getLoseActor()
        ival = Sequence(Func(self.notify.debug, 'before actorinterval sit-lose'), ActorInterval(diner, 'sit-lose'), Func(self.notify.debug, 'before deathSuit.setHpr'), Func(deathSuit.setHpr, diner.getHpr()), Func(self.notify.debug, 'before diner.hide'), Func(diner.hide), Func(self.notify.debug, 'before deathSuit.reparentTo'), Func(deathSuit.reparentTo, self.chairLocators[chairIndex]), Func(self.notify.debug, 'befor ActorInterval lose'), ActorInterval(deathSuit, 'lose', duration=MovieUtil.SUIT_LOSE_DURATION), Func(self.notify.debug, 'before remove deathsuit'), Func(removeDeathSuit, diner, deathSuit, name='remove-death-suit-%d-%d' % (chairIndex, self.index)), Func(self.notify.debug, 'diner.stash'), Func(diner.stash))
        spinningSound = base.loader.loadSfx('phase_3.5/audio/sfx/Cog_Death.ogg')
        deathSound = base.loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.ogg')
        deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2, node=deathSuit), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8, node=deathSuit), SoundInterval(deathSound, volume=0.32, node=deathSuit))
        intervalName = 'dinerDie-%d-%d' % (self.index, chairIndex)
        deathIval = Parallel(ival, deathSoundTrack)
        deathIval.start()
        self.activeIntervals[intervalName] = deathIval
 def exitDoorEnterOpening(self, ts):
     bldgActor = self.getBuildingActor()
     leftDoor = bldgActor.controlJoint(None, 'modelRoot', 'def_left_door')
     if self.leftSwing:
         h = -100
     else:
         h = 100
     if not leftDoor.isEmpty():
         otherNP = self.getDoorNodePath()
         trackName = 'doorDoorExitTrack-%d' % self.doId
         self.finishDoorExitTrack()
         self.doorExitTrack = Parallel(
             SoundInterval(
                 self.openSfx, node=leftDoor), Sequence(
                 LerpHprInterval(
                     nodePath=leftDoor, duration=0.6, hpr=VBase3(
                         h, 0, 0), startHpr=VBase3(
                         0, 0, 0), blendType='easeInOut')), name=trackName)
         self.doorExitTrack.start(ts)
     else:
         self.notify.warning(
             'exitDoorEnterOpening(): did not find leftDoor')
     return
    def startCharging(self):
        # Let's disable shadow movement.
        LocationSeeker.stopSeeking(self)
        self.dropShadow.setZ(self.dropShadow.getZ() - 0.45)

        # Let's start the charging effect.
        finalScale = 6
        self.shadowTrack = Sequence()
        chargeTrack = Parallel(
            LerpScaleInterval(self.dropShadow,
                              self.chargeDuration,
                              finalScale,
                              startScale=self.dropShadow.getScale(),
                              blendType='easeInOut'),
            Func(base.audio3d.attachSoundToObject, self.chargingSfx,
                 self.dropShadow),
            SoundInterval(self.chargingSfx,
                          duration=self.chargeDuration,
                          node=self.dropShadow))
        self.shadowTrack.append(chargeTrack)
        self.shadowTrack.append(self.__tickNearbyCogs())
        self.shadowTrack.append(Func(self.onFullCharge))
        self.shadowTrack.start()
        self.isCharging = True
class DistributedFoodBelt(DistributedObject.DistributedObject, FSM.FSM, FoodBeltBase):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFoodBelt')
    BeltSpeed = 5
    OnDuration = 300
    ToonupBeltSpeed = 1.0
    BeltActorPlayRate = 5.3499999999999996
    ToonupBeltActorPlayRate = BeltActorPlayRate * ToonupBeltSpeed / BeltSpeed
    ToonupModels = [
        'phase_6/models/golf/picnic_apple.bam',
        'phase_6/models/golf/picnic_cupcake.bam',
        'phase_6/models/golf/picnic_sandwich.bam',
        'phase_6/models/golf/picnic_chocolate_cake.bam']
    ToonupScales = [
        5,
        5,
        5,
        4]
    ToonupZOffsets = [
        -0.25,
        -0.25,
        -0,
        -0.25]
    
    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        FSM.FSM.__init__(self, 'DistributedFoodBelt')
        self.boss = None
        self.bossCogId = 0
        self.index = -1
        self.foodNodes = []
        self.foodIvals = []
        self.foodWaitTimes = []
        self.foodModelDict = { }
        self.foodNum = 0
        self.beltActor = None
        self.toonupIvals = []
        self.toonupWaitTimes = []
        self.toonupModelDict = { }
        self.toonupNum = 0

    
    def delete(self):
        DistributedObject.DistributedObject.delete(self)
        self.cleanup()

    
    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        if self.boss:
            self.boss.setBelt(self, self.index)
            self.loadAssets()
        else:
            self.notify.warning('announceGenerate self.boss is None, self.bossCogId = %d' % self.bossCogId)

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

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

    
    def setState(self, state):
        if state == 'F':
            self.demand('Off')
        elif state == 'N':
            self.demand('On')
        elif state == 'I':
            self.demand('Inactive')
        elif state == 'T':
            self.demand('Toonup')
        else:
            self.notify.error('Invalid state from AI: %s' % state)

    
    def enterOn(self):
        self.beltSoundInterval.loop()
        for i in xrange(len(self.foodNodes)):
            self.doMethodLater(self.foodWaitTimes[i], self.startFoodMoving, 'start-%d-%d' % (self.index, i), extraArgs = [
                i])
        

    
    def exitOn(self):
        self.beltSoundInterval.finish()
        for i in xrange(len(self.foodNodes)):
            taskName = 'start-%d-%d' % (self.index, i)
            self.removeTask(taskName)
        

    
    def enterToonup(self):
        self.beltSound.setPlayRate(self.ToonupBeltSpeed / self.BeltSpeed)
        self.beltSoundInterval.loop()
        for i in xrange(len(self.foodNodes)):
            self.removeFood(i)
            self.beltActor.setPlayRate(self.ToonupBeltActorPlayRate, 'idle')
            self.doMethodLater(self.toonupWaitTimes[i], self.startToonupMoving, 'startToonup-%d-%d' % (self.index, i), extraArgs = [
                i])
        

    
    def exitToonup(self):
        self.beltSoundInterval.finish()
        for i in xrange(len(self.foodNodes)):
            taskName = 'startToonup-%d-%d' % (self.index, i)
            self.removeTask(taskName)
        

    
    def enterInactive(self):
        for ival in self.foodIvals:
            ival.finish()
        
        for ival in self.toonupIvals:
            ival.finish()
        
        for i in xrange(len(self.foodNodes)):
            self.removeFood(i)
            self.removeToonup(i)
        
        if self.beltActor:
            self.beltActor.stop()
        

    
    def exitInactive(self):
        pass

    
    def startFoodMoving(self, foodIndex):
        if foodIndex < len(self.foodIvals):
            self.foodIvals[foodIndex].loop()
        else:
            self.notify.warning('startFoodMoving invalid index %d' % foodIndex)
        if self.beltActor:
            self.beltActor.loop('idle')
        

    
    def startToonupMoving(self, toonupIndex):
        if toonupIndex < len(self.toonupIvals):
            self.toonupIvals[toonupIndex].loop()
        else:
            self.notify.warning('startToonupMoving invalid index %d' % toonupIndex)
        if self.beltActor:
            self.beltActor.loop('idle')
        

    
    def loadAssets(self):
        self.beltModel = NodePath('beltModel')
        self.beltModel.reparentTo(self.boss.geom)
        self.startLocator = self.boss.geom.find('**/conveyer_belt_start_%d' % (self.index + 1))
        self.endLocator = self.boss.geom.find('**/conveyer_belt_end_%d' % (self.index + 1))
        center = (self.startLocator.getPos() + self.endLocator.getPos()) / 2.0
        self.beltHeight = center.getZ()
        self.beltHeight += 0.10000000000000001
        center.setZ(0)
        self.beltLength = (self.endLocator.getPos() - self.startLocator.getPos()).length()
        self.distBetweenFoodNodes = self.beltLength / self.NumFoodNodes
        self.notify.debug('setting beltModelPos to %s' % center)
        self.beltModel.setPos(center)
        self.setupFoodNodes()
        self.setupFoodIvals()
        self.setupToonupIvals()
        if self.index == 0:
            self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt1_model')
        else:
            self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt2_model')
        if self.beltActorModel:
            self.beltActor = Actor.Actor(self.beltActorModel)
            if self.index == 0:
                self.beltActor.loadAnims({
                    'idle': 'phase_12/models/bossbotHQ/food_belt1' })
            else:
                self.beltActor.loadAnims({
                    'idle': 'phase_12/models/bossbotHQ/food_belt2' })
            self.beltActor.reparentTo(render)
            self.beltActor.setPlayRate(self.BeltActorPlayRate, 'idle')
            mesh = self.beltActor.find('**/mesh_tide1')
            joint = self.beltActor.find('**/uvj_WakeWhiteTide1')
            mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.beltActor)
            self.beltActor.setPos(self.startLocator.getPos())
        
        self.beltSound = base.loadSfx('phase_12/audio/sfx/CHQ_FACT_conveyor_belt.wav')
        self.beltSound.setLoop(1)
        self.beltSoundInterval = SoundInterval(self.beltSound, node = self.beltModel, listenerNode = base.localAvatar, seamlessLoop = True, volume = 0.25, cutOff = 100)

    
    def cleanup(self):
        for i in xrange(len(self.foodNodes)):
            taskName = 'start-%d-%d' % (self.index, i)
            self.removeTask(taskName)
        
        for i in xrange(len(self.foodNodes)):
            taskName = 'startToonup-%d-%d' % (self.index, i)
            self.removeTask(taskName)
        
        for ival in self.foodIvals:
            ival.finish()
        
        self.foodIvals = []
        for ival in self.toonupIvals:
            ival.finish()
        
        self.toonupIvals = []
        self.beltSoundInterval.finish()
        self.beltActor.delete()
        self.beltModel = None
        self.removeAllTasks()
        self.ignoreAll()

    
    def setupFoodNodes(self):
        for i in xrange(self.NumFoodNodes):
            newPosIndex = self.NumFoodNodes - 1 - i
            yPos = -(self.beltLength / 2.0) + newPosIndex * self.distBetweenFoodNodes
            newFoodNode = NodePath('foodNode-%d-%d' % (self.index, i))
            newFoodNode.reparentTo(self.beltModel)
            newFoodNode.setPos(0, yPos, self.beltHeight)
            debugFood = None
            if debugFood:
                debugFood.setScale(0.10000000000000001)
                debugFood.reparentTo(newFoodNode)
            
            newFoodNode.setH(180)
            self.foodNodes.append(newFoodNode)
        

    
    def setupFoodIvals(self):
        for i in xrange(len(self.foodNodes)):
            foodIval = self.createOneFoodIval(self.foodNodes[i])
            self.foodIvals.append(foodIval)
        

    
    def createOneFoodIval(self, foodNode):
        foodIndex = self.foodNodes.index(foodNode)
        waitTimeForOne = self.distBetweenFoodNodes / self.BeltSpeed
        waitTime = waitTimeForOne * foodIndex
        self.foodWaitTimes.append(waitTime)
        totalTimeToTraverseBelt = self.beltLength / self.BeltSpeed
        startPosY = -(self.beltLength / 2.0)
        endPosY = self.beltLength / 2.0
        retval = Sequence(Func(self.loadFood, foodIndex), LerpPosInterval(foodNode, duration = totalTimeToTraverseBelt, startPos = Point3(0, startPosY, self.beltHeight), pos = Point3(0, endPosY, self.beltHeight)), ProjectileInterval(foodNode, startPos = Point3(0, endPosY, self.beltHeight), startVel = Point3(0, self.BeltSpeed, 0), endZ = 0), Func(self.removeFood, foodIndex))
        return retval

    
    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 removeFood(self, foodIndex):
        if foodIndex in self.foodModelDict:
            foodModel = self.foodModelDict[foodIndex]
            foodModel.stash()
        

    
    def touchedFood(self, colEntry):
        into = colEntry.getIntoNodePath()
        
        try:
            beltIndex = int(into.getTag('beltIndex'))
        except:
            beltIndex = 0

        
        try:
            foodIndex = int(into.getTag('foodIndex'))
        except:
            foodIndex = 0

        
        try:
            foodNum = int(into.getTag('foodNum'))
        except:
            foodNum = 0

        if self.boss:
            self.boss.localToonTouchedBeltFood(beltIndex, foodIndex, foodNum)
        

    
    def setupToonupIvals(self):
        for i in xrange(len(self.foodNodes)):
            toonupIval = self.createOneToonupIval(self.foodNodes[i])
            self.toonupIvals.append(toonupIval)
        

    
    def createOneToonupIval(self, foodNode):
        toonupIndex = self.foodNodes.index(foodNode)
        waitTimeForOne = self.distBetweenFoodNodes / self.ToonupBeltSpeed
        waitTime = waitTimeForOne * toonupIndex
        self.toonupWaitTimes.append(waitTime)
        totalTimeToTraverseBelt = self.beltLength / self.ToonupBeltSpeed
        startPosY = -(self.beltLength / 2.0)
        endPosY = self.beltLength / 2.0
        retval = Sequence(Func(self.loadToonup, toonupIndex), LerpPosInterval(foodNode, duration = totalTimeToTraverseBelt, startPos = Point3(0, startPosY, self.beltHeight), pos = Point3(0, endPosY, self.beltHeight)), ProjectileInterval(foodNode, startPos = Point3(0, endPosY, self.beltHeight), startVel = Point3(0, self.BeltSpeed, 0), endZ = 0), Func(self.removeToonup, toonupIndex))
        return retval

    
    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.10000000000000001)
            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 removeToonup(self, toonupIndex):
        if toonupIndex in self.toonupModelDict:
            toonupModel = self.toonupModelDict[toonupIndex]
            toonupModel.stash()
        

    
    def touchedToonup(self, colEntry):
        if base.localAvatar.hp >= base.localAvatar.maxHp:
            return None
        
        into = colEntry.getIntoNodePath()
        
        try:
            beltIndex = int(into.getTag('beltIndex'))
        except:
            beltIndex = 0

        
        try:
            toonupIndex = int(into.getTag('toonupIndex'))
        except:
            toonupIndex = 0

        
        try:
            toonupNum = int(into.getTag('toonupNum'))
        except:
            toonupNum = 0

        if self.boss:
            self.boss.localToonTouchedBeltToonup(beltIndex, toonupIndex, toonupNum)
class DistributedBanquetTable(DistributedObject.DistributedObject, FSM.FSM, BanquetTableBase.BanquetTableBase):
    notify = DirectNotifyGlobal.directNotify.newCategory("DistributedBanquetTable")
    rotationsPerSeatIndex = [90, 90, 0, 0, -90, -90, 180, 180]
    pitcherMinH = -360
    pitcherMaxH = 360
    rotateSpeed = 30
    waterPowerSpeed = base.config.GetDouble("water-power-speed", 15)
    waterPowerExponent = base.config.GetDouble("water-power-exponent", 0.75)
    useNewAnimations = True
    TugOfWarControls = False
    OnlyUpArrow = True
    if OnlyUpArrow:
        BASELINE_KEY_RATE = 3
    else:
        BASELINE_KEY_RATE = 6
    UPDATE_KEY_PRESS_RATE_TASK = "BanquetTableUpdateKeyPressRateTask"
    YELLOW_POWER_THRESHOLD = 0.75
    RED_POWER_THRESHOLD = 0.96999999999999997

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        FSM.FSM.__init__(self, "DistributedBanquetTable")
        self.boss = None
        self.index = -1
        self.diners = {}
        self.dinerStatus = {}
        self.serviceLocs = {}
        self.chairLocators = {}
        self.sitLocators = {}
        self.activeIntervals = {}
        self.dinerStatusIndicators = {}
        self.preparedForPhaseFour = False
        self.avId = 0
        self.toon = None
        self.pitcherSmoother = SmoothMover()
        self.pitcherSmoother.setSmoothMode(SmoothMover.SMOn)
        self.smoothStarted = 0
        self._DistributedBanquetTable__broadcastPeriod = 0.20000000000000001
        self.changeSeq = 0
        self.lastChangeSeq = 0
        self.pitcherAdviceLabel = None
        self.fireLength = 250
        self.fireTrack = None
        self.hitObject = None
        self.setupPowerBar()
        self.aimStart = None
        self.toonPitcherPosition = Point3(0, -2, 0)
        self.allowLocalRequestControl = True
        self.fadeTrack = None
        self.grabTrack = None
        self.gotHitByBoss = False
        self.keyTTL = []
        self.keyRate = 0
        self.buttons = [0, 1]
        self.lastPowerFired = 0
        self.moveSound = None
        self.releaseTrack = None

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        taskMgr.remove(self.triggerName)
        taskMgr.remove(self.smoothName)
        taskMgr.remove(self.watchControlsName)
        taskMgr.remove(self.pitcherAdviceName)
        taskMgr.remove(self.posHprBroadcastName)
        taskMgr.remove(self.waterPowerTaskName)
        if self.releaseTrack:
            self.releaseTrack.finish()
            self.releaseTrack = None

        if self.fireTrack:
            self.fireTrack.finish()
            self.fireTrack = None

        self.cleanupIntervals()

    def delete(self):
        DistributedObject.DistributedObject.delete(self)
        self.boss = None
        self.ignoreAll()
        for indicator in self.dinerStatusIndicators.values():
            indicator.delete()

        self.dinerStatusIndicators = {}
        for diner in self.diners.values():
            diner.delete()

        self.diners = {}
        self.powerBar.destroy()
        self.powerBar = None
        self.pitcherMoveSfx.stop()

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        self.loadAssets()
        self.smoothName = self.uniqueName("pitcherSmooth")
        self.pitcherAdviceName = self.uniqueName("pitcherAdvice")
        self.posHprBroadcastName = self.uniqueName("pitcherBroadcast")
        self.waterPowerTaskName = self.uniqueName("updateWaterPower")
        self.triggerName = self.uniqueName("trigger")
        self.watchControlsName = self.uniqueName("watchControls")

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

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

    def setState(self, state, avId, extraInfo):
        self.gotHitByBoss = extraInfo
        if state == "F":
            self.demand("Off")
        elif state == "N":
            self.demand("On")
        elif state == "I":
            self.demand("Inactive")
        elif state == "R":
            self.demand("Free")
        elif state == "C":
            self.demand("Controlled", avId)
        elif state == "L":
            self.demand("Flat", avId)
        else:
            self.notify.error("Invalid state from AI: %s" % state)

    def setNumDiners(self, numDiners):
        self.numDiners = numDiners

    def setDinerInfo(self, hungryDurations, eatingDurations, dinerLevels):
        self.dinerInfo = {}
        for i in xrange(len(hungryDurations)):
            hungryDur = hungryDurations[i]
            eatingDur = eatingDurations[i]
            dinerLevel = dinerLevels[i]
            self.dinerInfo[i] = (hungryDur, eatingDur, dinerLevel)

    def loadAssets(self):
        self.tableGroup = loader.loadModel("phase_12/models/bossbotHQ/BanquetTableChairs")
        tableLocator = self.boss.geom.find("**/TableLocator_%d" % (self.index + 1))
        if tableLocator.isEmpty():
            self.tableGroup.reparentTo(render)
            self.tableGroup.setPos(0, 75, 0)
        else:
            self.tableGroup.reparentTo(tableLocator)
        self.tableGeom = self.tableGroup.find("**/Geometry")
        self.setupDiners()
        self.setupChairCols()
        self.squirtSfx = loader.loadSfx("phase_4/audio/sfx/AA_squirt_seltzer_miss.mp3")
        self.hitBossSfx = loader.loadSfx("phase_5/audio/sfx/SA_watercooler_spray_only.mp3")
        self.hitBossSoundInterval = SoundInterval(self.hitBossSfx, node=self.boss, volume=1.0)
        self.serveFoodSfx = loader.loadSfx("phase_4/audio/sfx/MG_sfx_travel_game_bell_for_trolley.mp3")
        self.pitcherMoveSfx = base.loadSfx("phase_4/audio/sfx/MG_cannon_adjust.mp3")

    def setupDiners(self):
        for i in xrange(self.numDiners):
            newDiner = self.createDiner(i)
            self.diners[i] = newDiner
            self.dinerStatus[i] = self.HUNGRY

    def createDiner(self, i):
        diner = Suit.Suit()
        diner.dna = SuitDNA.SuitDNA()
        level = self.dinerInfo[i][2]
        level -= 4
        diner.dna.newSuitRandom(level=level, dept="c")
        diner.setDNA(diner.dna)
        if self.useNewAnimations:
            diner.loop("sit", fromFrame=i)
        else:
            diner.pose("landing", 0)
        locator = self.tableGroup.find("**/chair_%d" % (i + 1))
        locatorScale = locator.getNetTransform().getScale()[0]
        correctHeadingNp = locator.attachNewNode("correctHeading")
        self.chairLocators[i] = correctHeadingNp
        heading = self.rotationsPerSeatIndex[i]
        correctHeadingNp.setH(heading)
        sitLocator = correctHeadingNp.attachNewNode("sitLocator")
        base.sitLocator = sitLocator
        pos = correctHeadingNp.getPos(render)
        if SuitDNA.getSuitBodyType(diner.dna.name) == "c":
            sitLocator.setPos(0.5, 3.6499999999999999, -3.75)
        else:
            sitLocator.setZ(-2.3999999999999999)
            sitLocator.setY(2.5)
            sitLocator.setX(0.5)
        self.sitLocators[i] = sitLocator
        diner.setScale(1.0 / locatorScale)
        diner.reparentTo(sitLocator)
        newLoc = NodePath("serviceLoc-%d-%d" % (self.index, i))
        newLoc.reparentTo(correctHeadingNp)
        newLoc.setPos(0, 3.0, 1)
        self.serviceLocs[i] = newLoc
        base.serviceLoc = newLoc
        head = diner.find("**/joint_head")
        newIndicator = DinerStatusIndicator.DinerStatusIndicator(parent=head, pos=Point3(0, 0, 3.5), scale=5.0)
        newIndicator.wrtReparentTo(diner)
        self.dinerStatusIndicators[i] = newIndicator
        return diner

    def setupChairCols(self):
        for i in xrange(self.numDiners):
            chairCol = self.tableGroup.find("**/collision_chair_%d" % (i + 1))
            colName = "ChairCol-%d-%d" % (self.index, i)
            chairCol.setTag("chairIndex", str(i))
            chairCol.setName(colName)
            chairCol.setCollideMask(ToontownGlobals.WallBitmask)
            self.accept("enter" + colName, self.touchedChair)

    def touchedChair(self, colEntry):
        chairIndex = int(colEntry.getIntoNodePath().getTag("chairIndex"))
        if chairIndex in self.dinerStatus:
            status = self.dinerStatus[chairIndex]
            if status in (self.HUNGRY, self.ANGRY):
                self.boss.localToonTouchedChair(self.index, chairIndex)

    def serveFood(self, food, chairIndex):
        self.removeFoodModel(chairIndex)
        serviceLoc = self.serviceLocs.get(chairIndex)
        if not food or food.isEmpty():
            foodModel = loader.loadModel("phase_12/models/bossbotHQ/canoffood")
            foodModel.setScale(ToontownGlobals.BossbotFoodModelScale)
            foodModel.reparentTo(serviceLoc)
        else:
            food.wrtReparentTo(serviceLoc)
            tray = food.find("**/tray")
            if not tray.isEmpty():
                tray.hide()

            ivalDuration = 1.5
            foodMoveIval = Parallel(
                SoundInterval(self.serveFoodSfx, node=food),
                ProjectileInterval(
                    food, duration=ivalDuration, startPos=food.getPos(serviceLoc), endPos=serviceLoc.getPos(serviceLoc)
                ),
                LerpHprInterval(food, ivalDuration, Point3(0, -360, 0)),
            )
            intervalName = "serveFood-%d-%d" % (self.index, chairIndex)
            foodMoveIval.start()
            self.activeIntervals[intervalName] = foodMoveIval

    def setDinerStatus(self, chairIndex, status):
        if chairIndex in self.dinerStatus:
            oldStatus = self.dinerStatus[chairIndex]
            self.dinerStatus[chairIndex] = status
            if oldStatus != status:
                if status == self.EATING:
                    self.changeDinerToEating(chairIndex)
                elif status == self.HUNGRY:
                    self.changeDinerToHungry(chairIndex)
                elif status == self.ANGRY:
                    self.changeDinerToAngry(chairIndex)
                elif status == self.DEAD:
                    self.changeDinerToDead(chairIndex)
                elif status == self.HIDDEN:
                    self.changeDinerToHidden(chairIndex)

    def removeFoodModel(self, chairIndex):
        serviceLoc = self.serviceLocs.get(chairIndex)
        if serviceLoc:
            for i in xrange(serviceLoc.getNumChildren()):
                serviceLoc.getChild(0).removeNode()

    def changeDinerToEating(self, chairIndex):
        indicator = self.dinerStatusIndicators.get(chairIndex)
        eatingDuration = self.dinerInfo[chairIndex][1]
        if indicator:
            indicator.request("Eating", eatingDuration)

        diner = self.diners[chairIndex]
        intervalName = "eating-%d-%d" % (self.index, chairIndex)
        eatInTime = 32.0 / 24.0
        eatOutTime = 21.0 / 24.0
        eatLoopTime = 19 / 24.0
        rightHand = diner.getRightHand()
        waitTime = 5
        loopDuration = eatingDuration - eatInTime - eatOutTime - waitTime
        serviceLoc = self.serviceLocs[chairIndex]

        def foodAttach(self=self, diner=diner):
            foodModel = self.serviceLocs[chairIndex].getChild(0)
            (foodModel.reparentTo(diner.getRightHand()),)
            (foodModel.setHpr(Point3(0, -94, 0)),)
            (foodModel.setPos(Point3(-0.14999999999999999, -0.69999999999999996, -0.40000000000000002)),)
            scaleAdj = 1
            if SuitDNA.getSuitBodyType(diner.dna.name) == "c":
                scaleAdj = 0.59999999999999998
                (foodModel.setPos(Point3(0.10000000000000001, -0.25, -0.31)),)
            else:
                scaleAdj = 0.80000000000000004
                (foodModel.setPos(Point3(-0.25, -0.84999999999999998, -0.34000000000000002)),)
            oldScale = foodModel.getScale()
            newScale = oldScale * scaleAdj
            foodModel.setScale(newScale)

        def foodDetach(self=self, diner=diner):
            foodModel = diner.getRightHand().getChild(0)
            (foodModel.reparentTo(serviceLoc),)
            (foodModel.setPosHpr(0, 0, 0, 0, 0, 0),)
            scaleAdj = 1
            if SuitDNA.getSuitBodyType(diner.dna.name) == "c":
                scaleAdj = 0.59999999999999998
            else:
                scakeAdj = 0.80000000000000004
            oldScale = foodModel.getScale()
            newScale = oldScale / scaleAdj
            foodModel.setScale(newScale)

        eatIval = Sequence(
            ActorInterval(diner, "sit", duration=waitTime),
            ActorInterval(diner, "sit-eat-in", startFrame=0, endFrame=6),
            Func(foodAttach),
            ActorInterval(diner, "sit-eat-in", startFrame=6, endFrame=32),
            ActorInterval(diner, "sit-eat-loop", duration=loopDuration, loop=1),
            ActorInterval(diner, "sit-eat-out", startFrame=0, endFrame=12),
            Func(foodDetach),
            ActorInterval(diner, "sit-eat-out", startFrame=12, endFrame=21),
        )
        eatIval.start()
        self.activeIntervals[intervalName] = eatIval

    def changeDinerToHungry(self, chairIndex):
        intervalName = "eating-%d-%d" % (self.index, chairIndex)
        if intervalName in self.activeIntervals:
            self.activeIntervals[intervalName].finish()

        self.removeFoodModel(chairIndex)
        indicator = self.dinerStatusIndicators.get(chairIndex)
        if indicator:
            indicator.request("Hungry", self.dinerInfo[chairIndex][0])

        diner = self.diners[chairIndex]
        if random.choice([0, 1]):
            diner.loop("sit-hungry-left")
        else:
            diner.loop("sit-hungry-right")

    def changeDinerToAngry(self, chairIndex):
        self.removeFoodModel(chairIndex)
        indicator = self.dinerStatusIndicators.get(chairIndex)
        if indicator:
            indicator.request("Angry")

        diner = self.diners[chairIndex]
        diner.loop("sit-angry")

    def changeDinerToDead(self, chairIndex):
        def removeDeathSuit(suit, deathSuit):
            if not deathSuit.isEmpty():
                deathSuit.detachNode()
                suit.cleanupLoseActor()

        self.removeFoodModel(chairIndex)
        indicator = self.dinerStatusIndicators.get(chairIndex)
        if indicator:
            indicator.request("Dead")

        diner = self.diners[chairIndex]
        deathSuit = diner
        locator = self.tableGroup.find("**/chair_%d" % (chairIndex + 1))
        deathSuit = diner.getLoseActor()
        ival = Sequence(
            Func(self.notify.debug, "before actorinterval sit-lose"),
            ActorInterval(diner, "sit-lose"),
            Func(self.notify.debug, "before deathSuit.setHpr"),
            Func(deathSuit.setHpr, diner.getHpr()),
            Func(self.notify.debug, "before diner.hide"),
            Func(diner.hide),
            Func(self.notify.debug, "before deathSuit.reparentTo"),
            Func(deathSuit.reparentTo, self.chairLocators[chairIndex]),
            Func(self.notify.debug, "befor ActorInterval lose"),
            ActorInterval(deathSuit, "lose", duration=MovieUtil.SUIT_LOSE_DURATION),
            Func(self.notify.debug, "before remove deathsuit"),
            Func(removeDeathSuit, diner, deathSuit, name="remove-death-suit-%d-%d" % (chairIndex, self.index)),
            Func(self.notify.debug, "diner.stash"),
            Func(diner.stash),
        )
        spinningSound = base.loadSfx("phase_3.5/audio/sfx/Cog_Death.mp3")
        deathSound = base.loadSfx("phase_3.5/audio/sfx/ENC_cogfall_apart.mp3")
        deathSoundTrack = Sequence(
            Wait(0.80000000000000004),
            SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.20000000000000001, node=deathSuit),
            SoundInterval(
                spinningSound, duration=3.0, startTime=0.59999999999999998, volume=0.80000000000000004, node=deathSuit
            ),
            SoundInterval(deathSound, volume=0.32000000000000001, node=deathSuit),
        )
        intervalName = "dinerDie-%d-%d" % (self.index, chairIndex)
        deathIval = Parallel(ival, deathSoundTrack)
        deathIval.start()
        self.activeIntervals[intervalName] = deathIval

    def changeDinerToHidden(self, chairIndex):
        self.removeFoodModel(chairIndex)
        indicator = self.dinerStatusIndicators.get(chairIndex)
        if indicator:
            indicator.request("Inactive")

        diner = self.diners[chairIndex]
        diner.hide()

    def setAllDinersToSitNeutral(self):
        startFrame = 0
        for diner in self.diners.values():
            if not diner.isHidden():
                diner.loop("sit", fromFrame=startFrame)
                startFrame += 1
                continue

    def cleanupIntervals(self):
        for interval in self.activeIntervals.values():
            interval.finish()

        self.activeIntervals = {}

    def clearInterval(self, name, finish=1):
        if self.activeIntervals.has_key(name):
            ival = self.activeIntervals[name]
            if finish:
                ival.finish()
            else:
                ival.pause()
            if self.activeIntervals.has_key(name):
                del self.activeIntervals[name]

        else:
            self.notify.debug("interval: %s already cleared" % name)

    def finishInterval(self, name):
        if self.activeIntervals.has_key(name):
            interval = self.activeIntervals[name]
            interval.finish()

    def getNotDeadInfo(self):
        notDeadList = []
        for i in xrange(self.numDiners):
            if self.dinerStatus[i] != self.DEAD:
                notDeadList.append((self.index, i, 12))
                continue

        return notDeadList

    def enterOn(self):
        pass

    def exitOn(self):
        pass

    def enterInactive(self):
        for chairIndex in xrange(self.numDiners):
            indicator = self.dinerStatusIndicators.get(chairIndex)
            if indicator:
                indicator.request("Inactive")

            self.removeFoodModel(chairIndex)

    def exitInactive(self):
        pass

    def enterFree(self):
        self.resetPowerBar()
        if self.fadeTrack:
            self.fadeTrack.finish()
            self.fadeTrack = None

        self.prepareForPhaseFour()
        if self.avId == localAvatar.doId:
            self.tableGroup.setAlphaScale(0.29999999999999999)
            self.tableGroup.setTransparency(1)
            taskMgr.doMethodLater(5, self._DistributedBanquetTable__allowDetect, self.triggerName)
            self.fadeTrack = Sequence(
                Func(self.tableGroup.setTransparency, 1),
                self.tableGroup.colorScaleInterval(0.20000000000000001, VBase4(1, 1, 1, 0.29999999999999999)),
            )
            self.fadeTrack.start()
            self.allowLocalRequestControl = False
        else:
            self.allowLocalRequestControl = True
        self.avId = 0

    def exitFree(self):
        pass

    def touchedTable(self, colEntry):
        tableIndex = int(colEntry.getIntoNodePath().getTag("tableIndex"))
        if self.state == "Free" and self.avId == 0 and self.allowLocalRequestControl:
            self.d_requestControl()

    def prepareForPhaseFour(self):
        if not self.preparedForPhaseFour:
            for i in xrange(8):
                chair = self.tableGroup.find("**/chair_%d" % (i + 1))
                if not chair.isEmpty():
                    chair.hide()

                colChairs = self.tableGroup.findAllMatches("**/ChairCol*")
                for i in xrange(colChairs.getNumPaths()):
                    col = colChairs.getPath(i)
                    col.stash()

                colChairs = self.tableGroup.findAllMatches("**/collision_chair*")
                for i in xrange(colChairs.getNumPaths()):
                    col = colChairs.getPath(i)
                    col.stash()

            tableCol = self.tableGroup.find("**/collision_table")
            colName = "TableCol-%d" % self.index
            tableCol.setTag("tableIndex", str(self.index))
            tableCol.setName(colName)
            tableCol.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.BanquetTableBitmask)
            self.accept("enter" + colName, self.touchedTable)
            self.preparedForPhaseFour = True
            self.waterPitcherModel = loader.loadModel("phase_12/models/bossbotHQ/tt_m_ara_bhq_seltzerBottle")
            lampNode = self.tableGroup.find("**/lamp_med_5")
            pos = lampNode.getPos(self.tableGroup)
            lampNode.hide()
            bottleLocator = self.tableGroup.find("**/bottle_locator")
            pos = bottleLocator.getPos(self.tableGroup)
            self.waterPitcherNode = self.tableGroup.attachNewNode("pitcherNode")
            self.waterPitcherNode.setPos(pos)
            self.waterPitcherModel.reparentTo(self.waterPitcherNode)
            self.waterPitcherModel.ls()
            self.nozzle = self.waterPitcherModel.find("**/nozzle_tip")
            self.handLocator = self.waterPitcherModel.find("**/hand_locator")
            self.handPos = self.handLocator.getPos()

    def d_requestControl(self):
        self.sendUpdate("requestControl")

    def d_requestFree(self, gotHitByBoss):
        self.sendUpdate("requestFree", [gotHitByBoss])

    def enterControlled(self, avId):
        self.prepareForPhaseFour()
        self.avId = avId
        toon = base.cr.doId2do.get(avId)
        if not toon:
            return None

        self.toon = toon
        self.grabTrack = self.makeToonGrabInterval(toon)
        self.notify.debug("grabTrack=%s" % self.grabTrack)
        self.pitcherCamPos = Point3(0, -50, 40)
        self.pitcherCamHpr = Point3(0, -21, 0)
        if avId == localAvatar.doId:
            self.boss.toMovieMode()
            self._DistributedBanquetTable__enableControlInterface()
            self.startPosHprBroadcast()
            self.grabTrack = Sequence(
                self.grabTrack,
                Func(camera.wrtReparentTo, localAvatar),
                LerpPosHprInterval(camera, 1, self.pitcherCamPos, self.pitcherCamHpr),
                Func(self.boss.toCraneMode),
            )
            if self.TugOfWarControls:
                self._DistributedBanquetTable__spawnUpdateKeyPressRateTask()

            self.accept("exitCrane", self.gotBossZapped)
        else:
            self.startSmooth()
            toon.stopSmooth()
        self.grabTrack.start()

    def exitControlled(self):
        self.ignore("exitCrane")
        if self.grabTrack:
            self.grabTrack.finish()
            self.grabTrack = None

        nextState = self.getCurrentOrNextState()
        self.notify.debug("nextState=%s" % nextState)
        if nextState == "Flat":
            place = base.cr.playGame.getPlace()
            self.notify.debug("%s" % place.fsm)
            if self.avId == localAvatar.doId:
                self._DistributedBanquetTable__disableControlInterface()

        elif self.toon and not self.toon.isDisabled():
            self.toon.loop("neutral")
            self.toon.startSmooth()

        self.releaseTrack = self.makeToonReleaseInterval(self.toon)
        self.stopPosHprBroadcast()
        self.stopSmooth()
        if self.avId == localAvatar.doId:
            localAvatar.wrtReparentTo(render)
            self._DistributedBanquetTable__disableControlInterface()
            camera.reparentTo(base.localAvatar)
            camera.setPos(base.localAvatar.cameraPositions[0][0])
            camera.setHpr(0, 0, 0)
            self.goToFinalBattle()
            self.safeBossToFinalBattleMode()
        else:
            toon = base.cr.doId2do.get(self.avId)
            if toon:
                toon.wrtReparentTo(render)

        self.releaseTrack.start()

    def safeBossToFinalBattleMode(self):
        if self.boss:
            self.boss.toFinalBattleMode()

    def goToFinalBattle(self):
        if self.cr:
            place = self.cr.playGame.getPlace()
            if place and hasattr(place, "fsm"):
                if place.fsm.getCurrentState().getName() == "crane":
                    place.setState("finalBattle")

    def makeToonGrabInterval(self, toon):
        toon.pose("leverNeutral", 0)
        toon.update()
        rightHandPos = toon.rightHand.getPos(toon)
        self.toonPitcherPosition = Point3(self.handPos[0] - rightHandPos[0], self.handPos[1] - rightHandPos[1], 0)
        destZScale = rightHandPos[2] / self.handPos[2]
        grabIval = Sequence(
            Func(toon.wrtReparentTo, self.waterPitcherNode),
            Func(toon.loop, "neutral"),
            Parallel(
                ActorInterval(toon, "jump"),
                Sequence(
                    Wait(0.42999999999999999),
                    Parallel(
                        ProjectileInterval(
                            toon,
                            duration=0.90000000000000002,
                            startPos=toon.getPos(self.waterPitcherNode),
                            endPos=self.toonPitcherPosition,
                        ),
                        LerpHprInterval(toon, 0.90000000000000002, Point3(0, 0, 0)),
                        LerpScaleInterval(self.waterPitcherModel, 0.90000000000000002, Point3(1, 1, destZScale)),
                    ),
                ),
            ),
            Func(toon.setPos, self.toonPitcherPosition),
            Func(toon.loop, "leverNeutral"),
        )
        return grabIval

    def makeToonReleaseInterval(self, toon):
        temp1 = self.waterPitcherNode.attachNewNode("temp1")
        temp1.setPos(self.toonPitcherPosition)
        temp2 = self.waterPitcherNode.attachNewNode("temp2")
        temp2.setPos(0, -10, -self.waterPitcherNode.getZ())
        startPos = temp1.getPos(render)
        endPos = temp2.getPos(render)
        temp1.removeNode()
        temp2.removeNode()

        def getSlideToPos(toon=toon):
            return render.getRelativePoint(toon, Point3(0, -10, 0))

        if self.gotHitByBoss:
            self.notify.debug("creating zap interval instead")
            grabIval = Sequence(
                Func(toon.loop, "neutral"),
                Func(toon.wrtReparentTo, render),
                Parallel(ActorInterval(toon, "slip-backward"), toon.posInterval(0.5, getSlideToPos, fluid=1)),
            )
        else:
            grabIval = Sequence(
                Func(toon.loop, "neutral"),
                Func(toon.wrtReparentTo, render),
                Parallel(
                    ActorInterval(toon, "jump"),
                    Sequence(
                        Wait(0.42999999999999999),
                        ProjectileInterval(toon, duration=0.90000000000000002, startPos=startPos, endPos=endPos),
                    ),
                ),
            )
        return grabIval

    def b_clearSmoothing(self):
        self.d_clearSmoothing()
        self.clearSmoothing()

    def d_clearSmoothing(self):
        self.sendUpdate("clearSmoothing", [0])

    def clearSmoothing(self, bogus=None):
        self.pitcherSmoother.clearPositions(1)

    def doSmoothTask(self, task):
        self.pitcherSmoother.computeAndApplySmoothHpr(self.waterPitcherNode)
        return Task.cont

    def startSmooth(self):
        if not self.smoothStarted:
            taskName = self.smoothName
            taskMgr.remove(taskName)
            self.reloadPosition()
            taskMgr.add(self.doSmoothTask, taskName)
            self.smoothStarted = 1

    def stopSmooth(self):
        if self.smoothStarted:
            taskName = self.smoothName
            taskMgr.remove(taskName)
            self.forceToTruePosition()
            self.smoothStarted = 0

    def _DistributedBanquetTable__enableControlInterface(self):
        gui = loader.loadModel("phase_3.5/models/gui/avatar_panel_gui")
        self.closeButton = DirectButton(
            image=(
                gui.find("**/CloseBtn_UP"),
                gui.find("**/CloseBtn_DN"),
                gui.find("**/CloseBtn_Rllvr"),
                gui.find("**/CloseBtn_UP"),
            ),
            relief=None,
            scale=2,
            text=TTLocalizer.BossbotPitcherLeave,
            text_scale=0.040000000000000001,
            text_pos=(0, -0.070000000000000007),
            text_fg=VBase4(1, 1, 1, 1),
            pos=(1.05, 0, -0.81999999999999995),
            command=self._DistributedBanquetTable__exitPitcher,
        )
        self.accept("escape", self._DistributedBanquetTable__exitPitcher)
        self.accept("control", self._DistributedBanquetTable__controlPressed)
        self.accept("control-up", self._DistributedBanquetTable__controlReleased)
        self.accept("InputState-forward", self._DistributedBanquetTable__upArrow)
        self.accept("InputState-reverse", self._DistributedBanquetTable__downArrow)
        self.accept("InputState-turnLeft", self._DistributedBanquetTable__leftArrow)
        self.accept("InputState-turnRight", self._DistributedBanquetTable__rightArrow)
        self.accept("arrow_up", self._DistributedBanquetTable__upArrowKeyPressed)
        self.accept("arrow_down", self._DistributedBanquetTable__downArrowKeyPressed)
        taskMgr.add(self._DistributedBanquetTable__watchControls, self.watchControlsName)
        taskMgr.doMethodLater(5, self._DistributedBanquetTable__displayPitcherAdvice, self.pitcherAdviceName)
        self.arrowVert = 0
        self.arrowHorz = 0
        self.powerBar.show()

    def _DistributedBanquetTable__disableControlInterface(self):
        if self.closeButton:
            self.closeButton.destroy()
            self.closeButton = None

        self._DistributedBanquetTable__cleanupPitcherAdvice()
        self.ignore("escape")
        self.ignore("control")
        self.ignore("control-up")
        self.ignore("InputState-forward")
        self.ignore("InputState-reverse")
        self.ignore("InputState-turnLeft")
        self.ignore("InputState-turnRight")
        self.ignore("arrow_up")
        self.ignore("arrow_down")
        self.arrowVert = 0
        self.arrowHorz = 0
        taskMgr.remove(self.watchControlsName)
        taskMgr.remove(self.waterPowerTaskName)
        self.resetPowerBar()
        self.aimStart = None
        self.powerBar.hide()
        if self.TugOfWarControls:
            self._DistributedBanquetTable__killUpdateKeyPressRateTask()
            self.keyTTL = []

        self._DistributedBanquetTable__setMoveSound(None)

    def _DistributedBanquetTable__displayPitcherAdvice(self, task):
        if self.pitcherAdviceLabel == None:
            self.pitcherAdviceLabel = DirectLabel(
                text=TTLocalizer.BossbotPitcherAdvice,
                text_fg=VBase4(1, 1, 1, 1),
                text_align=TextNode.ACenter,
                relief=None,
                pos=(0, 0, 0.68999999999999995),
                scale=0.10000000000000001,
            )

    def _DistributedBanquetTable__cleanupPitcherAdvice(self):
        if self.pitcherAdviceLabel:
            self.pitcherAdviceLabel.destroy()
            self.pitcherAdviceLabel = None

        taskMgr.remove(self.pitcherAdviceName)

    def showExiting(self):
        if self.closeButton:
            self.closeButton.destroy()
            self.closeButton = DirectLabel(
                relief=None,
                text=TTLocalizer.BossbotPitcherLeaving,
                pos=(1.05, 0, -0.88),
                text_pos=(0, 0),
                text_scale=0.059999999999999998,
                text_fg=VBase4(1, 1, 1, 1),
            )

        self._DistributedBanquetTable__cleanupPitcherAdvice()

    def _DistributedBanquetTable__exitPitcher(self):
        self.showExiting()
        self.d_requestFree(False)

    def _DistributedBanquetTable__controlPressed(self):
        self._DistributedBanquetTable__cleanupPitcherAdvice()
        if self.TugOfWarControls:
            if self.power:
                self.aimStart = 1
                self._DistributedBanquetTable__endFireWater()

        elif self.state == "Controlled":
            self._DistributedBanquetTable__beginFireWater()

    def _DistributedBanquetTable__controlReleased(self):
        if self.TugOfWarControls:
            pass
        1
        if self.state == "Controlled":
            self._DistributedBanquetTable__endFireWater()

    def _DistributedBanquetTable__upArrow(self, pressed):
        self._DistributedBanquetTable__incrementChangeSeq()
        self._DistributedBanquetTable__cleanupPitcherAdvice()
        if pressed:
            self.arrowVert = 1
        elif self.arrowVert > 0:
            self.arrowVert = 0

    def _DistributedBanquetTable__downArrow(self, pressed):
        self._DistributedBanquetTable__incrementChangeSeq()
        self._DistributedBanquetTable__cleanupPitcherAdvice()
        if pressed:
            self.arrowVert = -1
        elif self.arrowVert < 0:
            self.arrowVert = 0

    def _DistributedBanquetTable__rightArrow(self, pressed):
        self._DistributedBanquetTable__incrementChangeSeq()
        self._DistributedBanquetTable__cleanupPitcherAdvice()
        if pressed:
            self.arrowHorz = 1
        elif self.arrowHorz > 0:
            self.arrowHorz = 0

    def _DistributedBanquetTable__leftArrow(self, pressed):
        self._DistributedBanquetTable__incrementChangeSeq()
        self._DistributedBanquetTable__cleanupPitcherAdvice()
        if pressed:
            self.arrowHorz = -1
        elif self.arrowHorz < 0:
            self.arrowHorz = 0

    def _DistributedBanquetTable__incrementChangeSeq(self):
        self.changeSeq = self.changeSeq + 1 & 255

    def stopPosHprBroadcast(self):
        taskName = self.posHprBroadcastName
        taskMgr.remove(taskName)

    def startPosHprBroadcast(self):
        taskName = self.posHprBroadcastName
        self.b_clearSmoothing()
        self.d_sendPitcherPos()
        taskMgr.remove(taskName)
        taskMgr.doMethodLater(
            self._DistributedBanquetTable__broadcastPeriod, self._DistributedBanquetTable__posHprBroadcast, taskName
        )

    def _DistributedBanquetTable__posHprBroadcast(self, task):
        self.d_sendPitcherPos()
        taskName = self.posHprBroadcastName
        taskMgr.doMethodLater(
            self._DistributedBanquetTable__broadcastPeriod, self._DistributedBanquetTable__posHprBroadcast, taskName
        )
        return Task.done

    def d_sendPitcherPos(self):
        timestamp = globalClockDelta.getFrameNetworkTime()
        self.sendUpdate("setPitcherPos", [self.changeSeq, self.waterPitcherNode.getH(), timestamp])

    def setPitcherPos(self, changeSeq, h, timestamp):
        self.changeSeq = changeSeq
        if self.smoothStarted:
            now = globalClock.getFrameTime()
            local = globalClockDelta.networkToLocalTime(timestamp, now)
            self.pitcherSmoother.setH(h)
            self.pitcherSmoother.setTimestamp(local)
            self.pitcherSmoother.markPosition()
        else:
            self.waterPitcherNode.setH(h)

    def _DistributedBanquetTable__watchControls(self, task):
        if self.arrowHorz:
            self._DistributedBanquetTable__movePitcher(self.arrowHorz)
        else:
            self._DistributedBanquetTable__setMoveSound(None)
        return Task.cont

    def _DistributedBanquetTable__movePitcher(self, xd):
        dt = globalClock.getDt()
        h = self.waterPitcherNode.getH() - xd * self.rotateSpeed * dt
        h %= 360
        self.notify.debug(
            "rotSpeed=%.2f curH=%.2f  xd =%.2f, dt = %.2f, h=%.2f"
            % (self.rotateSpeed, self.waterPitcherNode.getH(), xd, dt, h)
        )
        limitH = h
        self.waterPitcherNode.setH(limitH)
        if xd:
            self._DistributedBanquetTable__setMoveSound(self.pitcherMoveSfx)

    def reloadPosition(self):
        self.pitcherSmoother.clearPositions(0)
        self.pitcherSmoother.setHpr(self.waterPitcherNode.getHpr())
        self.pitcherSmoother.setPhonyTimestamp()

    def forceToTruePosition(self):
        if self.pitcherSmoother.getLatestPosition():
            self.pitcherSmoother.applySmoothHpr(self.waterPitcherNode)

        self.pitcherSmoother.clearPositions(1)

    def getSprayTrack(
        self, color, origin, target, dScaleUp, dHold, dScaleDown, horizScale=1.0, vertScale=1.0, parent=render
    ):
        track = Sequence()
        SPRAY_LEN = 1.5
        sprayProp = MovieUtil.globalPropPool.getProp("spray")
        sprayScale = hidden.attachNewNode("spray-parent")
        sprayRot = hidden.attachNewNode("spray-rotate")
        spray = sprayRot
        spray.setColor(color)
        if color[3] < 1.0:
            spray.setTransparency(1)

        def showSpray(sprayScale, sprayRot, sprayProp, origin, target, parent):
            if callable(origin):
                origin = origin()

            if callable(target):
                target = target()

            sprayRot.reparentTo(parent)
            sprayRot.clearMat()
            sprayScale.reparentTo(sprayRot)
            sprayScale.clearMat()
            sprayProp.reparentTo(sprayScale)
            sprayProp.clearMat()
            sprayRot.setPos(origin)
            sprayRot.lookAt(Point3(target))

        track.append(Func(showSpray, sprayScale, sprayRot, sprayProp, origin, target, parent))

        def calcTargetScale(target=target, origin=origin, horizScale=horizScale, vertScale=vertScale):
            if callable(target):
                target = target()

            if callable(origin):
                origin = origin()

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

        track.append(LerpScaleInterval(sprayScale, dScaleUp, calcTargetScale, startScale=Point3(0.01, 0.01, 0.01)))
        track.append(Func(self.checkHitObject))
        track.append(Wait(dHold))

        def prepareToShrinkSpray(spray, sprayProp, origin, target):
            if callable(target):
                target = target()

            if callable(origin):
                origin = origin()

            sprayProp.setPos(Point3(0.0, -SPRAY_LEN, 0.0))
            spray.setPos(target)

        track.append(Func(prepareToShrinkSpray, spray, sprayProp, origin, target))
        track.append(LerpScaleInterval(sprayScale, dScaleDown, Point3(0.01, 0.01, 0.01)))

        def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool):
            sprayProp.detachNode()
            MovieUtil.removeProp(sprayProp)
            sprayRot.removeNode()
            sprayScale.removeNode()

        track.append(Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, MovieUtil.globalPropPool))
        return track

    def checkHitObject(self):
        if not self.hitObject:
            return None

        if self.avId != base.localAvatar.doId:
            return None

        tag = self.hitObject.getNetTag("pieCode")
        pieCode = int(tag)
        if pieCode == ToontownGlobals.PieCodeBossCog:
            self.hitBossSoundInterval.start()
            self.sendUpdate("waterHitBoss", [self.index])
            if self.TugOfWarControls:
                damage = 1
                if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD:
                    damage = 1
                elif self.lastPowerFired < self.RED_POWER_THRESHOLD:
                    damage = 2
                else:
                    damage = 3
                self.boss.d_hitBoss(damage)
            else:
                damage = 1
                if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD:
                    damage = 1
                elif self.lastPowerFired < self.RED_POWER_THRESHOLD:
                    damage = 2
                else:
                    damage = 3
                self.boss.d_hitBoss(damage)

    def waterHitBoss(self, tableIndex):
        if self.index == tableIndex:
            self.hitBossSoundInterval.start()

    def setupPowerBar(self):
        self.powerBar = DirectWaitBar(
            pos=(0.0, 0, -0.93999999999999995),
            relief=DGG.SUNKEN,
            frameSize=(-2.0, 2.0, -0.20000000000000001, 0.20000000000000001),
            borderWidth=(0.02, 0.02),
            scale=0.25,
            range=1,
            sortOrder=50,
            frameColor=(0.5, 0.5, 0.5, 0.5),
            barColor=(0.75, 0.75, 1.0, 0.80000000000000004),
            text="",
            text_scale=0.26000000000000001,
            text_fg=(1, 1, 1, 1),
            text_align=TextNode.ACenter,
            text_pos=(0, -0.050000000000000003),
        )
        self.power = 0
        self.powerBar["value"] = self.power
        self.powerBar.hide()

    def resetPowerBar(self):
        self.power = 0
        self.powerBar["value"] = self.power
        self.powerBar["text"] = ""
        self.keyTTL = []

    def _DistributedBanquetTable__beginFireWater(self):
        if self.fireTrack and self.fireTrack.isPlaying():
            return None

        if self.aimStart != None:
            return None

        if not self.state == "Controlled":
            return None

        if not self.avId == localAvatar.doId:
            return None

        time = globalClock.getFrameTime()
        self.aimStart = time
        messenger.send("wakeup")
        taskMgr.add(self._DistributedBanquetTable__updateWaterPower, self.waterPowerTaskName)

    def _DistributedBanquetTable__endFireWater(self):
        if self.aimStart == None:
            return None

        if not self.state == "Controlled":
            return None

        if not self.avId == localAvatar.doId:
            return None

        taskMgr.remove(self.waterPowerTaskName)
        messenger.send("wakeup")
        self.aimStart = None
        origin = self.nozzle.getPos(render)
        target = self.boss.getPos(render)
        angle = deg2Rad(self.waterPitcherNode.getH() + 90)
        x = math.cos(angle)
        y = math.sin(angle)
        fireVector = Point3(x, y, 0)
        if self.power < 0.001:
            self.power = 0.001

        self.lastPowerFired = self.power
        fireVector *= self.fireLength * self.power
        target = origin + fireVector
        segment = CollisionSegment(origin[0], origin[1], origin[2], target[0], target[1], target[2])
        fromObject = render.attachNewNode(CollisionNode("pitcherColNode"))
        fromObject.node().addSolid(segment)
        fromObject.node().setFromCollideMask(
            ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask
        )
        fromObject.node().setIntoCollideMask(BitMask32.allOff())
        queue = CollisionHandlerQueue()
        base.cTrav.addCollider(fromObject, queue)
        base.cTrav.traverse(render)
        queue.sortEntries()
        self.hitObject = None
        if queue.getNumEntries():
            entry = queue.getEntry(0)
            target = entry.getSurfacePoint(render)
            self.hitObject = entry.getIntoNodePath()

        base.cTrav.removeCollider(fromObject)
        fromObject.removeNode()
        self.d_firingWater(origin, target)
        self.fireWater(origin, target)
        self.resetPowerBar()

    def _DistributedBanquetTable__updateWaterPower(self, task):
        if not self.powerBar:
            print "### no power bar!!!"
            return task.done

        newPower = self._DistributedBanquetTable__getWaterPower(globalClock.getFrameTime())
        self.power = newPower
        self.powerBar["value"] = newPower
        if self.power < self.YELLOW_POWER_THRESHOLD:
            self.powerBar["barColor"] = VBase4(0.75, 0.75, 1.0, 0.80000000000000004)
        elif self.power < self.RED_POWER_THRESHOLD:
            self.powerBar["barColor"] = VBase4(1.0, 1.0, 0.0, 0.80000000000000004)
        else:
            self.powerBar["barColor"] = VBase4(1.0, 0.0, 0.0, 0.80000000000000004)
        return task.cont

    def _DistributedBanquetTable__getWaterPower(self, time):
        elapsed = max(time - self.aimStart, 0.0)
        t = elapsed / self.waterPowerSpeed
        exponent = self.waterPowerExponent
        if t > 1:
            t = t % 1

        power = 1 - math.pow(1 - t, exponent)
        if power > 1.0:
            power = 1.0

        return power

    def d_firingWater(self, origin, target):
        self.sendUpdate("firingWater", [origin[0], origin[1], origin[2], target[0], target[1], target[2]])

    def firingWater(self, startX, startY, startZ, endX, endY, endZ):
        origin = Point3(startX, startY, startZ)
        target = Point3(endX, endY, endZ)
        self.fireWater(origin, target)

    def fireWater(self, origin, target):
        color = VBase4(0.75, 0.75, 1, 0.80000000000000004)
        dScaleUp = 0.10000000000000001
        dHold = 0.29999999999999999
        dScaleDown = 0.10000000000000001
        horizScale = 0.10000000000000001
        vertScale = 0.10000000000000001
        sprayTrack = self.getSprayTrack(color, origin, target, dScaleUp, dHold, dScaleDown, horizScale, vertScale)
        duration = self.squirtSfx.length()
        if sprayTrack.getDuration() < duration:
            duration = sprayTrack.getDuration()

        soundTrack = SoundInterval(self.squirtSfx, node=self.waterPitcherModel, duration=duration)
        self.fireTrack = Parallel(sprayTrack, soundTrack)
        self.fireTrack.start()

    def getPos(self, wrt=render):
        return self.tableGroup.getPos(wrt)

    def getLocator(self):
        return self.tableGroup

    def enterFlat(self, avId):
        self.prepareForPhaseFour()
        self.resetPowerBar()
        self.notify.debug("enterFlat %d" % self.index)
        if self.avId:
            toon = base.cr.doId2do.get(self.avId)
            if toon:
                toon.wrtReparentTo(render)
                toon.setZ(0)

        self.tableGroup.setScale(1, 1, 0.01)
        if self.avId and self.avId == localAvatar.doId:
            localAvatar.b_squish(ToontownGlobals.BossCogDamageLevels[ToontownGlobals.BossCogMoveAttack])

    def exitFlat(self):
        self.tableGroup.setScale(1.0)
        if self.avId:
            toon = base.cr.doId2do.get(self.avId)
            if toon:
                if toon == localAvatar:
                    self.boss.toCraneMode()
                    toon.b_setAnimState("neutral")

                toon.setAnimState("neutral")
                toon.loop("leverNeutral")

    def _DistributedBanquetTable__allowDetect(self, task):
        if self.fadeTrack:
            self.fadeTrack.finish()

        self.fadeTrack = Sequence(
            self.tableGroup.colorScaleInterval(0.20000000000000001, VBase4(1, 1, 1, 1)),
            Func(self.tableGroup.clearColorScale),
            Func(self.tableGroup.clearTransparency),
        )
        self.fadeTrack.start()
        self.allowLocalRequestControl = True

    def gotBossZapped(self):
        self.showExiting()
        self.d_requestFree(True)

    def _DistributedBanquetTable__upArrowKeyPressed(self):
        if self.TugOfWarControls:
            self._DistributedBanquetTable__pressHandler(0)

    def _DistributedBanquetTable__downArrowKeyPressed(self):
        if self.TugOfWarControls:
            self._DistributedBanquetTable__pressHandler(1)

    def _DistributedBanquetTable__pressHandler(self, index):
        if index == self.buttons[0]:
            self.keyTTL.insert(0, 1.0)
            if not self.OnlyUpArrow:
                self.buttons.reverse()

    def _DistributedBanquetTable__spawnUpdateKeyPressRateTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))
        taskMgr.doMethodLater(
            0.10000000000000001,
            self._DistributedBanquetTable__updateKeyPressRateTask,
            self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK),
        )

    def _DistributedBanquetTable__killUpdateKeyPressRateTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))

    def _DistributedBanquetTable__updateKeyPressRateTask(self, task):
        if self.state not in "Controlled":
            return Task.done

        for i in range(len(self.keyTTL)):
            self.keyTTL[i] -= 0.10000000000000001

        for i in range(len(self.keyTTL)):
            if self.keyTTL[i] <= 0:
                a = self.keyTTL[0:i]
                del self.keyTTL
                self.keyTTL = a
                break
                continue

        self.keyRate = len(self.keyTTL)
        keyRateDiff = self.keyRate - self.BASELINE_KEY_RATE
        diffPower = keyRateDiff / 300.0
        if self.power < 1 and diffPower > 0:
            diffPower = diffPower * math.pow(1 - self.power, 1.25)

        newPower = self.power + diffPower
        if newPower > 1:
            newPower = 1
        elif newPower < 0:
            newPower = 0

        self.notify.debug("diffPower=%.2f keyRate = %d, newPower=%.2f" % (diffPower, self.keyRate, newPower))
        self.power = newPower
        self.powerBar["value"] = newPower
        if self.power < self.YELLOW_POWER_THRESHOLD:
            self.powerBar["barColor"] = VBase4(0.75, 0.75, 1.0, 0.80000000000000004)
        elif self.power < self.RED_POWER_THRESHOLD:
            self.powerBar["barColor"] = VBase4(1.0, 1.0, 0.0, 0.80000000000000004)
        else:
            self.powerBar["barColor"] = VBase4(1.0, 0.0, 0.0, 0.80000000000000004)
        self._DistributedBanquetTable__spawnUpdateKeyPressRateTask()
        return Task.done

    def _DistributedBanquetTable__setMoveSound(self, sfx):
        if sfx != self.moveSound:
            if self.moveSound:
                self.moveSound.stop()

            self.moveSound = sfx
            if self.moveSound:
                base.playSfx(self.moveSound, looping=1, volume=0.5)