class PickPocketAttack(Attack):
    notify = directNotify.newCategory('PickPocketAttack')
    attack = 'pickpocket'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.dollar = None
        self.pickSfx = None
        return

    def doAttack(self, ts=0):
        self.dollar = loader.loadModel('phase_5/models/props/1dollar-bill-mod.bam')
        self.dollar.setY(0.22)
        self.dollar.setHpr(289.18, 252.75, 0.0)
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doPickPocketAttack')
        else:
            name = 'doPickPocketAttack'
        self.suitTrack = Parallel(ActorInterval(self.suit, 'pickpocket'), Sequence(Wait(0.4), Func(self.attemptDamage)), name=name)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def attemptDamage(self):
        shouldDamage = False
        suitH = self.suit.getH(render) % 360
        myH = base.localAvatar.getH(render) % 360
        if not -90.0 <= suitH - myH <= 90.0:
            if base.localAvatar.getDistance(self.suit) <= 15.0:
                shouldDamage = True
        if shouldDamage:
            self.playWeaponSound()
            self.dollar.reparentTo(self.suit.find('**/joint_Rhold'))
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def playWeaponSound(self):
        self.pickSfx = base.audio3d.loadSfx('phase_5/audio/sfx/SA_pick_pocket.ogg')
        base.audio3d.attachSoundToObject(self.pickSfx, self.suit)
        self.pickSfx.play()

    def cleanup(self):
        Attack.cleanup(self)
        if self.pickSfx:
            self.pickSfx.stop()
            self.pickSfx = None
        if self.dollar:
            self.dollar.removeNode()
            self.dollar = None
        return
예제 #2
0
class PickPocketAttack(Attack):
    notify = directNotify.newCategory('PickPocketAttack')
    attack = 'pickpocket'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.dollar = None
        self.pickSfx = None
        return

    def doAttack(self, ts = 0):
        self.dollar = loader.loadModel('phase_5/models/props/1dollar-bill-mod.bam')
        self.dollar.setY(0.22)
        self.dollar.setHpr(289.18, 252.75, 0.0)
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doPickPocketAttack')
        else:
            name = 'doPickPocketAttack'
        self.suitTrack = Parallel(ActorInterval(self.suit, 'pickpocket'), Sequence(Wait(0.4), Func(self.attemptDamage)), name=name)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def attemptDamage(self):
        shouldDamage = False
        suitH = self.suit.getH(render) % 360
        myH = base.localAvatar.getH(render) % 360
        if not -90.0 <= suitH - myH <= 90.0:
            if base.localAvatar.getDistance(self.suit) <= 15.0:
                shouldDamage = True
        if shouldDamage:
            self.playWeaponSound()
            self.dollar.reparentTo(self.suit.find('**/joint_Rhold'))
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def playWeaponSound(self):
        self.pickSfx = base.audio3d.loadSfx('phase_5/audio/sfx/SA_pick_pocket.mp3')
        base.audio3d.attachSoundToObject(self.pickSfx, self.suit)
        self.pickSfx.play()

    def cleanup(self):
        Attack.cleanup(self)
        if self.pickSfx:
            self.pickSfx.stop()
            self.pickSfx = None
        if self.dollar:
            self.dollar.removeNode()
            self.dollar = None
        return
class DistributedPieTurret(DistributedAvatar, DistributedSmoothNode):
    notify = directNotify.newCategory('DistributedPieTurret')

    def __init__(self, cr):
        DistributedAvatar.__init__(self, cr)
        DistributedSmoothNode.__init__(self, cr)
        self.fsm = ClassicFSM(
            'DistributedPieTurret',
            [
                State('off', self.enterOff, self.exitOff),
                State('scan', self.enterScan, self.exitScan),
                State('shoot', self.enterShoot, self.exitShoot)
             ],
             'off', 'off'
         )
        self.fsm.enterInitialState()
        self.reloadTime = 0.25
        self.cannon = None
        self.track = None
        self.owner = None
        self.gag = None
        self.readyGag = None
        self.hitGag = None
        self.explosion = None
        self.wallCollNode = None
        self.eventCollNode = None
        self.event = None
        self.suit = None
        self.eventId = None
        self.entities = []
        self.upgradeID = None
        self.deathEvent = None

    def setOwner(self, avatar):
        self.owner = avatar

    def getOwner(self):
        return self.owner

    def setGag(self, upgradeId):
        gags = {0 : CIGlobals.WholeCreamPie, 1 : CIGlobals.WholeFruitPie, 2 : CIGlobals.BirthdayCake, 3 : CIGlobals.WeddingCake}
        self.gag = gags.get(upgradeId)
        if not self.readyGag:
            self.loadGagInTurret()

    def b_setGag(self, upgradeId):
        self.sendUpdate('setGag', [upgradeId])
        self.setGag(upgradeId)
        self.upgradeID = upgradeId

    def getGag(self):
        return self.gag

    def getGagID(self):
        return self.upgradeID

    def generate(self):
        DistributedAvatar.generate(self)
        DistributedSmoothNode.generate(self)

    def announceGenerate(self):
        DistributedAvatar.announceGenerate(self)
        DistributedSmoothNode.announceGenerate(self)
        self.healthLabel.setScale(1.1)
        self.deathEvent = self.uniqueName('DistributedPieTurret-death')
        self.makeTurret()

    def disable(self):
        self.fsm.requestFinalState()
        del self.fsm

        # This should fix crashes related to Sequences.
        if self.track:
            self.track.pause()
            self.track = None

        # Cleanup entities.
        for ent in self.entities:
            ent.cleanup()
        self.entities = None

        # Get rid of explosions.
        if self.explosion:
            self.explosion.removeNode()
            self.explosion = None

        self.removeTurret()
        DistributedSmoothNode.disable(self)
        DistributedAvatar.disable(self)

    def showAndMoveHealthLabel(self):
        self.unstashHpLabel()
        self.stopMovingHealthLabel()
        moveTrack = LerpPosInterval(self.healthLabel,
                                duration = 0.5,
                                pos = Point3(0, 0, 5),
                                startPos = Point3(0, 0, 0),
                                blendType = 'easeOut')
        self.healthLabelTrack = Sequence(moveTrack, Wait(1.0), Func(self.stashHpLabel))
        self.healthLabelTrack.start()

    # BEGIN STATES

    def enterShoot(self, suitId):
        if self.cannon:
            smoke = loader.loadModel("phase_4/models/props/test_clouds.bam")
            smoke.setBillboardPointEye()
            smoke.reparentTo(self.cannon.find('**/cannon'))
            smoke.setPos(0, 6, -3)
            smoke.setScale(0.5)
            smoke.wrtReparentTo(render)
            self.suit = self.cr.doId2do.get(suitId)
            self.cannon.find('**/cannon').lookAt(self.suit.find('**/joint_head'))
            self.cannon.find('**/square_drop_shadow').headsUp(self.suit.find('**/joint_head'))
            self.track = Sequence(Parallel(LerpScaleInterval(smoke, 0.5, 3), LerpColorScaleInterval(smoke, 0.5, Vec4(2, 2, 2, 0))), Func(smoke.removeNode))
            self.track.start()
            self.createAndShootGag()

    def exitShoot(self):
        if hasattr(self, 'suit'):
            del self.suit

    def shoot(self, suitId):
        self.fsm.request('shoot', [suitId])

    def scan(self, timestamp = None, afterShooting = 0):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)

        self.fsm.request('scan', [ts, afterShooting])
        
    def buildScanTrack(self, ts = None):
        if self.track:
            self.track.pause()
            self.track = None
        self.track = Parallel(
            Sequence(
                LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (60, 0, 0),
                    startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
                    startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
            ),
            Sequence(
                LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (60, 0, 0),
                    startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
                    startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
            )
        )
        if ts:
            self.track.loop(ts)
        else:
            self.track.loop()

    def enterScan(self, ts = 0, afterShooting = 0):
        if afterShooting:
            self.track = Parallel(
                LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
                    startHpr = self.cannon.find('**/cannon').getHpr(), blendType = 'easeInOut'),
                LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
                    startHpr = self.cannon.find('**/square_drop_shadow').getHpr(), blendType = 'easeInOut'),
                name = "afterShootTrack" + str(id(self))
            )
            self.track.setDoneEvent(self.track.getName())
            self.acceptOnce(self.track.getDoneEvent(), self._afterShootTrackDone)
            self.track.start(ts)
        else:
            self.buildScanTrack(ts)

    def exitScan(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    # END STATES

    def _afterShootTrackDone(self):
        self.buildScanTrack()

    def makeTurret(self):
        self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon.bam')
        self.cannon.reparentTo(self)
        self.loadGagInTurret()
        self.setupWallSphere()
        if self.isLocal():
            self.setupEventSphere()

    def removeTurret(self):
        self.removeWallSphere()
        self.removeGagInTurret()
        if self.cannon:
            self.cannon.removeNode()
            self.cannon = None

    def getCannon(self):
        return self.cannon.find('**/cannon')

    def setupWallSphere(self):
        sphere = CollisionSphere(0.0, 0.0, 0.0, 3.0)
        node = CollisionNode('DistributedPieTurret.WallSphere')
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.wallCollNode = self.cannon.attachNewNode(node)
        self.wallCollNode.setZ(2)
        self.wallCollNode.setY(1.0)

    def removeWallSphere(self):
        if self.wallCollNode:
            self.wallCollNode.removeNode()
            self.wallCollNode = None

    def createAndShootGag(self):
        if not self.readyGag:
            self.loadGagInTurret()
        if self.readyGag:
            self.readyGag.shoot(Point3(0, 200, -90))
            self.entities.append(self.readyGag)
            collideEventName = self.readyGag.getCollideEventName()
            self.readyGag = None
            if self.isLocal():
                self.acceptOnce(collideEventName, self.handleGagCollision)
        Sequence(Wait(self.reloadTime), Func(self.loadGagInTurret)).start()

    def loadGagInTurret(self):
        if self.cannon and self.gag:
            self.removeGagInTurret()
            self.eventId = random.uniform(0, 100000000)
            self.readyGag = TurretGag(self, self.uniqueName('pieTurretCollision') + str(self.eventId), self.gag)
            self.readyGag.build()

    def removeGagInTurret(self):
        if self.readyGag:
            self.readyGag.cleanup()
            self.readyGag = None

    def makeSplat(self, index, pos):
        if index >= len(self.entities):
            return
        ent = self.entities[index]
        gagClass = ent.gagClass
        splat = gagClass.buildSplat(gagClass.splatScale, gagClass.splatColor)
        base.audio3d.attachSoundToObject(gagClass.hitSfx, splat)
        splat.reparentTo(render)
        splat.setPos(pos[0], pos[1], pos[2])
        gagClass.hitSfx.play()
        Sequence(Wait(0.5), Func(splat.cleanup)).start()
        self.hitGag = None

    def d_makeSplat(self, index, pos):
        self.sendUpdate('makeSplat', [index, pos])

    def b_makeSplat(self, index, pos):
        self.d_makeSplat(index, pos)
        self.makeSplat(index, pos)

    def handleGagCollision(self, entry, ent):
        x, y, z = ent.getGag().getPos(render)
        self.b_makeSplat(self.entities.index(ent), [x, y, z])
        if self.isLocal():
            intoNP = entry.getIntoNodePath()
            avNP = intoNP.getParent()
            for key in self.cr.doId2do.keys():
                obj = self.cr.doId2do[key]
                if obj.__class__.__name__ == 'DistributedSuit':
                    if obj.getKey() == avNP.getKey():
                        if obj.getHealth() > 0:
                            obj.sendUpdate('hitByGag', [ent.getID()])
        ent.cleanup()

    def setHealth(self, hp):
        DistributedAvatar.setHealth(self, hp)
        if self.isLocal():
            base.localAvatar.getMyBattle().getTurretManager().updateTurretGui()

    def die(self):
        self.fsm.requestFinalState()
        turretPos = self.cannon.getPos(render)
        self.removeTurret()
        self.explosion = loader.loadModel("phase_3.5/models/props/explosion.bam")
        self.explosion.setScale(0.5)
        self.explosion.reparentTo(render)
        self.explosion.setBillboardPointEye()
        self.explosion.setPos(turretPos + (0, 0, 5))
        sfx = base.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_cogfall_apart.ogg")
        base.audio3d.attachSoundToObject(sfx, self)
        base.playSfx(sfx)
        messenger.send(self.deathEvent)

    def isLocal(self):
        return self.getOwner() == base.localAvatar.doId

    def getDeathEvent(self):
        return self.deathEvent
class DistributedDodgeballGame(DistributedToonFPSGame, TeamMinigame):
    notify = directNotify.newCategory('DistributedDodgeballGame')
    TreeData = [
     [
      'prop_snow_tree_small_ur', Point3(23.23, 66.52, 7.46)],
     [
      'prop_snow_tree_small_ul', Point3(-34.03, 88.02, 24.17)],
     [
      'prop_snow_tree_small_ur', Point3(-54.8, 0, 4.19)],
     [
      'prop_snow_tree_small_ul', Point3(54.8, -5, 4.19)],
     [
      'prop_snow_tree_small_ur', Point3(62.71, 62.66, 16.8)],
     [
      'prop_snow_tree_small_ul', Point3(-23.23, -66.52, 6)],
     [
      'prop_snow_tree_small_ur', Point3(34.03, -88.02, 23)],
     [
      'prop_snow_tree_small_ul', Point3(-62.71, -62.66, 16)]]
    SnowballData = [
     Point3(30, 0, 0.75),
     Point3(22.5, 0, 0.75),
     Point3(15, 0, 0.75),
     Point3(7.5, 0, 0.75),
     Point3(0, 0, 0.75),
     Point3(-7.5, 0, 0.75),
     Point3(-15, 0, 0.75),
     Point3(-22.5, 0, 0.75),
     Point3(-30, 0, 0.75)]
    GameSong = 'phase_4/audio/bgm/MG_Dodgeball.ogg'
    GameDesc = 'Welcome to the north! You have been invited to play dodgeball with the penguins!\n\nHow To Play\nWASD to Move and use the mouse to aim.\nLeft click to Throw!\nThrow a snowball at a teammate to unfreeze them!\n\nObjective\nThe first team to freeze everyone!'
    InitCamTrans = [
     Point3(25, 45, 19.5317), Vec3(154.001, -15, 0)]
    SnowBallDmg = 25
    GetSnowBalls = 'Pick up a snowball from the center!'
    Team2OtherBarrier = {BLUE: 'red_barrier_coll', RED: 'blue_barrier_coll'}

    def __init__(self, cr):
        try:
            self.DistributedDodgeballGame_initialized
            return
        except:
            self.DistributedDodgeballGame_initialized = 1

        DistributedToonFPSGame.__init__(self, cr)
        TeamMinigame.__init__(self, 'BlueSnow', ('phase_4/maps/db_blue_neutral.png',
                                                 'phase_4/maps/db_blue_hover.png',
                                                 'phase_4/maps/db_blue_hover.png'), 'RedIce', ('phase_4/maps/db_red_neutral.png',
                                                                                               'phase_4/maps/db_red_hover.png',
                                                                                               'phase_4/maps/db_red_hover.png'))
        self.fsm.addState(State('chooseTeam', self.enterChooseTeam, self.exitChooseTeam, ['waitForOthers']))
        self.fsm.addState(State('scrollBy', self.enterScrollBy, self.exitScrollBy, ['countdown']))
        self.fsm.addState(State('countdown', self.enterCountdown, self.exitCountdown, ['play']))
        self.fsm.addState(State('announceGameOver', self.enterAnnGameOver, self.exitAnnGameOver, ['displayWinners']))
        self.fsm.addState(State('displayWinners', self.enterDisplayWinners, self.exitDisplayWinners, ['gameOver']))
        self.fsm.getStateNamed('waitForOthers').addTransition('chooseTeam')
        self.fsm.getStateNamed('waitForOthers').addTransition('scrollBy')
        self.fsm.getStateNamed('play').addTransition('announceGameOver')
        self.firstPerson = DodgeballFirstPerson(self)
        self.scrollBySeq = None
        self.infoText = None
        self.redScoreLbl = None
        self.blueScoreLbl = None
        self.infoText = getAlertText()
        self.spawnPointsByTeam = {BLUE: [
                [
                 Point3(5, 15, 0), Vec3(180, 0, 0)],
                [
                 Point3(15, 15, 0), Vec3(180, 0, 0)],
                [
                 Point3(-5, 15, 0), Vec3(180, 0, 0)],
                [
                 Point3(-15, 15, 0), Vec3(180, 0, 0)]], 
           RED: [
               [
                Point3(5, -15, 0), Vec3(0, 0, 0)],
               [
                Point3(15, -15, 0), Vec3(0, 0, 0)],
               [
                Point3(-5, -15, 0), Vec3(0, 0, 0)],
               [
                Point3(-15, -15, 0), Vec3(0, 0, 0)]]}
        self.winnerMusic = base.loadMusic('phase_9/audio/bgm/encntr_hall_of_fame.mid')
        self.loserMusic = base.loadMusic('phase_9/audio/bgm/encntr_sting_announce.mid')
        self.danceSound = base.loadSfx('phase_3.5/audio/sfx/ENC_Win.ogg')
        self.sky = None
        self.arena = None
        self.fog = None
        self.snow = None
        self.snowRender = None
        self.trees = []
        self.snowballs = []
        return

    def getTeamDNAColor(self, team):
        print 'getTeamDNAColor'
        if team == TEAM1:
            print 'blue'
            return ToonDNA.colorName2DNAcolor['blue']
        if team == TEAM2:
            print 'bright red'
            return ToonDNA.colorName2DNAcolor['bright red']

    def enterDisplayWinners(self):
        base.localAvatar.stopLookAround()
        base.localAvatar.resetHeadHpr()
        base.localAvatar.getGeomNode().show()
        camera.reparentTo(render)
        camera.setPos((-2.5, 12, 3.5))
        camera.setHpr((-175.074, -5.47218, 0))
        base.transitions.fadeIn()
        base.playSfx(self.danceSound, looping=1)
        if self.winnerTeam == self.team:
            base.playMusic(self.winnerMusic, volume=0.8)
        else:
            base.playMusic(self.loserMusic, volume=0.8)
        winnerPositions = [(-2, 0, 0), (2, 0, 0), (6, 0, 0), (-6, 0, 0)]
        loserPositions = [(-3.5, -10, 0), (-1.5, -15, 0), (3.0, -8, 0), (5.5, -12, 0)]
        for team in [RED, BLUE]:
            for avId in self.playerListByTeam[team]:
                av = self.cr.doId2do.get(avId)
                if av:
                    av.stopSmooth()
                    av.setHpr(0, 0, 0)
                    if team == self.winnerTeam:
                        posList = winnerPositions
                        av.setAnimState('off')
                        av.stop()
                        if not self.getRemoteAvatar(avId).isFrozen:
                            av.loop('win')
                    else:
                        posList = loserPositions
                        av.setAnimState('off')
                        av.stop()
                        if not self.getRemoteAvatar(avId).isFrozen:
                            av.loop('pout')
                    pos = random.choice(posList)
                    posList.remove(pos)
                    av.setPos(pos)

        if self.winnerTeam == team:
            text = 'YOU WIN!'
        else:
            text = 'YOU LOSE!'
        self.gameOverLbl.setText(text)
        self.track = Sequence(Wait(2.0), Func(self.gameOverLbl.setScale, 0.01), Func(self.gameOverLbl.show), getAlertPulse(self.gameOverLbl, 0.27, 0.25))
        self.track.start()

    def exitDisplayWinners(self):
        base.transitions.noTransitions()
        self.danceSound.stop()
        if hasattr(self, 'track'):
            self.track.finish()
            self.track = None
        self.gameOverLbl.hide()
        return

    def enterAnnGameOver(self, timeRanOut=0):
        self.firstPerson.vModel.hide()
        text = 'GAME\nOVER'
        if timeRanOut:
            text = "TIME's\nUP"
        self.gameOverLbl.setText(text)
        self.gameOverLbl.show()
        base.transitions.fadeScreen()
        taskMgr.doMethodLater(3.0, self.__annGameOverTask, self.uniqueName('annGameOverTask'))

    def __annGameOverTask(self, task):
        self.gameOverLbl.hide()
        self.ival = Sequence(base.transitions.getFadeOutIval(), Func(self.fsm.request, 'displayWinners'))
        self.ival.start()
        return task.done

    def exitAnnGameOver(self):
        taskMgr.remove(self.uniqueName('annGameOverTask'))
        if hasattr(self, 'ival'):
            self.ival.finish()
            del self.ival
        self.gameOverLbl.hide()

    def teamWon(self, team):
        self.winnerTeam = team
        base.localAvatar.disableAvatarControls()
        self.firstPerson.end()
        self.deleteTimer()
        self.fsm.request('announceGameOver')

    def incrementTeamScore(self, team):
        TeamMinigame.incrementTeamScore(self, team)
        if team == BLUE:
            self.blueScoreLbl.setText('BLUE: ' + str(self.scoreByTeam[team]))
            ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self.blueScoreLbl, 'blueScorePulse'))
        else:
            if team == RED:
                self.redScoreLbl.setText('RED: ' + str(self.scoreByTeam[team]))
                ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self.redScoreLbl, 'redScorePulse'))

    def getWinterDodgeballScoreText(self, color):
        text = OnscreenText(fg=color, font=CIGlobals.getMinnieFont(), scale=0.15, shadow=(0,
                                                                                          0,
                                                                                          0,
                                                                                          1))
        return text

    def snowballHitWall(self, snowballIndex):
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitWallOrPlayer()
        base.playSfx(snowball.impactSound, node=snowball, volume=1.5)

    def snowballHitGround(self, snowballIndex):
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitGround()
        base.playSfx(snowball.impactSound, node=snowball, volume=1.5)

    def snowballHitPlayer(self, damagedPlayer, throwerTeam, snowballIndex):
        av = self.getRemoteAvatar(damagedPlayer)
        if av:
            if throwerTeam == av.team:
                if av.unFreeze():
                    if damagedPlayer == base.localAvatar.doId:
                        self.showAlert('A team member has unfroze you!')
                        self.firstPerson.camFSM.request('unfrozen')
                        self.sendUpdate('teamMateUnfrozeMe', [self.team])
            elif av.freeze():
                if damagedPlayer == base.localAvatar.doId:
                    self.showAlert("You've been frozen by an enemy!")
                    self.firstPerson.camFSM.request('frozen')
                    self.sendUpdate('enemyFrozeMe', [self.team, throwerTeam])
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitWallOrPlayer()
        base.playSfx(snowball.impactSound, node=snowball, volume=1.5)

    def playerCaughtSnowball(self, snowballIndex, catcherId):
        av = self.getRemoteAvatar(catcherId)
        if av:
            snowball = self.snowballs[snowballIndex]
            snowball.pauseThrowIval()
            snowball.pickup(av)

    def setupRemoteAvatar(self, avId):
        av = RemoteDodgeballAvatar(self, self.cr, avId)
        if avId == self.cr.localAvId:
            self.myRemoteAvatar = av
        self.remoteAvatars.append(av)

    def __getSnowTree(self, path):
        trees = loader.loadModel('phase_8/models/props/snow_trees.bam')
        tree = trees.find('**/' + path)
        tree.find('**/*shadow*').removeNode()
        return tree

    def load(self):
        self.setMinigameMusic(DistributedDodgeballGame.GameSong)
        self.setDescription(DistributedDodgeballGame.GameDesc)
        self.setWinnerPrize(200)
        self.setLoserPrize(0)
        self.createWorld()
        self.blueScoreLbl = self.getWinterDodgeballScoreText(VBase4(0, 0, 1, 1))
        self.blueScoreLbl.reparentTo(base.a2dTopLeft)
        self.blueScoreLbl['align'] = TextNode.ALeft
        self.blueScoreLbl.setText('Blue: 0')
        self.blueScoreLbl.setZ(-0.17)
        self.blueScoreLbl.setX(0.05)
        self.blueScoreLbl.hide()
        self.redScoreLbl = self.getWinterDodgeballScoreText(VBase4(1, 0, 0, 1))
        self.redScoreLbl.reparentTo(base.a2dTopLeft)
        self.redScoreLbl['align'] = TextNode.ALeft
        self.redScoreLbl.setText('Red: 0')
        self.redScoreLbl.setZ(-0.35)
        self.redScoreLbl.setX(0.05)
        self.redScoreLbl.hide()
        trans = DistributedDodgeballGame.InitCamTrans
        camera.setPos(trans[0])
        camera.setHpr(trans[1])
        DistributedToonFPSGame.load(self)

    def createWorld(self):
        self.deleteWorld()
        self.sky = loader.loadModel('phase_3.5/models/props/BR_sky.bam')
        self.sky.reparentTo(render)
        self.sky.setZ(-40)
        self.sky.setFogOff()
        self.arena = loader.loadModel('phase_4/models/minigames/dodgeball_arena.egg')
        self.arena.reparentTo(render)
        self.arena.setScale(0.75)
        self.arena.find('**/team_divider').setBin('ground', 18)
        self.arena.find('**/floor').setBin('ground', 18)
        self.arena.find('**/team_divider_coll').setCollideMask(CIGlobals.FloorBitmask)
        for data in DistributedDodgeballGame.TreeData:
            code = data[0]
            pos = data[1]
            tree = self.__getSnowTree(code)
            tree.reparentTo(self.arena)
            tree.setPos(pos)
            self.trees.append(tree)

        for i in xrange(len(DistributedDodgeballGame.SnowballData)):
            snowdata = DistributedDodgeballGame.SnowballData[i]
            snowball = Snowball(self, i)
            snowball.load()
            snowball.reparentTo(render)
            snowball.setPos(snowdata)
            self.snowballs.append(snowball)

        self.snow = ParticleLoader.loadParticleEffect('phase_8/etc/snowdisk.ptf')
        self.snow.setPos(0, 0, 5)
        self.snowRender = self.arena.attachNewNode('snowRender')
        self.snowRender.setDepthWrite(0)
        self.snowRender.setBin('fixed', 1)
        self.snow.start(camera, self.snowRender)
        self.fog = Fog('snowFog')
        self.fog.setColor(0.486, 0.784, 1)
        self.fog.setExpDensity(0.003)
        render.setFog(self.fog)

    def throw(self, snowballIndex, p):
        snowball = self.snowballs[snowballIndex]
        snowball.throw(p)

    def snowballPickup(self, snowballIndex, pickerUpperAvId):
        remoteAv = self.getRemoteAvatar(pickerUpperAvId)
        if remoteAv:
            snowball = self.snowballs[snowballIndex]
            snowball.pickup(remoteAv)

    def deleteWorld(self):
        if self.redScoreLbl:
            self.redScoreLbl.destroy()
            self.redScoreLbl = None
        if self.blueScoreLbl:
            self.blueScoreLbl.destroy()
            self.blueScoreLbl = None
        for snowball in self.snowballs:
            snowball.removeNode()

        self.snowballs = []
        for tree in self.trees:
            tree.removeNode()

        self.trees = []
        if self.snow:
            self.snow.cleanup()
            self.snow = None
        if self.snowRender:
            self.snowRender.removeNode()
            self.snowRender = None
        self.fog = None
        if self.sky:
            self.sky.removeNode()
            self.sky = None
        if self.arena:
            self.arena.removeNode()
            self.arena = None
        render.clearFog()
        return

    def enterPlay(self):
        self.createTimer()
        self.redScoreLbl.show()
        self.blueScoreLbl.show()
        self.firstPerson.camFSM.request('unfrozen')
        self.arena.find('**/' + self.Team2OtherBarrier[self.team]).stash()

    def exitPlay(self):
        self.firstPerson.crosshair.destroy()
        self.firstPerson.crosshair = None
        self.firstPerson.camFSM.request('off')
        DistributedToonFPSGame.exitPlay(self)
        return

    def enterCountdown(self):
        self.firstPerson.start()
        self.firstPerson.disableMouse()
        self.infoText.setText(DistributedDodgeballGame.GetSnowBalls)
        self.countdownText = getGameText()
        self.countdownIval = Parallel(Sequence(Func(self.countdownText.setText, '5'), getCountdownIval(self.countdownText), Func(self.countdownText.setText, '4'), getCountdownIval(self.countdownText), Func(self.countdownText.setText, '3'), getCountdownIval(self.countdownText), Func(self.countdownText.setText, '2'), getCountdownIval(self.countdownText), Func(self.countdownText.setText, '1'), getCountdownIval(self.countdownText)), getAlertPulse(self.infoText), name='COUNTDOWNIVAL')
        self.countdownIval.setDoneEvent(self.countdownIval.getName())
        self.acceptOnce(self.countdownIval.getDoneEvent(), self.__handleCountdownDone)
        self.countdownIval.start()

    def __handleCountdownDone(self):
        self.fsm.request('play')

    def exitCountdown(self):
        if hasattr(self, 'countdownText'):
            self.countdownText.destroy()
            del self.countdownText
        if hasattr(self, 'countdownIval'):
            self.ignore(self.countdownIval.getDoneEvent())
            self.countdownIval.finish()
            del self.countdownIval

    def enterScrollBy(self):
        BLUE_START_POS = Point3(-20, 0, 4)
        BLUE_END_POS = Point3(20, 0, 4)
        BLUE_HPR = Vec3(0, 0, 0)
        RED_START_POS = Point3(20, 0, 4)
        RED_END_POS = Point3(-20, 0, 4)
        RED_HPR = Vec3(180, 0, 0)
        self.playMinigameMusic()
        self.scrollBySeq = Sequence(Func(camera.setHpr, BLUE_HPR), LerpPosInterval(camera, duration=5.0, pos=BLUE_END_POS, startPos=BLUE_START_POS, blendType='easeOut'), Func(base.transitions.fadeOut, 0.4), Wait(0.5), Func(base.transitions.fadeIn, 0.4), Func(camera.setHpr, RED_HPR), LerpPosInterval(camera, duration=5.0, pos=RED_END_POS, startPos=RED_START_POS, blendType='easeOut'), name='SCROLLBYSEQ')
        self.scrollBySeq.setDoneEvent(self.scrollBySeq.getName())
        self.acceptOnce(self.scrollBySeq.getDoneEvent(), self.__handleScrollByDone)
        self.scrollBySeq.start()

    def __handleScrollByDone(self):
        self.fsm.request('countdown')

    def exitScrollBy(self):
        if self.scrollBySeq:
            self.ignore(self.scrollBySeq.getDoneEvent())
            self.scrollBySeq.finish()
            self.scrollBySeq = None
        return

    def allPlayersReady(self):
        self.fsm.request('scrollBy')

    def chooseUrTeam(self):
        self.fsm.request('chooseTeam')

    def enterChooseTeam(self):
        self.makeSelectionGUI()

    def acceptedIntoTeam(self, spawnPoint):
        TeamMinigame.acceptedIntoTeam(self)
        self.sendUpdate('readyToStart')
        self.fsm.request('waitForOthers')
        pos, hpr = self.spawnPointsByTeam[self.team][spawnPoint]
        base.localAvatar.setPos(pos)
        base.localAvatar.setHpr(hpr)

    def exitChooseTeam(self):
        self.destroySelectionGUI()

    def announceGenerate(self):
        DistributedToonFPSGame.announceGenerate(self)
        base.camLens.setMinFov(CIGlobals.GunGameFOV / (4.0 / 3.0))
        self.load()

    def disable(self):
        base.camLens.setMinFov(CIGlobals.DefaultCameraFov / (4.0 / 3.0))
        self.fsm.requestFinalState()
        self.deleteWorld()
        self.trees = None
        self.snowballs = None
        self.spawnPointsByTeam = None
        if self.firstPerson:
            self.firstPerson.reallyEnd()
            self.firstPerson.cleanup()
            self.firstPerson = None
        self.scrollBySeq = None
        self.winnerMusic = None
        self.loserMusic = None
        self.danceSound = None
        self.infoText = None
        base.localAvatar.setWalkSpeedNormal()
        DistributedToonFPSGame.disable(self)
        return
예제 #5
0
class Suit(Avatar):
    notify = directNotify.newCategory('Suit')

    def __init__(self):
        Avatar.__init__(self)
        self.dept = None
        self.suit = None
        self.head = None
        self.headModel = None
        self.variant = None
        self.handColor = None
        self.voice = None
        self.chat = None
        self.chatDial = None
        self.shadow = None
        self.deathSound = None
        self.propeller = None
        self.smallExp = None
        self.largeExp = None
        self.explosion = None
        self.hasSpawned = False
        self.suitTrack = None
        self.timestampAnimTrack = None
        self.propellerSounds = {}
        self.healthBar = None
        self.healthBarGlow = None
        self.condition = 0
        self.avatarType = CIGlobals.Suit
        self.suitPlan = None
        self.footstepSound = None
        self.showNametagInMargins = False
        self.surfaceProp = "metal"

        self.activities = {
            ACT_WAKE_ANGRY: WakeAngry(self),
            ACT_SMALL_FLINCH: Flinch(self),
            ACT_DIE: Die(self),
            ACT_VICTORY_DANCE: VictoryDance(self),
            ACT_COG_FLY_DOWN: FlyDown(self),
            ACT_SIT: Sit(self),
            ACT_STUN: Stun(self)
        }

        self.standWalkRunReverse = [('neutral', 'walk', 0.0, 5.0, 1.0, 1.0)]

        self.gruntSound = base.audio3d.loadSfx(
            "phase_14/audio/sfx/cog_grunt.ogg")
        base.audio3d.attachSoundToObject(self.gruntSound, self)

        self.animFSM = ClassicFSM('Suit', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('walk', self.enterWalk, self.exitWalk),
            State('die', self.enterDie, self.exitDie),
            State('win', self.enterWin, self.exitWin),
            State('flail', self.enterFlail, self.exitFlail),
            State('flyDown', self.enterFlyDown, self.exitFlyDown),
            State('flyAway', self.enterFlyAway, self.exitFlyAway),
            State('flyNeutral', self.enterFlyNeutral, self.exitFlyNeutral),
            State('trayWalk', self.enterTrayWalk, self.exitTrayWalk),
            State('trayNeutral', self.enterTrayNeutral, self.exitTrayNeutral),
            State('stunned', self.enterStunned, self.exitStunned),
            State('pie', self.enterPie, self.exitPie),
            State('drop', self.enterDrop, self.exitDrop),
            State('drop-react', self.enterDropReact, self.exitDropReact),
            State('soak', self.enterSoak, self.exitSoak),
            State('squirt-small', self.enterSquirtSmall, self.exitSquirtSmall)
        ], 'off', 'off')
        self.animFSM.enterInitialState()

    def getRightHandNode(self):
        return self.find("**/joint_Rhold")

    def getLeftHandNode(self):
        return self.find("**/joint_Lhold")

    def getHeadNode(self):
        return self.headModel

    def getUpperBodySubpart(self):
        return [None]

    def getLowerBodySubpart(self):
        return [None]

    def getMoveAction(self, forward, rotate, strafe):
        return 0

    def enterPie(self, ts=0):
        self.play('pie')

    def exitPie(self):
        self.stop()

    def enterDrop(self, ts=0):
        self.play("drop")

    def exitDrop(self):
        self.stop()

    def enterDropReact(self, ts=0):
        self.play('drop-react')

    def exitDropReact(self):
        self.stop()

    def enterSoak(self, ts=0):
        self.play('soak')

    def exitSoak(self):
        self.stop()

    def enterSquirtSmall(self, ts=0):
        self.play('squirt-small')

    def exitSquirtSmall(self):
        self.stop()

    def enterStunned(self, animB4Stun, ts=0):
        self.show()

        if isinstance(animB4Stun, int):
            animB4Stun = SuitGlobals.getAnimById(animB4Stun).getName()

        self.stunnedSound = base.loadSfxOnNode(
            "phase_4/audio/sfx/SZ_TC_bird1.ogg", self)
        self.stunnedSound.setLoop(True)
        self.stunnedSound.play()

        self.stunnedIval = Parallel(
            Sequence(ActorInterval(self, animB4Stun),
                     Func(self.loop, 'stunned')),
            SuitGlobals.createStunInterval(self, 0, 100))
        self.stunnedIval.start()

    def clearStunnedIval(self):
        if hasattr(self, 'stunnedSound'):
            self.stunnedSound.stop()
            del self.stunnedSound
        if hasattr(self, 'stunnedIval'):
            self.stunnedIval.finish()
            del self.stunnedIval

    def exitStunned(self):
        self.clearStunnedIval()
        self.stop()

    def getLeftHand(self):
        return self.find("**/joint_Lhold")

    def getRightHand(self):
        return self.find("**/joint_Rhold")

    def getNametagJoints(self):
        return []

    # BEGIN STATES

    def enterOff(self, ts=0):
        pass

    def exitOff(self):
        pass

    def exitGeneral(self):
        pass

    def enterTrayWalk(self, ts=0):
        self.show()
        self.loop('tray-walk')

    def exitTrayWalk(self):
        self.exitGeneral()

    def enterTrayNeutral(self, ts=0):
        self.loop('tray-neutral')

    def exitTrayNeutral(self):
        self.stop()

    def enterNeutral(self, ts=0):
        self.show()
        self.loop("neutral")

    def exitNeutral(self):
        self.exitTimestampAnimTrack()
        self.exitGeneral()

    def enterWalk(self, ts=0):
        self.show()
        self.loop("walk")
        self.enableRay()
        self.disableShadowRay()
        self.startFootsteps()

    def exitWalk(self):
        self.stopFootsteps()
        self.exitTimestampAnimTrack()
        self.exitGeneral()
        self.enableShadowRay()

    def enterFlail(self, ts=0):
        self.pingpong('flail', fromFrame=30, toFrame=35)

    def exitFlail(self):
        self.stop()

    def exitTimestampAnimTrack(self):
        if self.timestampAnimTrack:
            self.timestampAnimTrack.pause()
            self.timestampAnimTrack = None

    def enterFlyNeutral(self, ts=0):
        self.disableRay()
        self.enableShadowRay()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['neutral']
        base.playSfx(sfx, node=self, looping=1)
        self.propeller.loop('chan', fromFrame=0, toFrame=3)
        self.setPlayRate(0.8, 'land')
        self.pingpong('land', fromFrame=0, toFrame=10)

    def exitFlyNeutral(self):
        self.cleanupPropeller()

    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)

    def exitFlyDown(self):
        self.cleanupPropeller()
        self.enableRay()
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.exitGeneral()

    def enterFlyAway(self, ts=0, doFadeOut=0):
        self.show()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['out']
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyAway')
        else:
            name = 'enterFlyAway'
        dur = self.getDuration('land')
        actInt = ActorInterval(self,
                               'land',
                               loop=0,
                               startTime=dur,
                               endTime=0.0)
        lastSpinFrame = 8
        propDur = self.propeller.getDuration('chan')
        fr = self.propeller.getFrameRate('chan')
        spinTime = lastSpinFrame / fr
        openTime = (lastSpinFrame + 1) / fr
        propTrack = Parallel(
            SoundInterval(sfx, node=self),
            Sequence(
                Func(self.propeller.show),
                ActorInterval(self.propeller,
                              'chan',
                              endTime=openTime,
                              startTime=propDur),
                ActorInterval(self.propeller,
                              'chan',
                              constrainedLoop=1,
                              duration=propDur - openTime,
                              startTime=spinTime,
                              endTime=0.0)))
        self.suitTrack = Parallel(actInt,
                                  propTrack,
                                  name=self.taskName('trackName'))
        if doFadeOut:
            fadeOut = Sequence(
                Wait(4.0), Func(self.setTransparency, 1),
                self.colorScaleInterval(1,
                                        colorScale=VBase4(1, 1, 1, 0),
                                        startColorScale=VBase4(1, 1, 1, 1)),
                Func(self.clearColorScale), Func(self.clearTransparency),
                Func(self.reparentTo, hidden))
            self.suitTrack.append(fadeOut)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyAway)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)
        self.disableRay()
        self.enableShadowRay()

    def exitFlyAway(self):
        self.cleanupPropeller()
        if self.suitTrack:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.exitGeneral()

    def enterDie(self, ts=0):
        self.show()
        self.clearStunnedIval()
        self.generateCog(isLose=1)
        self.nametag.clearChatText()
        self.deleteNameTag()
        self.deathSound = base.audio3d.loadSfx(
            "phase_3.5/audio/sfx/Cog_Death_Full.ogg")
        base.audio3d.attachSoundToObject(self.deathSound, self)
        trackName = self.uniqueName('enterDie')

        smallGears = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosionSmall.ptf')
        #smallGears.getParticlesNamed('particles-1').setPoolSize(30)

        singleGear = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosion.ptf')
        singleGear.getParticlesNamed('particles-1').setPoolSize(1)

        smallGearExplosion = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosion.ptf')
        smallGearExplosion.getParticlesNamed('particles-1').setPoolSize(10)

        bigGearExplosion = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosionBig.ptf')
        bigGearExplosion.getParticlesNamed('particles-1').setPoolSize(30)

        smallGears.setDepthWrite(False)
        singleGear.setDepthWrite(False)
        smallGearExplosion.setDepthWrite(False)
        bigGearExplosion.setDepthWrite(False)

        gearPoint = self.getPos(render) + (0, 0, self.getHeight() - 0.2)

        self.smallGears = smallGears
        self.smallGears.setPos(gearPoint)
        self.singleGear = singleGear
        self.singleGear.setPos(gearPoint)
        self.smallGearExp = smallGearExplosion
        self.smallGearExp.setPos(gearPoint)
        self.bigGearExp = bigGearExplosion
        self.bigGearExp.setPos(gearPoint)

        gearTrack = Sequence(Wait(0.7), Func(self.doSingleGear), Wait(1.5),
                             Func(self.doSmallGears), Wait(3.0),
                             Func(self.doBigExp))
        self.suitTrack = Parallel(
            Sequence(Wait(0.8), SoundInterval(self.deathSound, duration=4.28)),
            Sequence(Wait(0.7), Func(self.doSingleGear), Wait(4.5),
                     Func(self.suitExplode), Wait(1.0),
                     Func(self.disableBodyCollisions)),
            gearTrack,
            Sequence(ActorInterval(self, 'lose', duration=6),
                     Func(self.getGeomNode().hide)),
            name=trackName)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getName(), self.exitDie)
        if self.isDistributed():
            self.suitTrack.delayDelete = DelayDelete.DelayDelete(
                self, trackName)
        self.suitTrack.start(ts)

    def doSingleGear(self):
        self.singleGear.start(CIGlobals.getParticleRender())

    def doSmallGears(self):
        self.smallGears.start(CIGlobals.getParticleRender())

    def doSmallExp(self):
        self.smallGearExp.start(CIGlobals.getParticleRender())

    def doBigExp(self):
        self.bigGearExp.start(CIGlobals.getParticleRender())

    def suitExplode(self):
        pos = self.getPart('body').find('**/joint_head').getPos(render) + (
            0, 0, 2)

        # Force the loser suit to use UnlitGeneric shader, workaround for the has_mat() assertion
        BSPUtility.applyUnlitOverride(self)

        CIGlobals.makeExplosion(pos, 0.5, soundVol=0.32, smoke=False)

    def exitDie(self):
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getName())
            self.suitTrack.finish()
            if self.isDistributed():
                DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'singleGear'):
            self.singleGear.softStop()
            del self.singleGear
        if hasattr(self, 'smallGears'):
            self.smallGears.softStop()
            del self.smallGears
        if hasattr(self, 'smallGearExp'):
            self.smallGearExp.softStop()
            del self.smallGearExp
        if hasattr(self, 'bigGearExp'):
            self.bigGearExp.softStop()
            del self.bigGearExp
        if self.deathSound:
            self.deathSound.stop()
        self.deathSound = None

    def enterWin(self, ts=0):
        self.play('win')

    def exitWin(self):
        self.exitGeneral()

    # END STATES

    def generateSuit(self, suitPlan, variant, voice=None, hideFirst=False):
        startTime = globalClock.getRealTime()

        self.suitPlan = suitPlan
        self.suit = suitPlan.getSuitType()
        self.head = suitPlan.getHead()
        self.dept = suitPlan.getDept()
        self.handColor = suitPlan.getHandColor()
        self.variant = variant
        self.setVoice(voice)
        self.generateCog()

        mat = CIGlobals.getCharacterMaterial(shininess=50.0,
                                             specular=(0.4, 0.4, 0.4, 1))
        self.setMaterial(mat)

        #ts = TextureStage('shiny')
        #ts.setMode(TextureStage.MAdd)
        #ts.setRgbScale(2)
        #tex = loader.loadCubeMap('phase_14/maps/cubemap/defaultcubemap_#.png')
        #tex = loader.loadTexture('phase_14/maps/envmap001a_cog.png')
        #self.setTexGen(ts, TexGenAttrib.MEyeSphereMap)
        #self.setTexture(ts, tex)

        self.initializeBodyCollisions()

        if hideFirst:
            self.hide()
        else:
            self.show()

        endTime = globalClock.getRealTime()
        print("generateSuit took {0} ms".format((endTime - startTime) * 1000))

    def __blinkRed(self, task):
        self.healthBar.setColor(SuitGlobals.healthColors[3], 1)
        #self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[3], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.17)
        return task.done

    def __blinkGray(self, task):
        if not self.healthBar:
            return
        self.healthBar.setColor(SuitGlobals.healthColors[4], 1)
        #self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[4], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.0)
        return task.done

    def generateHealthBar(self):
        self.removeHealthBar()
        button = loader.loadModel('phase_3.5/models/gui/matching_game_gui.bam'
                                  ).find('**/minnieCircle')
        button.setScale(3.0)
        button.setH(180)
        button.setColor(SuitGlobals.healthColors[0])
        chestNull = self.find('**/def_joint_attachMeter')
        if chestNull.isEmpty():
            chestNull = self.find('**/joint_attachMeter')
        button.reparentTo(chestNull)
        self.healthBar = button
        #self.healthBarGlow = loader.loadModel('phase_3.5/models/props/glow.bam')
        #self.healthBarGlow.reparentTo(self.healthBar)
        #self.healthBarGlow.setScale(0.28)
        #self.healthBarGlow.setPos(-0.005, 0.01, 0.015)
        #self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[0])
        button.flattenLight()
        button.setLightOff()
        self.condition = 0
        if hasattr(self, 'getHealth'):
            self.updateHealthBar(self.getHealth())

    def updateHealthBar(self, hp):
        if not self.healthBar:
            return
        if hp > self.health:
            self.health = hp
        health = 0.0
        try:
            health = float(hp) / float(self.maxHealth)
        except:
            pass
        if health > 0.95:
            condition = 0
        elif health > 0.7:
            condition = 1
        elif health > 0.3:
            condition = 2
        elif health > 0.05:
            condition = 3
        elif health > 0.0:
            condition = 4
        else:
            condition = 5
        if self.condition != condition:
            taskMgr.remove(self.taskName('blink-task'))
            if condition == 4:
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75),
                                      Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            elif condition == 5:
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25),
                                      Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            else:
                self.healthBar.setColor(SuitGlobals.healthColors[condition], 1)
                #self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[condition], 1)
            self.condition = condition

    def removeHealthBar(self):
        if self.healthBar:
            self.healthBar.removeNode()
            self.healthBar = None
        if self.condition == 4 or self.condition == 5:
            taskMgr.remove(self.taskName('blink-task'))
        self.healthCondition = 0
        return

    def initializeBodyCollisions(self):
        self.notify.info('Initializing Body Collisions!')
        self.setupPhysics(2, self.getHeight())
        self.enableRay()

    def hideSuit(self):
        self.hide()

    def showSuit(self):
        self.show()
        fadeIn = Sequence(
            Func(self.setTransparency, 1),
            self.colorScaleInterval(0.6,
                                    colorScale=Vec4(1, 1, 1, 1),
                                    startColorScale=Vec4(1, 1, 1, 0)),
            Func(self.clearColorScale), Func(self.clearTransparency),
            Func(self.reparentTo, render))
        fadeIn.start()

    def doStunEffect(self):
        self.clearStunnedIval()
        self.stunnedIval = SuitGlobals.createStunInterval(self, 0, 2)
        self.stunnedIval.start()

    def doGagEffect(self, flags):
        GagEffects.doGagEffect(self, flags)

    def generateCog(self, isLose=0, nameTag=True):
        #startTime = globalClock.getRealTime()

        generateCollector.start()

        cleanupCollector.start()
        self.cleanup()
        cleanupCollector.stop()

        if not isLose:

            if self.suitPlan in SuitBank.suitSetups:
                setup = SuitBank.suitSetups[self.suitPlan]
            else:
                setup = SuitBank.SuitSetup()
                SuitBank.suitSetups[self.suitPlan] = setup

            if not self.variant in setup.actor:
                setupActor = Actor()
                if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                    setupActor.loadModel(
                        'phase_5/models/char/cog%s_robot-zero.bam' %
                        (str(self.suit)), 'body')
                else:
                    setupActor.loadModel(
                        'phase_3.5/models/char/suit%s-mod.bam' %
                        (str(self.suit)), 'body')
                animations = SuitGlobals.animations
                anims = {}
                for anim in animations:
                    if not self.suit in anim.getSuitTypes():
                        continue
                    path = 'phase_%s/models/char/suit%s-%s.bam' % (
                        anim.getPhase(), self.suit, anim.getFile())
                    anims[anim.getName()] = path
                setupActor.loadAnims(anims, 'body')
                setup.actor[self.variant] = setupActor

            actorCollector.start()
            self.copyActor(setup.actor[self.variant])
            actorCollector.stop()

            healthBarCollector.start()
            self.generateHealthBar()
            healthBarCollector.stop()

            footstepCollector.start()
            if self.suitPlan.suitType == SuitType.A:
                self.footstepSound = base.audio3d.loadSfx(
                    "phase_5/audio/sfx/ENC_cogafssm.ogg")
            elif self.suitPlan.suitType == SuitType.B:
                self.footstepSound = base.audio3d.loadSfx(
                    "phase_5/audio/sfx/ENC_cogbfssm.ogg")
            elif self.suitPlan.suitType == SuitType.C:
                self.footstepSound = base.audio3d.loadSfx(
                    "phase_5/audio/sfx/ENC_cogcfssm.ogg")
            if self.footstepSound:
                base.audio3d.attachSoundToObject(self.footstepSound, self)
                self.footstepSound.setVolume(0.0)
                self.footstepSound.setLoop(True)
                self.footstepSound.play()
            footstepCollector.stop()

        else:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.loadModel(
                    'phase_5/models/char/cog%s_robot-lose-mod.bam' %
                    (str(self.suit)), 'body')
            else:
                self.loadModel(
                    'phase_4/models/char/suit%s-lose-mod.bam' %
                    (str(self.suit)), 'body')
            self.loadAnims(
                {
                    'lose':
                    'phase_4/models/char/suit%s-lose.bam' % (str(self.suit))
                }, 'body')

        genHeadCollector.start()
        if self.variant != Variant.SKELETON:
            self.headModel = self.head.generate()
            self.headModel.reparentTo(self.find("**/joint_head"))
        if self.suitPlan.getName() == SuitGlobals.VicePresident:
            self.headModel.setScale(0.35)
            self.headModel.setHpr(270, 0, 270)
            self.headModel.setZ(-0.10)
            self.headModel.loop('neutral')
        if self.variant == Variant.SKELETON:
            self.headModel = self.find("**/joint_head")
        #antenna = loader.loadModel("models/police_antenna.bam")
        ##antenna.reparentTo(self.find("**/joint_head"))
        #antenna.setPos(0.5, -0.5, 0)
        #antenna.setScale(1.25)
        #antenna.clearModelNodes()
        #antenna.flattenStrong()
        genHeadCollector.stop()

        self.setClothes()

        classScale = 1.0  #self.suitPlan.getCogClassAttrs().scaleMod
        self.setAvatarScale(
            (self.suitPlan.getScale() / SuitGlobals.scaleFactors[self.suit]) *
            classScale)
        self.setHeight(self.suitPlan.getHeight())

        nametagCollector.start()
        if nameTag:
            self.setupNameTag()
        nametagCollector.stop()

        Avatar.initShadow(self)

        if self.variant != Variant.SKELETON:
            # We've already done all manipulating to the cog, we can just flatten it.
            self.getPart('body').flattenStrong()
            self.postFlatten()
            self.headModel.flattenStrong()
            if isinstance(self.headModel, Actor):
                self.headModel.postFlatten()

        #endTime = globalClock.getRealTime()
        #print("GenerateCog took {0} seconds".format(endTime - startTime))

        generateCollector.stop()

    def cleanup(self):
        if self.footstepSound:
            self.footstepSound.stop()
        self.footstepSound = None
        self.cleanupPropeller()
        self.clearChatbox()
        if self.shadow:
            self.deleteShadow()
        if self.headModel:
            self.headModel.removeNode()
        self.headModel = None
        if self.getPart('body'):
            self.removePart('body')
        self.timestampAnimTrack = None

    def generatePropeller(self):
        self.cleanupPropeller()

        self.propeller = Actor(
            'phase_4/models/props/propeller-mod.bam',
            {'chan': 'phase_4/models/props/propeller-chan.bam'})
        self.propeller.reparentTo(self.find("**/joint_head"))
        self.propellerSounds['in'] = base.audio3d.loadSfx(
            SuitGlobals.propellerInSfx)
        self.propellerSounds['out'] = base.audio3d.loadSfx(
            SuitGlobals.propellerOutSfx)
        self.propellerSounds['neutral'] = base.audio3d.loadSfx(
            SuitGlobals.propellerNeutSfx)
        for sound in self.propellerSounds.values():
            base.audio3d.attachSoundToObject(sound, self.propeller)

    def cleanupPropeller(self):
        for sound in self.propellerSounds.values():
            base.audio3d.detachSound(sound)
            sound.stop()
        self.propellerSounds = {}
        if self.propeller and not self.propeller.isEmpty():
            self.propeller.cleanup()
        self.propeller = None

    def setVoice(self, voice):
        if not voice:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.voice = Voice.SKELETON
            else:
                self.voice = Voice.NORMAL
        else:
            self.voice = voice

        if self.variant == Variant.SKELETON:
            head = self
        else:
            head = self.headModel

        self.addSound("statement",
                      self.voice.getSoundFile('statement'),
                      node=head)
        self.addSound("grunt", self.voice.getSoundFile('grunt'), node=head)
        self.addSound("question",
                      self.voice.getSoundFile('question'),
                      node=head)
        if self.voice == Voice.NORMAL:
            self.addSound("question2",
                          self.voice.getSoundFile('question_2'),
                          node=head)

        self.chatSoundTable[CHAT_SHORT] = "statement"
        self.chatSoundTable[CHAT_MEDIUM] = "statement"
        self.chatSoundTable[CHAT_LONG] = "statement"
        self.chatSoundTable[CHAT_EXCLAIM] = "grunt"
        self.chatSoundTable[CHAT_HOWL] = "statement"
        if self.voice == Voice.NORMAL:
            self.chatSoundTable[CHAT_QUESTION] = ["question", "question2"]
        else:
            self.chatSoundTable[CHAT_QUESTION] = "question"

    def setClothes(self):
        setClothesCollector.start()

        if self.variant == Variant.SKELETON:
            parts = self.findAllMatches('**/pPlane*')
            for partNum in range(0, parts.getNumPaths()):
                bb = parts.getPath(partNum)
                bb.setTwoSided(1)
            tie = 'phase_5/maps/cog_robot_tie_%s.mat' % self.dept.getTie()
            #tie.setMinfilter(Texture.FTLinearMipmapLinear)
            #tie.setMagfilter(Texture.FTLinear)
            self.find('**/tie').setBSPMaterial(tie, 1)
        else:
            texture = 'phase_3.5/maps/tt_t_ene_' + self.dept.getName().lower(
            ) + '.mat'
            #texture = 'materials/models/suit/tt_t_ene_police.mat'
            #if self.variant == Variant.WAITER:
            #    prefix = 'phase_3.5/maps/waiter_m_%s.mat'
            #elif self.variant == Variant.CORRODED:
            #    prefix = 'phase_3.5/maps/' + self.dept.getClothingPrefix() + '_rust_%s.mat'

            #legTex = prefix % 'leg'
            #armTex = prefix % 'sleeve'
            #blazTex = prefix % 'blazer'

            #texs = [legTex, armTex, blazTex]

            #for texture in texs:
            #    texture.setMinfilter(Texture.FTLinearMipmapLinear)
            #    texture.setMagfilter(Texture.FTLinear)

            #self.find('**/legs').setBSPMaterial(legTex, 1)
            #self.find('**/arms').setBSPMaterial(armTex, 1)
            #self.find('**/torso').setBSPMaterial(blazTex, 1)

            #self.find('**/hands').setBSPMaterial("phase_3.5/maps/tt_t_ene_sellbotRental_hand.mat", 1)

            body = self.getPart('body')
            for child in body.getChildren():
                if isinstance(child.node(), GeomNode):
                    child.setBSPMaterial(texture, 1)

            if not self.variant == Variant.CORRODED:
                self.find('**/hands').setColor(self.handColor)
            else:
                self.find('**/hands').setColor(Variant.CORRODED_HAND_COLOR)

        setClothesCollector.stop()

    def startFootsteps(self):
        classAttrs = self.suitPlan.getCogClassAttrs()

        if not self.footstepSound or not classAttrs.footsteps:
            return

        self.footstepSound.setPlayRate(classAttrs.walkMod)
        self.footstepSound.setLoop(True)
        self.footstepSound.play()

    def stopFootsteps(self):
        if not self.footstepSound:
            return

        self.footstepSound.stop()

    def setSpeed(self, forwardSpeed, rotateSpeed, strafeSpeed=0.0):
        Avatar.setSpeed(self, forwardSpeed, rotateSpeed, strafeSpeed)

        currSpeed = self.currentSpeed
        if self.doingActivity:
            currSpeed = 0

        if self.footstepSound and self.standWalkRunReverse is not None:
            action = self.getMoveAction(forwardSpeed, rotateSpeed, strafeSpeed)
            minSpeed = self.standWalkRunReverse[action][2]
            maxSpeed = self.standWalkRunReverse[action][3]
            self.footstepSound.setVolume(
                CIGlobals.clamp(
                    CIGlobals.remapVal(currSpeed, minSpeed, maxSpeed, 0, 1), 0,
                    1))
            self.footstepSound.setPlayRate(max(1, currSpeed / maxSpeed))

    def setName(self, nameString, charName):
        Avatar.setName(self, nameString, charName=charName, createNow=1)

    def setupNameTag(self, tempName=None):
        Avatar.setupNameTag(self, tempName=tempName)
        if self.nametag:
            if self.level > 0:
                self.nametag.setText(self.nametag.getText() +
                                     '\n%s\nLevel %s %s' %
                                     (self.dept.getName(), self.level,
                                      self.suitPlan.getCogClassName()))
            else:
                self.nametag.setText(self.nametag.getText() + '\n%s' %
                                     (self.dept.getName()))

    def clearChatbox(self):
        self.clearChat()
        self.chat = None
        if self.chatDial:
            base.audio3d.detachSound(self.chatDial)
            self.chatDial.stop()
            self.chatDial = None

    def getDept(self):
        return self.dept

    def getVariant(self):
        return self.variant

    def disable(self):
        if self.suitTrack:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.animFSM.requestFinalState()
        self.cleanup()
        Avatar.disable(self)

    def delete(self):
        Avatar.delete(self)
        self.cleanup()
class Suit(Avatar):
    notify = directNotify.newCategory('Suit')
    audio3d = Audio3DManager(base.sfxManagerList[0], camera)
    audio3d.setDistanceFactor(25)
    audio3d.setDropOffFactor(audio3d.getDistanceFactor() / 1000)

    def __init__(self):
        Avatar.__init__(self)
        self.dept = None
        self.suit = None
        self.head = None
        self.headModel = None
        self.variant = None
        self.handColor = None
        self.voice = None
        self.chat = None
        self.chatDial = None
        self.shadow = None
        self.propeller = None
        self.smallExp = None
        self.largeExp = None
        self.explosion = None
        self.hasSpawned = False
        self.suitTrack = None
        self.timestampAnimTrack = None
        self.propellerSounds = {}
        self.healthBar = None
        self.healthBarGlow = None
        self.condition = 0
        self.avatarType = CIGlobals.Suit
        self.suitPlan = None
        self.animFSM = ClassicFSM('Suit', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('walk', self.enterWalk, self.exitWalk),
            State('die', self.enterDie, self.exitDie),
            State('win', self.enterWin, self.exitWin),
            State('attack', self.enterAttack, self.exitAttack),
            State('flail', self.enterFlail, self.exitFlail),
            State('flyDown', self.enterFlyDown, self.exitFlyDown),
            State('flyAway', self.enterFlyAway, self.exitFlyAway),
            State('flyNeutral', self.enterFlyNeutral, self.exitFlyNeutral),
            State('trayWalk', self.enterTrayWalk, self.exitTrayWalk),
            State('trayNeutral', self.enterTrayNeutral, self.exitTrayNeutral)
        ], 'off', 'off')
        self.animFSM.enterInitialState()
        self.initializeBodyCollisions()

    def getNametagJoints(self):
        return []

    # BEGIN STATES

    def enterOff(self, ts=0):
        self.anim = None
        return

    def exitOff(self):
        pass

    def exitGeneral(self):
        self.stop()

    def enterTrayWalk(self, ts=0):
        self.show()
        self.loop('tray-walk')

    def exitTrayWalk(self):
        self.exitGeneral()

    def enterTrayNeutral(self, ts=0):
        self.loop('tray-neutral')

    def exitTrayNeutral(self):
        self.stop()

    def enterNeutral(self, ts=0):
        self.show()
        self.loop("neutral")

    def exitNeutral(self):
        self.exitTimestampAnimTrack()
        self.exitGeneral()

    def enterWalk(self, ts=0):
        self.show()
        self.loop("walk")
        self.disableShadowRay()

    def exitWalk(self):
        self.exitTimestampAnimTrack()
        self.exitGeneral()
        self.enableShadowRay()

    def enterFlail(self, ts=0):
        self.pingpong('flail', fromFrame=30, toFrame=35)

    def exitFlail(self):
        self.stop()

    def exitTimestampAnimTrack(self):
        if self.timestampAnimTrack:
            self.timestampAnimTrack.pause()
            self.timestampAnimTrack = None

    def enterAttack(self, attack, target, ts=0):
        self.show()

        if hasattr(self, 'uniqueName'):
            doneEvent = self.uniqueName('suitAttackDone')
        else:
            doneEvent = 'suitAttackDone'
        self.suitAttackState = SuitAttacks(doneEvent, self, target)
        self.suitAttackState.load(attack)
        self.suitAttackState.enter(ts)
        self.headsUp(target)
        self.acceptOnce(doneEvent, self.handleSuitAttackDone)

    def handleSuitAttackDone(self):
        self.exitAttack()

    def exitAttack(self):
        if hasattr(self, 'uniqueName'):
            self.ignore(self.uniqueName('suitAttackDone'))
        else:
            self.ignore('suitAttackDone')
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.exit()
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.unload()
        if hasattr(self, 'suitAttackState'):
            del self.suitAttackState

    def interruptAttack(self):
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.currentAttack.interruptAttack()
            self.clearChatbox()

    def handleWeaponTouch(self):
        if hasattr(self, 'suitAttackState'):
            currentAttack = self.suitAttackState.currentAttack
            if hasattr(currentAttack, 'handleWeaponTouch'):
                currentAttack.handleWeaponTouch()

    def enterFlyNeutral(self, ts=0):
        self.disableRay()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['neutral']
        sfx.setLoop(True)
        base.playSfx(sfx, node=self)
        self.propeller.loop('chan', fromFrame=0, toFrame=3)
        self.setPlayRate(0.8, 'land')
        self.pingpong('land', fromFrame=0, toFrame=10)

    def exitFlyNeutral(self):
        self.cleanupPropeller()

    def enterFlyDown(self, ts=0):
        self.disableRay()
        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)

    def exitFlyDown(self):
        self.initializeRay(self.avatarType, 2)
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.exitGeneral()
        self.cleanupPropeller()

    def enterFlyAway(self, ts=0, doFadeOut=0):
        self.show()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['out']
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyAway')
        else:
            name = 'enterFlyAway'
        dur = self.getDuration('land')
        actInt = ActorInterval(self,
                               'land',
                               loop=0,
                               startTime=dur,
                               endTime=0.0)
        lastSpinFrame = 8
        propDur = self.propeller.getDuration('chan')
        fr = self.propeller.getFrameRate('chan')
        spinTime = lastSpinFrame / fr
        openTime = (lastSpinFrame + 1) / fr
        propTrack = Parallel(
            SoundInterval(sfx, node=self),
            Sequence(
                Func(self.propeller.show),
                ActorInterval(self.propeller,
                              'chan',
                              endTime=openTime,
                              startTime=propDur),
                ActorInterval(self.propeller,
                              'chan',
                              constrainedLoop=1,
                              duration=propDur - openTime,
                              startTime=spinTime,
                              endTime=0.0)))
        self.suitTrack = Parallel(actInt,
                                  propTrack,
                                  name=self.taskName('trackName'))
        if doFadeOut:
            fadeOut = Sequence(
                Wait(4.0), Func(self.setTransparency, 1),
                self.colorScaleInterval(1,
                                        colorScale=VBase4(1, 1, 1, 0),
                                        startColorScale=VBase4(1, 1, 1, 1)),
                Func(self.clearColorScale), Func(self.clearTransparency),
                Func(self.reparentTo, hidden))
            self.suitTrack.append(fadeOut)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyAway)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)
        self.disableRay()

    def exitFlyAway(self):
        if self.suitTrack:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.cleanupPropeller()
        self.exitGeneral()

    def enterDie(self, ts=0):
        self.show()
        self.generateCog(isLose=1)
        self.nametag.clearChatText()
        self.deleteNameTag()
        deathSound = base.audio3d.loadSfx(
            "phase_3.5/audio/sfx/Cog_Death_Full.ogg")
        base.audio3d.attachSoundToObject(deathSound, self)
        trackName = self.uniqueName('enterDie')

        smallGears = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosionSmall.ptf')
        smallGears.getParticlesNamed('particles-1').setPoolSize(30)

        singleGear = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosion.ptf')
        singleGear.getParticlesNamed('particles-1').setPoolSize(1)

        smallGearExplosion = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosion.ptf')
        smallGearExplosion.getParticlesNamed('particles-1').setPoolSize(10)

        bigGearExplosion = ParticleLoader.loadParticleEffect(
            'phase_3.5/etc/gearExplosionBig.ptf')
        bigGearExplosion.getParticlesNamed('particles-1').setPoolSize(30)

        smallGears.setDepthWrite(False)
        singleGear.setDepthWrite(False)
        smallGearExplosion.setDepthWrite(False)
        bigGearExplosion.setDepthWrite(False)

        self.smallGears = smallGears
        self.smallGears.setPos(self.find('**/joint_head').getPos() + (0, 0, 2))
        self.singleGear = singleGear
        self.smallGearExp = smallGearExplosion
        self.bigGearExp = bigGearExplosion

        gearTrack = Sequence(Wait(0.7), Func(self.doSingleGear), Wait(1.5),
                             Func(self.doSmallGears), Wait(3.0),
                             Func(self.doBigExp))
        self.suitTrack = Parallel(Sequence(
            Wait(0.8),
            SoundInterval(deathSound,
                          node=self,
                          duration=deathSound.length() / 2)),
                                  Sequence(Wait(0.7), Func(self.doSingleGear),
                                           Wait(4.3), Func(self.suitExplode),
                                           Wait(1.0),
                                           Func(self.disableBodyCollisions),
                                           Func(self.__cleanupExplosion)),
                                  gearTrack,
                                  Sequence(
                                      ActorInterval(self, 'lose', duration=6),
                                      Func(self.getGeomNode().hide)),
                                  name=trackName)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getName(), self.exitDie)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, trackName)
        self.suitTrack.start(ts)
        del deathSound

    def doSingleGear(self):
        self.singleGear.start(self.getGeomNode())

    def doSmallGears(self):
        self.smallGears.start(self.getGeomNode())

    def doSmallExp(self):
        self.smallGearExp.start(self.getGeomNode())

    def doBigExp(self):
        self.bigGearExp.start(self.getGeomNode())

    def suitExplode(self):
        self.explosion = loader.loadModel(
            'phase_3.5/models/props/explosion.bam')
        self.explosion.setScale(0.5)
        self.explosion.reparentTo(render)
        self.explosion.setBillboardPointEye()
        if self.variant == Variant.SKELETON:
            self.explosion.setPos(
                self.getPart('body').find('**/joint_head').getPos(render) +
                (0, 0, 2))
        else:
            self.explosion.setPos(self.headModel.getPos(render) + (0, 0, 2))

    def __cleanupExplosion(self):
        if self.explosion:
            self.explosion.removeNode()
            self.explosion = None

    def exitDie(self):
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getName())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'singleGear'):
            self.singleGear.cleanup()
            del self.singleGear
        if hasattr(self, 'smallGears'):
            self.smallGears.cleanup()
            del self.smallGears
        if hasattr(self, 'smallGearExp'):
            self.smallGearExp.cleanup()
            del self.smallGearExp
        if hasattr(self, 'bigGearExp'):
            self.bigGearExp.cleanup()
            del self.bigGearExp
        self.__cleanupExplosion()

    def enterWin(self, ts=0):
        self.play('win')

    def exitWin(self):
        self.exitGeneral()

    # END STATES

    def generate(self, suitPlan, variant, voice=None, hideFirst=True):
        self.suitPlan = suitPlan
        self.suit = suitPlan.getSuitType()
        self.head = suitPlan.getHead()
        self.dept = suitPlan.getDept()
        self.handColor = suitPlan.getHandColor()
        self.variant = variant
        self.setVoice(voice)
        self.generateCog()
        if hideFirst:
            self.hide()

    def __blinkRed(self, task):
        self.healthBar.setColor(SuitGlobals.healthColors[3], 1)
        self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[3], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.17)
        return Task.done

    def __blinkGray(self, task):
        if not self.healthBar:
            return
        self.healthBar.setColor(SuitGlobals.healthColors[4], 1)
        self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[4], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.0)
        return Task.done

    def generateHealthBar(self):
        self.removeHealthBar()
        button = loader.loadModel('phase_3.5/models/gui/matching_game_gui.bam'
                                  ).find('**/minnieCircle')
        button.setScale(3.0)
        button.setH(180)
        button.setColor(SuitGlobals.healthColors[0])
        chestNull = self.find('**/def_joint_attachMeter')
        if chestNull.isEmpty():
            chestNull = self.find('**/joint_attachMeter')
        button.reparentTo(chestNull)
        self.healthBar = button
        self.healthBarGlow = loader.loadModel(
            'phase_3.5/models/props/glow.bam')
        self.healthBarGlow.reparentTo(self.healthBar)
        self.healthBarGlow.setScale(0.28)
        self.healthBarGlow.setPos(-0.005, 0.01, 0.015)
        self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[0])
        button.flattenLight()
        self.condition = 0
        if hasattr(self, 'getHealth'):
            self.updateHealthBar(self.getHealth())

    def updateHealthBar(self, hp):
        if not self.healthBar:
            return
        if hp > self.health:
            self.health = hp
        health = 0.0
        try:
            health = float(hp) / float(self.maxHealth)
        except:
            pass
        if health > 0.95:
            condition = 0
        elif health > 0.7:
            condition = 1
        elif health > 0.3:
            condition = 2
        elif health > 0.05:
            condition = 3
        elif health > 0.0:
            condition = 4
        else:
            condition = 5
        if self.condition != condition:
            if condition == 4:
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75),
                                      Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            elif condition == 5:
                if self.condition == 4:
                    taskMgr.remove(self.taskName('blink-task'))
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25),
                                      Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            else:
                self.healthBar.setColor(SuitGlobals.healthColors[condition], 1)
                self.healthBarGlow.setColor(
                    SuitGlobals.healthGlowColors[condition], 1)
            self.condition = condition

    def removeHealthBar(self):
        if self.healthBar:
            self.healthBar.removeNode()
            self.healthBar = None
        if self.condition == 4 or self.condition == 5:
            taskMgr.remove(self.taskName('blink-task'))
        self.healthCondition = 0
        return

    def initializeLocalCollisions(self, name):
        self.notify.info('Initializing Local Collisions!')
        Avatar.initializeLocalCollisions(self, 1, 3, name)

    def initializeBodyCollisions(self):
        self.notify.info('Initializing Body Collisions!')
        Avatar.initializeBodyCollisions(self, self.avatarType, 6, 2)
        self.initializeRay(self.avatarType, 2)

    def hideSuit(self):
        self.hide()

    def showSuit(self):
        self.show()
        fadeIn = Sequence(
            Func(self.setTransparency, 1),
            self.colorScaleInterval(0.6,
                                    colorScale=Vec4(1, 1, 1, 1),
                                    startColorScale=Vec4(1, 1, 1, 0)),
            Func(self.clearColorScale), Func(self.clearTransparency),
            Func(self.reparentTo, render))
        fadeIn.start()

    def generateCog(self, isLose=0):
        self.cleanup()
        if not isLose:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.loadModel(
                    'phase_5/models/char/cog%s_robot-zero.bam' %
                    (str(self.suit)), 'body')
            else:
                self.loadModel(
                    'phase_3.5/models/char/suit%s-mod.bam' % (str(self.suit)),
                    'body')
            animations = SuitGlobals.animations
            anims = {}
            for anim in animations:
                if not self.suit in anim.getSuitTypes():
                    continue
                path = 'phase_%s/models/char/suit%s-%s.bam' % (
                    anim.getPhase(), self.suit, anim.getFile())
                anims[anim.getName()] = path
            self.loadAnims(anims, 'body')
            self.generateHealthBar()
            self.generatePropeller()
        else:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.loadModel(
                    'phase_5/models/char/cog%s_robot-lose-mod.bam' %
                    (str(self.suit)), 'body')
            else:
                self.loadModel(
                    'phase_4/models/char/suit%s-lose-mod.bam' %
                    (str(self.suit)), 'body')
            self.loadAnims(
                {
                    'lose':
                    'phase_4/models/char/suit%s-lose.bam' % (str(self.suit))
                }, 'body')
        if self.variant != Variant.SKELETON:
            self.headModel = self.head.generate()
            self.headModel.reparentTo(self.find('**/joint_head'))
        if self.suitPlan.getName() == SuitGlobals.VicePresident:
            self.headModel.setScale(0.35)
            self.headModel.setHpr(270, 0, 270)
            self.headModel.setZ(-0.10)
            self.headModel.loop('neutral')
        self.setClothes()
        self.setAvatarScale(self.suitPlan.getScale() /
                            SuitGlobals.scaleFactors[self.suit])
        self.setHeight(self.suitPlan.getHeight())
        self.setupNameTag()
        Avatar.initShadow(self)

    def cleanup(self):
        self.cleanupPropeller()
        self.clearChatbox()
        if self.shadow:
            self.deleteShadow()
        if self.getPart('body'):
            self.removePart('body')
        if self.headModel:
            self.headModel.removeNode()
            self.headModel = None
        self.timestampAnimTrack = None

    def generatePropeller(self):
        self.cleanupPropeller()
        self.propeller = Actor(
            'phase_4/models/props/propeller-mod.bam',
            {'chan': 'phase_4/models/props/propeller-chan.bam'})
        self.propeller.reparentTo(self.find('**/joint_head'))
        self.propellerSounds['in'] = self.audio3d.loadSfx(
            SuitGlobals.propellerInSfx)
        self.propellerSounds['out'] = self.audio3d.loadSfx(
            SuitGlobals.propellerOutSfx)
        self.propellerSounds['neutral'] = self.audio3d.loadSfx(
            SuitGlobals.propellerNeutSfx)
        for sound in self.propellerSounds.values():
            self.audio3d.attachSoundToObject(sound, self.propeller)

    def cleanupPropeller(self):
        for sound in self.propellerSounds.values():
            self.audio3d.detachSound(sound)
            sound.stop()
        self.propellerSounds = {}
        if self.propeller:
            self.propeller.cleanup()
            self.propeller = None

    def setVoice(self, voice):
        if not voice:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.voice = Voice.SKELETON
            else:
                self.voice = Voice.NORMAL
        else:
            self.voice = voice

    def setClothes(self):
        if self.variant == Variant.SKELETON:
            parts = self.findAllMatches('**/pPlane*')
            for partNum in range(0, parts.getNumPaths()):
                bb = parts.getPath(partNum)
                bb.setTwoSided(1)
            tie = loader.loadTexture('phase_5/maps/cog_robot_tie_%s.jpg' %
                                     self.dept.getTie())
            tie.setMinfilter(Texture.FTLinearMipmapLinear)
            tie.setMagfilter(Texture.FTLinear)
            self.find('**/tie').setTexture(tie, 1)
        else:
            prefix = 'phase_3.5/maps/' + self.dept.getClothingPrefix(
            ) + '_%s.jpg'
            if self.variant == Variant.WAITER:
                prefix = 'phase_3.5/maps/waiter_m_%s.jpg'
            self.find('**/legs').setTexture(loader.loadTexture(prefix % 'leg'),
                                            1)
            self.find('**/arms').setTexture(
                loader.loadTexture(prefix % 'sleeve'), 1)
            self.find('**/torso').setTexture(
                loader.loadTexture(prefix % 'blazer'), 1)
            self.find('**/hands').setColor(self.handColor)

    def setName(self, nameString, charName):
        Avatar.setName(self,
                       nameString,
                       avatarType=self.avatarType,
                       charName=charName,
                       createNow=1)

    def setupNameTag(self, tempName=None):
        Avatar.setupNameTag(self, tempName=tempName)
        if self.nametag:
            if self.level > 0:
                self.nametag.setText(self.nametag.getText() +
                                     '\n%s\nLevel %s' %
                                     (self.dept.getName(), self.level))
            else:
                self.nametag.setText(self.nametag.getText() + '\n%s' %
                                     (self.dept.getName()))

    def setChat(self, chat):
        self.clearChatbox()
        Avatar.setChat(self, chat)
        self.chat = chat
        chatDial = None
        questionDial = self.voice.getSoundFile('question')
        question02Dial = None
        gruntDial = self.voice.getSoundFile('grunt')
        statementDial = self.voice.getSoundFile('statement')
        if self.voice == Voice.NORMAL:
            question02Dial = self.voice.getSoundFile('question_2')

        if '!' in self.chat:
            chatDial = self.audio3d.loadSfx(gruntDial)
        elif '?' in self.chat:
            questionDials = [questionDial]
            if self.voice == Voice.NORMAL:
                questionDials.append(question02Dial)
            chatDial = self.audio3d.loadSfx(random.choice(questionDials))
        else:
            chatDial = self.audio3d.loadSfx(statementDial)
        self.chatDial = chatDial

        if self.variant == Variant.SKELETON:
            self.audio3d.attachSoundToObject(self.chatDial, self)
        else:
            self.audio3d.attachSoundToObject(self.chatDial, self.headModel)
        base.playSfx(self.chatDial, node=self)

    def clearChatbox(self):
        self.clearChat()
        self.chat = None
        if self.chatDial:
            self.chatDial.stop()
            self.chatDial = None

    def getDept(self):
        return self.dept

    def getVariant(self):
        return self.variant

    def disable(self):
        if self.suitTrack:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.animFSM.requestFinalState()
        self.cleanup()
        Avatar.disable(self)

    def delete(self):
        Avatar.delete(self)
        self.cleanup()
class DistributedPieTurret(DistributedAvatar, DistributedSmoothNode):
	notify = directNotify.newCategory("DistributedPieTurret")

	def __init__(self, cr):
		DistributedAvatar.__init__(self, cr)
		DistributedSmoothNode.__init__(self, cr)
		self.fsm = ClassicFSM(
			'DistributedPieTurret',
			[
				State('off', self.enterOff, self.exitOff),
				State('scan', self.enterScan, self.exitScan),
				#State('lockOn', self.enterLockOn, self.exitLockOn),
				State('shoot', self.enterShoot, self.exitShoot)
			],
			'off', 'off'
		)
		self.fsm.enterInitialState()
		self.cannon = None
		self.track = None
		self.avatar = None
		self.readyPie = None
		self.explosion = None
		self.wallCollNode = None
		self.eventCollNode = None
		self.event = None
		self.piesInFlight = []

	def setHealth(self, hp):
		DistributedAvatar.setHealth(self, hp)
		if self.getAvatar() == base.localAvatar.doId:
			base.localAvatar.getMyBattle().getTurretManager().updateTurretGui()

	def die(self):
		self.fsm.requestFinalState()
		turretPos = self.cannon.getPos(render)
		self.removeTurret()
		self.explosion = loader.loadModel("phase_3.5/models/props/explosion.bam")
		self.explosion.setScale(0.5)
		self.explosion.reparentTo(render)
		self.explosion.setBillboardPointEye()
		self.explosion.setPos(turretPos + (0, 0, 1))
		sfx = base.localAvatar.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_cogfall_apart.mp3")
		base.localAvatar.audio3d.attachSoundToObject(sfx, self)
		base.playSfx(sfx)

	def showAndMoveHealthLabel(self):
		self.unstashHpLabel()
		self.stopMovingHealthLabel()
		moveTrack = LerpPosInterval(self.healthLabel,
								duration = 0.5,
								pos = Point3(0, 0, 5),
								startPos = Point3(0, 0, 0),
								blendType = 'easeOut')
		self.healthLabelTrack = Sequence(moveTrack, Wait(1.0), Func(self.stashHpLabel))
		self.healthLabelTrack.start()

	def enterOff(self):
		pass

	def exitOff(self):
		pass

	def makeSplat(self, pos):
		splat = Actor("phase_3.5/models/props/splat-mod.bam",
			{"chan": "phase_3.5/models/props/splat-chan.bam"})
		splat.setScale(0.5)
		splat.reparentTo(render)
		splat.setBillboardPointEye()
		splat.setColor(VBase4(1, 1, 0, 1))
		x, y, z = pos
		splat.setPos(x, y, z)
		sfx = base.localAvatar.audio3d.loadSfx("phase_4/audio/sfx/AA_wholepie_only.mp3")
		base.localAvatar.audio3d.attachSoundToObject(sfx, splat)
		base.playSfx(sfx)
		track = Sequence(
			ActorInterval(splat, "chan"),
			Func(splat.cleanup),
			Func(splat.removeNode)
		)
		track.start()

	def d_makeSplat(self, pos):
		self.sendUpdate("makeSplat", [pos])

	def b_makeSplat(self, pos):
		self.d_makeSplat(pos)
		self.makeSplat(pos)

	def shoot(self, suitId):
		self.fsm.request('shoot', [suitId])

	def enterShoot(self, suitId):
		if self.cannon:
			smoke = loader.loadModel("phase_4/models/props/test_clouds.bam")
			smoke.setBillboardPointEye()
			smoke.reparentTo(self.cannon.find('**/cannon'))
			smoke.setPos(0, 6, -3)
			smoke.setScale(0.5)
			smoke.wrtReparentTo(render)
			self.suit = self.cr.doId2do.get(suitId)
			self.cannon.find('**/cannon').lookAt(self.suit.find('**/joint_head'))
			self.cannon.find('**/square_drop_shadow').headsUp(self.suit.find('**/joint_head'))
			self.eventId = random.uniform(0, 100000000)
			track = Sequence(Parallel(LerpScaleInterval(smoke, 0.5, 3), LerpColorScaleInterval(smoke, 0.5, Vec4(2, 2, 2, 0))), Func(smoke.removeNode))
			track.start()
			self.createAndShootPie()

	def loadPieInTurret(self):
		if self.cannon:
			if self.readyPie:
				self.readyPie.removeNode()
				self.readyPie = None
			pie = loader.loadModel("phase_3.5/models/props/tart.bam")
			pie.reparentTo(self.cannon.find('**/cannon'))
			pie.setY(5.2)
			pie.setHpr(90, -90, 90)
			self.readyPie = pie

	def removePieInTurret(self):
		if self.readyPie:
			self.readyPie.removeNode()
			self.readyPie = None

	def createAndShootPie(self):
		if not self.readyPie:
			self.loadPieInTurret()
		local = 0
		if base.localAvatar.doId == self.getAvatar():
			local = 1
		proj = ProjectilePie(self.uniqueName('pieTurretCollision') + str(self.eventId), self.cannon.find('**/cannon'), self.readyPie, Point3(0, 200, -90), 0.9, 2.5, local, self)
		self.readyPie = None
		self.piesInFlight.append(proj)
		if local:
			self.acceptOnce(self.uniqueName('pieTurretCollision') + str(self.eventId), self.handlePieCollision)
		Sequence(Wait(0.25), Func(self.loadPieInTurret)).start()

	def handlePieCollision(self, entry, proj):
		x, y, z = proj.pie.getPos(render)
		self.b_makeSplat([x, y, z])
		proj.cleanup()
		if base.localAvatar.doId == self.getAvatar():
			intoNP = entry.getIntoNodePath()
			avNP = intoNP.getParent()
			for key in self.cr.doId2do.keys():
				obj = self.cr.doId2do[key]
				if obj.__class__.__name__ == "DistributedSuit":
					if obj.getKey() == avNP.getKey():
						if obj.getHealth() > 0:
							base.localAvatar.sendUpdate('suitHitByPie', [obj.doId, GagGlobals.getIDByName(CIGlobals.WholeCreamPie)])

	def exitShoot(self):
		del self.suit
		del self.eventId

	def scan(self, timestamp = None, afterShooting = 0):
		if timestamp == None:
			ts = 0.0
		else:
			ts = globalClockDelta.localElapsedTime(timestamp)

		self.fsm.request('scan', [ts, afterShooting])

	def enterScan(self, ts = 0, afterShooting = 0):
		if afterShooting:
			self.track = Parallel(
				LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
					startHpr = self.cannon.find('**/cannon').getHpr(), blendType = 'easeInOut'),
				LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
					startHpr = self.cannon.find('**/square_drop_shadow').getHpr(), blendType = 'easeInOut'),
				name = "afterShootTrack" + str(id(self))
			)
			self.track.setDoneEvent(self.track.getName())
			self.acceptOnce(self.track.getDoneEvent(), self._afterShootTrackDone)
			self.track.start(ts)
		else:
			self.track = Parallel(
				Sequence(
					LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (60, 0, 0),
						startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
					LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
						startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
				),
				Sequence(
					LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (60, 0, 0),
						startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
					LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
						startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
				)
			)
			self.track.loop(ts)

	def _afterShootTrackDone(self):
		self.track = None
		self.track = Parallel(
			Sequence(
				LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (60, 0, 0),
					startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
				LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
					startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
			),
			Sequence(
				LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (60, 0, 0),
					startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
				LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
					startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
			)
		)
		self.track.loop()

	def exitScan(self):
		if self.track:
			self.ignore(self.track.getDoneEvent())
			self.track.finish()
			self.track = None

	def setAvatar(self, avId):
		self.avatar = avId

	def getAvatar(self):
		return self.avatar

	def makeTurret(self):
		self.cannon = loader.loadModel("phase_4/models/minigames/toon_cannon.bam")
		self.cannon.reparentTo(self)
		self.loadPieInTurret()
		self.setupWallSphere()
		if self.getAvatar() == base.localAvatar.doId:
			self.setupEventSphere()

	def setupWallSphere(self):
		sphere = CollisionSphere(0.0, 0.0, 0.0, 3.0)
		node = CollisionNode('DistributedPieTurret.WallSphere')
		node.addSolid(sphere)
		node.setCollideMask(CIGlobals.WallBitmask)
		self.wallCollNode = self.cannon.attachNewNode(node)
		self.wallCollNode.setZ(2)
		self.wallCollNode.setY(1.0)

	def removeWallSphere(self):
		if self.wallCollNode:
			self.wallCollNode.removeNode()
			self.wallCollNode = None

	def removeTurret(self):
		self.removeWallSphere()
		self.removePieInTurret()
		if self.cannon:
			self.cannon.removeNode()
			self.cannon = None

	def generate(self):
		DistributedAvatar.generate(self)
		DistributedSmoothNode.generate(self)

	def announceGenerate(self):
		DistributedAvatar.announceGenerate(self)
		DistributedSmoothNode.announceGenerate(self)
		self.healthLabel.setScale(1.1)
		self.makeTurret()

	def disable(self):
		self.fsm.requestFinalState()
		del self.fsm
		for projs in self.piesInFlight:
			projs.cleanup()
		self.piesInFlight = None
		if self.explosion:
			self.explosion.removeNode()
			self.explosion = None
		self.removeTurret()
		DistributedSmoothNode.disable(self)
		DistributedAvatar.disable(self)
class DistributedTutorial(DistributedObject):
    notify = directNotify.newCategory('DistributedTutorial')
    GUIDE_NAME = 'Professor Prepostera'
    GUIDE_START_POS = (5, 10, -0.5)
    GUIDE_WATCH_POS = (12.4, 27.92, 0)
    GUIDE_WATCH_HPR = (41.63, 0, 0)
    GUIDE_INTRO_SPEECH = [
        'Hey, looks like you made it!', 'So, welcome to OToontown.',
        'OToontown is short for Old Toontown, or Toontown from the past.',
        'Not long ago, Toons used to live in present day Toontown.',
        'Unfortunately, the Cogs planned a mega-invasion that was sure to be a complete takeover of Toontown and make all Toons go sad for good.',
        "There was no way we could have let that happen, so we built a time machine, and sent every Toon back in time to OToontown, where Cogs didn't exist yet.",
        'The Cogs completely took over present day Toontown, and turned it into what they wanted it to be - a business metropolis.',
        'Toons happily live and play in OToontown now, but we want to learn about present day Toontown...',
        ' ...or as we now call it, CogTropolis.',
        "We've built time machines that send Toons back to CogTropolis to fight Cogs and to see what the Cogs have done.",
        "We know that the Cogs took over Toontown and turned it into a grey business city, but we don't know how they did it.",
        'Shopkeepers around OToontown will reward you for finding evidence that may help solve the mystery of how the Cogs turned Toontown into CogTropolis.',
        'Before you are able to head to CogTropolis, you need to be trained for battle.',
        'The Cogs have become much more skilled battlers and no longer wait for you to throw a gag before attacking you.',
        'This is much more difficult for Toons, and it may take some time to get used to.',
        "I'm going to give you 2 gags to start...",
        'A cupcake, and a squirting flower.',
        'Equip Gags in your loadout to use by pressing the corresponding key on your keyboard.',
        'You can use or throw the Gag that you have equipped by pressing the ALT key.',
        'Also, use the Arrow Keys on your keyboard to move, and press CTRL to jump.',
        "I'm going to summon one of our dummy bots for you to practice battling.",
        "Click your mouse when you're ready."
    ]
    GUIDE_PT2_INFO = [
        "Now it'll get a tad bit tougher.",
        'This next dummy bot will be walking around.',
        'This will test your aiming skills.',
        "Click your mouse when you're ready."
    ]
    GUIDE_PT3_INFO = [
        'This final dummy bot will walk around and try to attack you at times.',
        'Defeat this Cog to continue on to the next part of the tutorial.'
    ]
    GUIDE_DONE = [
        'Great!',
        'Did you know that Cog Invasion Online Alpha allows you to use any Gag that you want?',
        'Just use the Gags page in your Shticker Book to swap out the Gags that you want to be able to use!',
        'Also during Alpha testing, your Toon will start off with 5000 jellybeans and 100 Laff points.',
        "If you're looking to fight some Cogs, hop on the Trolley in the playgrounds to be teleported to CogTropolis!",
        'Each playground has a different difficulty of Cogs, so be careful!',
        "You can only fight Cogs in Toontown Central, Minnie's Melodyland, Donald's Dock, and Donald's Dreamland at the moment.",
        'You can also visit the Minigame Area using your Shticker Book to play some fun minigames!',
        'These games are best to play with other Toons!',
        'Remember, if you find any bugs or strange things during gameplay, you can go to the Contact Us page at coginvasion.com to report the issue.',
        'Have fun!'
    ]
    GUIDE_START_TRAINING = "Alright! Let's do this!"

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM.ClassicFSM('TutorialFSM', [
            State.State('off', self.enterOff, self.exitOff),
            State.State('newPlayerEmerge', self.enterPlayerEmerge,
                        self.exitPlayerEmerge, ['off', 'introSpeech']),
            State.State('introSpeech', self.enterGuideIntroSpeech,
                        self.exitGuideIntroSpeech,
                        ['off', 'introSpeech2Training']),
            State.State('introSpeech2Training', self.enterIntroSpeech2Training,
                        self.exitIntroSpeech2Training, ['off', 'training1']),
            State.State('training1', self.enterTrainingPT1,
                        self.exitTrainingPT1, ['off', 'training2info']),
            State.State('training2info', self.enterTraining2Info,
                        self.exitTraining2Info, ['off', 'training2']),
            State.State('training2', self.enterTrainingPT2,
                        self.exitTrainingPT2, ['off', 'training3info']),
            State.State('training3info', self.enterTraining3Info,
                        self.exitTraining3Info, ['off', 'training3']),
            State.State('training3', self.enterTrainingPT3,
                        self.exitTrainingPT3, ['off', 'trainingDone']),
            State.State('trainingDone', self.enterTrainingDone,
                        self.exitTrainingDone, ['off', 'leaveTutorial']),
            State.State('leaveTutorial', self.enterLeaveTutorial,
                        self.exitLeaveTutorial, ['off'])
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.dnaStore = DNAStorage()
        self.streetGeom = None
        self.sky = None
        self.skyUtil = SkyUtil()
        self.guide = None
        self.music = None
        self.battleMusic = None
        self.playerCamPos = None
        self.playerCamHpr = None
        return

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def introStuff(self):
        base.localAvatar.getGeomNode().hide()
        base.localAvatar.setPos(0, 0, -0.5)
        base.localAvatar.setHpr(0, 0, 0)
        self.guide.setPos(self.GUIDE_START_POS)
        self.guide.headsUp(base.localAvatar)
        base.localAvatar.attachCamera()

    def enterPlayerEmerge(self):
        self.introStuff()
        self.guide.loop('neutral')
        base.transitions.irisIn()
        base.taskMgr.doMethodLater(1.0, self.__playerEmerge,
                                   'playerEmergeTask')

    def __playerEmerge(self, task):
        base.localAvatar.setAnimState('teleportIn',
                                      callback=self.__playerEmergeFinished)
        return Task.done

    def __playerEmergeFinished(self):
        base.localAvatar.setAnimState('neutral')
        self.fsm.request('introSpeech')

    def exitPlayerEmerge(self):
        base.localAvatar.detachCamera()
        base.taskMgr.remove('playerEmergeTask')
        base.transitions.noTransitions()

    def enterGuideIntroSpeech(self):
        base.localAvatar.attachCamera()
        renderPos = base.camera.getPos(render)
        renderHpr = base.camera.getHpr(render)
        base.localAvatar.detachCamera()
        endPos = base.localAvatar.getPos(render) + (0, 0, 4)
        base.camera.setPos(endPos)
        base.camera.lookAt(self.guide, 0, 0, 3)
        endHpr = base.camera.getHpr(render)
        base.camera.setPos(renderPos)
        base.camera.setHpr(renderHpr)
        self.chatIndex = -1
        self.doNextIntroSpeech()
        self.camMoveTrack = Sequence(
            Parallel(
                LerpPosInterval(base.camera,
                                duration=3.0,
                                pos=endPos,
                                startPos=renderPos,
                                blendType='easeOut'),
                LerpQuatInterval(base.camera,
                                 duration=3.0,
                                 hpr=endHpr,
                                 startHpr=renderHpr,
                                 blendType='easeOut')),
            Func(base.localAvatar.getGeomNode().hide))
        self.camMoveTrack.start()

    def __finishedReadingGuideIntroSpeech(self):
        self.guide.autoClearChat = True
        self.guide.setChat(self.GUIDE_START_TRAINING)
        self.fsm.request('introSpeech2Training')

    def doNextIntroSpeech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_INTRO_SPEECH):
            self.__finishedReadingGuideIntroSpeech()
            return
        self.guide.setChat(self.GUIDE_INTRO_SPEECH[self.chatIndex])
        Sequence(Wait(0.1),
                 Func(self.acceptOnce, 'mouse1-up',
                      self.doNextIntroSpeech)).start()

    def exitGuideIntroSpeech(self):
        self.camMoveTrack.finish()
        base.localAvatar.getGeomNode().show()
        del self.camMoveTrack
        del self.chatIndex

    def enterIntroSpeech2Training(self):
        startCamPos = base.camera.getPos(render)
        startCamHpr = base.camera.getHpr(render)
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        base.localAvatar.attachCamera()
        endCamPos = base.camera.getPos(render)
        endCamHpr = base.camera.getHpr(render)
        base.localAvatar.detachCamera()
        startHpr = self.guide.getHpr(render)
        self.guide.headsUp(self.GUIDE_WATCH_POS)
        endHpr = self.guide.getHpr(render)
        self.guide.loop('run')
        self.camMoveIval = Parallel(
            LerpPosInterval(base.camera,
                            duration=2.0,
                            pos=endCamPos,
                            startPos=startCamPos,
                            blendType='easeOut'),
            LerpQuatInterval(base.camera,
                             duration=2.0,
                             hpr=endCamHpr,
                             startHpr=startCamHpr,
                             blendType='easeOut'),
            Sequence(
                LerpPosInterval(self.guide,
                                duration=2.0,
                                pos=self.GUIDE_WATCH_POS,
                                startPos=self.guide.getPos(render)),
                Func(self.guide.loop, 'walk'),
                LerpHprInterval(self.guide,
                                duration=1.0,
                                hpr=self.GUIDE_WATCH_HPR,
                                startHpr=endHpr),
                Func(self.guide.loop, 'neutral')),
            LerpHprInterval(self.guide,
                            duration=1.0,
                            hpr=endHpr,
                            startHpr=startHpr))
        self.camMoveIval.setDoneEvent('introSpeech2TrainingDone')
        self.acceptOnce('introSpeech2TrainingDone', self.__handleIS2TDone)
        self.camMoveIval.start()

    def __handleIS2TDone(self):
        self.fsm.request('training1')

    def exitIntroSpeech2Training(self):
        self.ignore('introSpeech2TrainingDone')
        self.camMoveIval.finish()
        del self.camMoveIval

    def makeWhisper(self, msg):
        whisper = WhisperPopup(msg, CIGlobals.getToonFont(),
                               ChatGlobals.WTSystem)
        whisper.manage(base.marginManager)

    def enterTrainingPT1(self):
        self.music.stop()
        base.playMusic(self.battleMusic, volume=0.8, looping=1)
        self.sendUpdate('makeSuit', [0])
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.startBlink()
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.collisionsOn()
        base.localAvatar.enableAvatarControls()
        base.localAvatar.enableGags(1)
        base.localAvatar.showGagButton()
        base.localAvatar.startTrackAnimToSpeed()
        self.makeWhisper(
            'This should be pretty simple. Just throw a gag at this dummy bot to defeat it.'
        )

    def suitNoHealth(self, index):
        if index == 0:
            self.makeWhisper(
                ('Good job, {0}!').format(base.localAvatar.getName()))
        else:
            if index == 1:
                self.makeWhisper("Wow, you're doing very well!")

    def suitExploded(self, index):
        if index == 0:
            self.makeWhisper(
                'Pick up the jellybean that he dropped. You can use them to buy more gags for your Toon.'
            )
        self.battleMusic.stop()
        base.playMusic(self.music, looping=1, volume=0.8)

    def pickedUpJellybean(self):
        if self.fsm.getCurrentState().getName() == 'training1':
            self.fsm.request('training2info')
        else:
            if self.fsm.getCurrentState().getName() == 'training2':
                self.fsm.request('training3info')
            else:
                if self.fsm.getCurrentState().getName() == 'training3':
                    self.fsm.request('trainingDone')

    def exitTrainingPT1(self):
        base.localAvatar.lastState = None
        base.localAvatar.disableAvatarControls()
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.stopBlink()
        base.localAvatar.collisionsOff()
        base.localAvatar.controlManager.placeOnFloor()
        base.localAvatar.disableGags()
        base.localAvatar.stopTrackAnimToSpeed()
        base.localAvatar.hideGagButton()
        return

    def enterTraining2Info(self):
        base.camera.setPos(3.09, 37.16, 3.93)
        base.camera.setHpr(225, 0, 0)
        self.guide.autoClearChat = False
        self.chatIndex = -1
        self.doNextTraining2Speech()

    def __finishedReadingGuideTraining2Speech(self):
        self.guide.autoClearChat = True
        self.guide.clearChat()
        self.fsm.request('training2')

    def doNextTraining2Speech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_PT2_INFO):
            self.__finishedReadingGuideTraining2Speech()
            return
        self.guide.setChat(self.GUIDE_PT2_INFO[self.chatIndex])
        Sequence(
            Wait(0.1),
            Func(self.acceptOnce, 'mouse1-up',
                 self.doNextTraining2Speech)).start()

    def exitTraining2Info(self):
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        del self.chatIndex

    def enterTrainingPT2(self):
        self.music.stop()
        base.playMusic(self.battleMusic, volume=0.8, looping=1)
        self.sendUpdate('makeSuit', [1])
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.startBlink()
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.collisionsOn()
        base.localAvatar.enableAvatarControls()
        base.localAvatar.enableGags(1)
        base.localAvatar.showGagButton()
        base.localAvatar.startTrackAnimToSpeed()

    def exitTrainingPT2(self):
        base.localAvatar.lastState = None
        base.localAvatar.disableAvatarControls()
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.stopBlink()
        base.localAvatar.collisionsOff()
        base.localAvatar.controlManager.placeOnFloor()
        base.localAvatar.disableGags()
        base.localAvatar.stopTrackAnimToSpeed()
        base.localAvatar.hideGagButton()
        return

    def enterTraining3Info(self):
        base.camera.setPos(3.09, 37.16, 3.93)
        base.camera.setHpr(225, 0, 0)
        self.guide.autoClearChat = False
        self.chatIndex = -1
        self.doNextTraining3Speech()

    def __finishedReadingGuideTraining3Speech(self):
        self.guide.autoClearChat = True
        self.guide.clearChat()
        self.fsm.request('training3')

    def doNextTraining3Speech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_PT3_INFO):
            self.__finishedReadingGuideTraining3Speech()
            return
        self.guide.setChat(self.GUIDE_PT3_INFO[self.chatIndex])
        Sequence(
            Wait(0.1),
            Func(self.acceptOnce, 'mouse1-up',
                 self.doNextTraining3Speech)).start()

    def exitTraining3Info(self):
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        del self.chatIndex

    def enterTrainingPT3(self):
        self.music.stop()
        base.playMusic(self.battleMusic, volume=0.8, looping=1)
        self.sendUpdate('makeSuit', [2])
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.startBlink()
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.collisionsOn()
        base.localAvatar.enableAvatarControls()
        base.localAvatar.enableGags(1)
        base.localAvatar.showGagButton()
        base.localAvatar.startTrackAnimToSpeed()

    def exitTrainingPT3(self):
        base.localAvatar.lastState = None
        base.localAvatar.disableAvatarControls()
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.stopBlink()
        base.localAvatar.collisionsOff()
        base.localAvatar.controlManager.placeOnFloor()
        base.localAvatar.disableGags()
        base.localAvatar.stopTrackAnimToSpeed()
        base.localAvatar.hideGagButton()
        return

    def enterTrainingDone(self):
        base.camera.setPos(3.09, 37.16, 3.93)
        base.camera.setHpr(225, 0, 0)
        self.guide.autoClearChat = False
        self.chatIndex = -1
        self.doNextTrainingDoneSpeech()

    def __finishedReadingGuideTrainingDoneSpeech(self):
        self.guide.autoClearChat = True
        self.guide.clearChat()
        self.fsm.request('leaveTutorial')

    def doNextTrainingDoneSpeech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_DONE):
            self.__finishedReadingGuideTrainingDoneSpeech()
            return
        self.guide.setChat(self.GUIDE_DONE[self.chatIndex])
        Sequence(
            Wait(0.1),
            Func(self.acceptOnce, 'mouse1-up',
                 self.doNextTrainingDoneSpeech)).start()

    def exitTrainingDone(self):
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        del self.chatIndex

    def enterLeaveTutorial(self):
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.b_setAnimState('teleportOut',
                                        callback=self.__teleOutDone)

    def __teleOutDone(self):
        zoneId = CIGlobals.ToontownCentralId
        hoodId = CIGlobals.ToontownCentral
        whereName = 'playground'
        avId = base.localAvatar.doId
        loaderName = 'safeZoneLoader'
        self.sendUpdate('finishedTutorial')
        self.cr.playGame.load()
        self.cr.playGame.enter(hoodId, zoneId, base.localAvatar.doId)

    def exitLeaveTutorial(self):
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()

    def announceGenerate(self):
        DistributedObject.announceGenerate(self)
        base.transitions.fadeScreen(0.0)
        self.guide = Toon(base.cr)
        self.guide.autoClearChat = False
        self.guide.parseDNAStrand(NPCGlobals.NPC_DNA[self.GUIDE_NAME])
        self.guide.setName(self.GUIDE_NAME)
        self.guide.generateToon()
        self.guide.nametag.setNametagColor(
            NametagGlobals.NametagColors[NametagGlobals.CCNPC])
        self.guide.nametag.setActive(0)
        self.guide.nametag.updateAll()
        self.guide.startBlink()
        self.guide.reparentTo(render)
        base.localAvatar.reparentTo(render)
        loader.loadDNAFile(self.dnaStore,
                           'phase_3.5/dna/storage_tutorial.pdna')
        node = loader.loadDNAFile(self.dnaStore,
                                  'phase_3.5/dna/tutorial_street.pdna')
        if node.getNumParents() == 1:
            self.streetGeom = NodePath(node.getParent(0))
            self.streetGeom.reparentTo(hidden)
        else:
            self.streetGeom = hidden.attachNewNode(node)
        self.streetGeom.flattenMedium()
        gsg = base.win.getGsg()
        if gsg:
            self.streetGeom.prepareScene(gsg)
        self.streetGeom.reparentTo(render)
        self.streetGeom.setPos(20.5, -20, 0)
        self.streetGeom.setH(90)
        self.sky = loader.loadModel('phase_3.5/models/props/TT_sky.bam')
        self.skyUtil.startSky(self.sky)
        self.sky.reparentTo(camera)
        ce = CompassEffect.make(NodePath(),
                                CompassEffect.PRot | CompassEffect.PZ)
        self.sky.node().setEffect(ce)
        self.music = base.loadMusic('phase_3.5/audio/bgm/TC_SZ.mid')
        base.playMusic(self.music, volume=0.8, looping=1)
        self.battleMusic = base.loadMusic(
            'phase_3.5/audio/bgm/encntr_general_bg.mid')
        self.fsm.request('newPlayerEmerge')
        base.localAvatar.inTutorial = True

    def disable(self):
        self.fsm.requestFinalState()
        del self.fsm
        if self.guide:
            self.guide.disable()
            self.guide.delete()
            self.guide = None
        if self.streetGeom:
            self.streetGeom.removeNode()
            self.streetGeom = None
        if self.sky:
            self.sky.removeNode()
            self.sky = None
        if self.music:
            self.music.stop()
            self.music = None
        if self.battleMusic:
            self.battleMusic.stop()
            self.battleMusic = None
        self.dnaStore.reset_nodes()
        self.dnaStore.reset_hood_nodes()
        self.dnaStore.reset_place_nodes()
        self.dnaStore.reset_hood()
        self.dnaStore.reset_fonts()
        self.dnaStore.reset_DNA_vis_groups()
        self.dnaStore.reset_textures()
        self.dnaStore.reset_block_numbers()
        self.dnaStore.reset_block_zones()
        self.dnaStore.reset_suit_points()
        self.dnaStore = None
        self.skyUtil = None
        base.localAvatar.inTutorial = False
        DistributedObject.disable(self)
        return
예제 #9
0
class HangUpAttack(Attack):
    notify = directNotify.newCategory('HangUpAttack')
    attack = 'hangup'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.phone = None
        self.receiver = None
        self.collNP = None
        self.phoneSfx = None
        self.hangupSfx = None
        self.shootIval = None
        self.cord = None
        self.receiverOutCord = None
        self.phoneOutCord = None
        return

    def loadAttack(self):
        self.phone = loader.loadModel('phase_3.5/models/props/phone.bam')
        self.phone.setHpr(0, 0, 180)
        if self.suit.type == 'B':
            self.phone.setPos(0.7, 0.15, 0)
        elif self.suit.type == 'C':
            self.phone.setPos(0.25, 0, 0)
        self.receiver = loader.loadModel('phase_3.5/models/props/receiver.bam')
        self.receiver.reparentTo(self.phone)
        self.cord = Rope()
        self.cord.ropeNode.setUseVertexColor(1)
        self.cord.ropeNode.setUseVertexThickness(1)
        self.cord.setup(3, ({'node': self.phone,
          'point': (0.8, 0, 0.2),
          'color': (0, 0, 0, 1),
          'thickness': 1000}, {'node': self.phone,
          'point': (2, 0, 0),
          'color': (0, 0, 0, 1),
          'thickness': 1000}, {'node': self.receiver,
          'point': (1.1, 0.25, 0.5),
          'color': (0, 0, 0, 1),
          'thickness': 1000}), [])
        self.cord.setH(180)
        self.phoneSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup.mp3')
        base.audio3d.attachSoundToObject(self.phoneSfx, self.phone)
        self.hangupSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup_place_down.mp3')
        base.audio3d.attachSoundToObject(self.hangupSfx, self.phone)
        collSphere = CollisionSphere(0, 0, 0, 2)
        collSphere.setTangible(0)
        collNode = CollisionNode('phone_shootout')
        collNode.addSolid(collSphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        self.collNP = self.phone.attachNewNode(collNode)

    def doAttack(self, ts = 0):
        self.loadAttack()
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doHangupAttack')
        else:
            name = 'doHangupAttack'
        if self.suit.type == 'A':
            delay2playSound = 1.0
            delayAfterSoundToPlaceDownReceiver = 0.2
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.0
            receiverInHandPos = Point3(-0.5, 0.5, -1)
        elif self.suit.type == 'B':
            delay2playSound = 1.5
            delayAfterSoundToPlaceDownReceiver = 0.7
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.5
            receiverInHandPos = Point3(-0.3, 0.5, -0.8)
        elif self.suit.type == 'C':
            delay2playSound = 1.0
            delayAfterSoundToPlaceDownReceiver = 1.15
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.5
            receiverInHandPos = Point3(-0.3, 0.5, -0.8)
        self.suitTrack = Parallel(name=name)
        self.suitTrack.append(ActorInterval(self.suit, 'phone'))
        self.suitTrack.append(Sequence(Wait(delay2playSound), SoundInterval(self.phoneSfx, duration=2.1), Wait(delayAfterSoundToPlaceDownReceiver), Func(self.receiver.setPos, 0, 0, 0), Func(self.receiver.setH, 0.0), Func(self.receiver.reparentTo, self.phone), Func(self.acceptOnce, 'enter' + self.collNP.node().getName(), self.handleCollision), Func(self.shootOut), Parallel(SoundInterval(self.hangupSfx), Sequence(Wait(delayAfterShootToIgnoreCollisions), Func(self.ignore, 'enter' + self.collNP.node().getName())))))
        self.suitTrack.append(Sequence(Func(self.phone.reparentTo, self.suit.find('**/joint_Lhold')), Func(self.cord.reparentTo, render), Wait(delay2PickUpReceiver), Func(self.receiver.reparentTo, self.suit.find('**/joint_Rhold')), Func(self.receiver.setPos, receiverInHandPos), Func(self.receiver.setH, 270.0)))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def handleCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def shootOut(self):
        pathNode = NodePath('path')
        pathNode.reparentTo(self.suit)
        pathNode.setPos(0, 50, self.phone.getZ(self.suit))
        self.collNP.reparentTo(render)
        self.shootIval = LerpPosInterval(self.collNP, duration=1.0, pos=pathNode.getPos(render), startPos=self.phone.getPos(render))
        self.shootIval.start()
        pathNode.removeNode()
        del pathNode

    def cleanup(self):
        Attack.cleanup(self)
        if self.shootIval:
            self.shootIval.pause()
            self.shootIval = None
        if self.cord:
            self.cord.removeNode()
            self.cord = None
        if self.phone:
            self.phone.removeNode()
            self.phone = None
        if self.receiver:
            self.receiver.removeNode()
            self.receiver = None
        if self.collNP:
            self.collNP.node().clearSolids()
            self.collNP.removeNode()
            self.collNP = None
        if self.phoneSfx:
            self.phoneSfx.stop()
            self.phoneSfx = None
        return
class DistributedDodgeballGame(DistributedToonFPSGame, TeamMinigame):
    """The winter dodgeball minigame (client side)"""

    notify = directNotify.newCategory("DistributedDodgeballGame")

    TreeData = [['prop_snow_tree_small_ur', Point3(23.23, 66.52, 7.46)],
	            ['prop_snow_tree_small_ul', Point3(-34.03, 88.02, 24.17)],
	            ['prop_snow_tree_small_ur', Point3(-54.80, 0, 4.19)],
	            ['prop_snow_tree_small_ul', Point3(54.80, -5, 4.19)],
	            ['prop_snow_tree_small_ur', Point3(62.71, 62.66, 16.80)],
	            ['prop_snow_tree_small_ul', Point3(-23.23, -66.52, 6)],
	            ['prop_snow_tree_small_ur', Point3(34.03, -88.02, 23)],
	            ['prop_snow_tree_small_ul', Point3(-62.71, -62.66, 16)]]

    SnowballData = [Point3(30, 0, 0.75),
                    Point3(22.5, 0, 0.75),
                    Point3(15, 0, 0.75),
                    Point3(7.5, 0, 0.75),
                    Point3(0, 0, 0.75),
                    Point3(-7.5, 0, 0.75),
                    Point3(-15, 0, 0.75),
                    Point3(-22.5, 0, 0.75),
                    Point3(-30, 0, 0.75)]

    GameSong = "phase_4/audio/bgm/MG_Dodgeball.ogg"
    GameDesc = ("Welcome to the north! You have been invited to play dodgeball with the penguins!\n\n"
                "How To Play\nWASD to Move and use the mouse to aim.\nLeft click to Throw!\nRight click"
                " to Catch!\n\nObjective\nThe first team to get everyone out wins!")

    InitCamTrans = [Point3(25, 45, 19.5317), Vec3(154.001, -15, 0)]

    SnowBallDmg = 25

    GetSnowBalls = "Pick up a snowball from the center!"

    def __init__(self, cr):
        try:
            self.DistributedDodgeballGame_initialized
            return
        except:
            self.DistributedDodgeballGame_initialized = 1

        DistributedToonFPSGame.__init__(self, cr)

        TeamMinigame.__init__(self, "BlueSnow", ('phase_4/maps/db_blue_neutral.png',
                                               'phase_4/maps/db_blue_hover.png',
                                               'phase_4/maps/db_blue_hover.png'),
                                    "RedIce", ('phase_4/maps/db_red_neutral.png',
                                               'phase_4/maps/db_red_hover.png',
                                               'phase_4/maps/db_red_hover.png'))

        self.fsm.addState(State('chooseTeam', self.enterChooseTeam, self.exitChooseTeam, ['waitForOthers']))
        self.fsm.addState(State('scrollBy', self.enterScrollBy, self.exitScrollBy, ['countdown']))
        self.fsm.addState(State('countdown', self.enterCountdown, self.exitCountdown, ['play']))
        self.fsm.getStateNamed('waitForOthers').addTransition('chooseTeam')
        self.fsm.getStateNamed('waitForOthers').addTransition('scrollBy')

        self.firstPerson = DodgeballFirstPerson(self)

        self.scrollBySeq = None
        self.infoText = None

        self.infoText = getAlertText()

        self.spawnPointsByTeam = {
            BLUE: [
                [Point3(5, 15, 0), Vec3(180, 0, 0)],
                [Point3(15, 15, 0), Vec3(180, 0, 0)],
                [Point3(-5, 15, 0), Vec3(180, 0, 0)],
                [Point3(-15, 15, 0), Vec3(180, 0, 0)]],
            RED: [
                [Point3(5, -15, 0), Vec3(0, 0, 0)],
                [Point3(15, -15, 0), Vec3(0, 0, 0)],
                [Point3(-5, -15, 0), Vec3(0, 0, 0)],
                [Point3(-15, -15, 0), Vec3(0, 0, 0)]]}

        # Environment vars
        self.sky = None
        self.arena = None
        self.fog = None
        self.snow = None
        self.snowRender = None
        self.trees = []
        self.snowballs = []

    def snowballHitWall(self, snowballIndex):
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitWallOrPlayer()

    def snowballHitPlayer(self, damagedPlayer, snowballIndex):
        av = self.getRemoteAvatar(damagedPlayer)
        if av:
            print "setting health"
            av.setHealth(av.health - DistributedDodgeballGame.SnowBallDmg)
        if damagedPlayer == base.localAvatar.doId:
            self.showAlert("You were hit by a snowball!")
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitWallOrPlayer()

    def playerCaughtSnowball(self, snowballIndex, catcherId):
        av = self.getRemoteAvatar(catcherId)
        if av:
            snowball = self.snowballs[snowballIndex]
            snowball.pauseThrowIval()
            snowball.pickup(av)

    def setupRemoteAvatar(self, avId):
        av = RemoteDodgeballAvatar(self, self.cr, avId)
        if avId == self.cr.localAvId:
            self.myRemoteAvatar = av
        print "setup remove avatar {0}".format(avId)
        self.remoteAvatars.append(av)

    def __getSnowTree(self, path):
        trees = loader.loadModel('phase_8/models/props/snow_trees.bam')
        tree = trees.find('**/' + path)
        tree.find('**/*shadow*').removeNode()
        return tree

    def load(self):
        self.setMinigameMusic(DistributedDodgeballGame.GameSong)
        self.setDescription(DistributedDodgeballGame.GameDesc)
        self.createWorld()

        trans = DistributedDodgeballGame.InitCamTrans
        camera.setPos(trans[0])
        camera.setHpr(trans[1])

        DistributedToonFPSGame.load(self)

    def createWorld(self):
        self.deleteWorld()

        self.sky = loader.loadModel("phase_3.5/models/props/BR_sky.bam")
        self.sky.reparentTo(render)
        self.sky.setZ(-40)
        self.sky.setFogOff()

        self.arena = loader.loadModel("phase_4/models/minigames/dodgeball_arena.egg")
        self.arena.reparentTo(render)
        self.arena.setScale(0.75)
        self.arena.find('**/team_divider').setBin('ground', 18)
        self.arena.find('**/floor').setBin('ground', 18)
        self.arena.find('**/team_divider_coll').setCollideMask(CIGlobals.FloorBitmask)

        for data in DistributedDodgeballGame.TreeData:
            code = data[0]
            pos = data[1]
            tree = self.__getSnowTree(code)
            tree.reparentTo(self.arena)
            tree.setPos(pos)
            self.trees.append(tree)

        for i in xrange(len(DistributedDodgeballGame.SnowballData)):
            snowdata = DistributedDodgeballGame.SnowballData[i]
            snowball = Snowball(self, i)
            snowball.load()
            snowball.reparentTo(render)
            snowball.setPos(snowdata)
            self.snowballs.append(snowball)

        self.snow = ParticleLoader.loadParticleEffect('phase_8/etc/snowdisk.ptf')
        self.snow.setPos(0, 0, 5)
        self.snowRender = self.arena.attachNewNode('snowRender')
        self.snowRender.setDepthWrite(0)
        self.snowRender.setBin('fixed', 1)
        self.snow.start(camera, self.snowRender)

        self.fog = Fog('snowFog')
        self.fog.setColor(0.486, 0.784, 1)
        self.fog.setExpDensity(0.003)
        render.setFog(self.fog)

    def throw(self, snowballIndex, p):
        snowball = self.snowballs[snowballIndex]
        snowball.throw(p)

    def snowballPickup(self, snowballIndex, pickerUpperAvId):
        remoteAv = self.getRemoteAvatar(pickerUpperAvId)
        if remoteAv:
            snowball = self.snowballs[snowballIndex]
            snowball.pickup(remoteAv)

    def deleteWorld(self):
        for snowball in self.snowballs:
            snowball.removeNode()
        self.snowballs = []
        for tree in self.trees:
            tree.removeNode()
        self.trees = []
        if self.snow:
            self.snow.cleanup()
            self.snow = None
        if self.snowRender:
            self.snowRender.removeNode()
            self.snowRender = None
        self.fog = None
        if self.sky:
            self.sky.removeNode()
            self.sky = None
        if self.arena:
            self.arena.removeNode()
            self.arena = None
        render.clearFog()

    def enterPlay(self):
        self.firstPerson.reallyStart()

    def exitPlay(self):
        self.firstPerson.end()

    def enterCountdown(self):
        self.firstPerson.start()
        self.firstPerson.disableMouse()

        self.infoText.setText(DistributedDodgeballGame.GetSnowBalls)

        self.countdownText = getGameText()
        self.countdownIval = Parallel(
            Sequence(
                Func(self.countdownText.setText, "5"),
                getCountdownIval(self.countdownText),
                Func(self.countdownText.setText, "4"),
                getCountdownIval(self.countdownText),
                Func(self.countdownText.setText, "3"),
                getCountdownIval(self.countdownText),
                Func(self.countdownText.setText, "2"),
                getCountdownIval(self.countdownText),
                Func(self.countdownText.setText, "1"),
                getCountdownIval(self.countdownText)),
            getAlertPulse(self.infoText),
            name = "COUNTDOWNIVAL")
        self.countdownIval.setDoneEvent(self.countdownIval.getName())
        self.acceptOnce(self.countdownIval.getDoneEvent(), self.__handleCountdownDone)
        self.countdownIval.start()

    def __handleCountdownDone(self):
        self.fsm.request('play')

    def exitCountdown(self):
        if hasattr(self, 'countdownText'):
            self.countdownText.destroy()
            del self.countdownText
        if hasattr(self, 'countdownIval'):
            self.ignore(self.countdownIval.getDoneEvent())
            self.countdownIval.finish()
            del self.countdownIval

    def enterScrollBy(self):
        BLUE_START_POS = Point3(-20, 0, 4)
        BLUE_END_POS = Point3(20, 0, 4)
        BLUE_HPR = Vec3(0, 0, 0)

        RED_START_POS = Point3(20, 0, 4)
        RED_END_POS = Point3(-20, 0, 4)
        RED_HPR = Vec3(180, 0, 0)

        self.playMinigameMusic()

        self.scrollBySeq = Sequence(
            Func(camera.setHpr, BLUE_HPR),
            LerpPosInterval(
                camera, duration = 5.0, pos = BLUE_END_POS, startPos = BLUE_START_POS, blendType = 'easeOut'),
            Func(base.transitions.fadeOut, 0.4),
            Wait(0.5),
            Func(base.transitions.fadeIn, 0.4),
            Func(camera.setHpr, RED_HPR),
            LerpPosInterval(
                camera, duration = 5.0, pos = RED_END_POS, startPos = RED_START_POS, blendType = 'easeOut'),
            name = "SCROLLBYSEQ")
        self.scrollBySeq.setDoneEvent(self.scrollBySeq.getName())
        self.acceptOnce(self.scrollBySeq.getDoneEvent(), self.__handleScrollByDone)
        self.scrollBySeq.start()

    def __handleScrollByDone(self):
        self.fsm.request('countdown')

    def exitScrollBy(self):
        if self.scrollBySeq:
            self.ignore(self.scrollBySeq.getDoneEvent())
            self.scrollBySeq.finish()
            self.scrollBySeq = None

    def allPlayersReady(self):
        self.fsm.request('scrollBy')

    def chooseUrTeam(self):
        # The AI has told us it's time to choose our team.
        self.fsm.request('chooseTeam')

    def enterChooseTeam(self):
        self.makeSelectionGUI()

    def acceptedIntoTeam(self, spawnPoint):
        TeamMinigame.acceptedIntoTeam(self)

        self.sendUpdate('readyToStart')
        self.fsm.request('waitForOthers')

        pos, hpr = self.spawnPointsByTeam[self.team][spawnPoint]
        base.localAvatar.setPos(pos)
        base.localAvatar.setHpr(hpr)

    def exitChooseTeam(self):
        self.destroySelectionGUI()

    def announceGenerate(self):
        DistributedToonFPSGame.announceGenerate(self)
        base.camLens.setMinFov(CIGlobals.GunGameFOV / (4./3.))
        self.load()

    def disable(self):
        self.deleteWorld()
        self.trees = None
        self.snowballs = None
        self.spawnPointsByTeam = None
        if self.firstPerson:
            self.firstPerson.cleanup()
            self.firstPerson = None
        DistributedToonFPSGame.disable(self)
예제 #11
0
class ThrowAttack(Attack):
    notify = directNotify.newCategory('ThrowAttack')
    attack = 'throw'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.weapon_state = None
        self.weapon = None
        self.wss = None
        self.wsnp = None
        self.suitTrack = None
        self.weaponSfx = None
        self.throwTrajectory = None
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        self.startNP = None
        self.theActorIval = None
        return

    def handleWeaponCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)
            self.suit.b_handleWeaponTouch()

    def doAttack(self, weapon_path, weapon_scale, track_name, animation_name, collsphere_radius, weapon_coll_id, weapon_h = 0, weapon_p = 0, weapon_r = 0, weapon_x = 0, weapon_y = 0, weapon_z = 0, ts = 0):
        self.weapon_state = 'start'
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            weapon_coll_id = self.suit.uniqueName(weapon_coll_id)
        self.weapon = loader.loadModel(weapon_path)
        self.weapon.setScale(weapon_scale)
        self.weapon.setHpr(weapon_h, weapon_p, weapon_r)
        self.weapon.setPos(weapon_x, weapon_y, weapon_z)
        self.wss = CollisionSphere(0, 0, 0, collsphere_radius)
        self.wss.setTangible(0)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        if self.attack not in ('glowerpower',):
            actorIval = ActorInterval(self.suit, animation_name, playRate=2.0, duration=1.2)
            actorIval2 = ActorInterval(self.suit, animation_name, startTime=2.4)
        else:
            actorIval = ActorInterval(self.suit, animation_name)
        seq = Sequence()
        if self.attack not in ('glowerpower',):
            self.suitTrack = Parallel(Sequence(actorIval, actorIval2), name=track_name)
            self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
            seq.append(Wait(1.2))
            if self.suit.suitPlan.getSuitType() == 'C':
                seq.append(Wait(0))
            else:
                seq.append(Wait(0.7))
            seq.append(Func(self.throwObject))
            seq.append(Wait(1.0))
            seq.append(Func(self.delWeapon))
        else:
            self.suitTrack = Parallel(actorIval, name=track_name)
            seq.append(Wait(1))
            seq.append(Func(self.throwObject))
            seq.append(Wait(0.5))
            seq.append(Func(self.delWeapon))
        self.suitTrack.append(seq)
        wsnode = CollisionNode(weapon_coll_id)
        wsnode.addSolid(self.wss)
        wsnode.setCollideMask(CIGlobals.WallBitmask)
        self.wsnp = self.weapon.attachNewNode(wsnode)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, track_name)
        self.suitTrack.start(ts)

    def playWeaponSound(self):
        if self.weapon and self.weaponSfx:
            base.audio3d.attachSoundToObject(self.weaponSfx, self.suit)
            self.weaponSfx.play()

    def throwObject(self, projectile = True):
        if not self.weapon:
            return
        self.acceptOnce('enter' + self.wsnp.node().getName(), self.handleWeaponCollision)
        self.playWeaponSound()
        if self.weapon:
            self.weapon.wrtReparentTo(render)
            self.weapon.setHpr(Vec3(0, 0, 0))
        if self.attack not in ('glowerpower',):
            parent = self.suit.find('**/joint_Rhold')
        else:
            parent = self.suit.find('**/joint_head')
        startNP = parent.attachNewNode('startNp')
        startNP.lookAt(render, self.targetX, self.targetY, self.targetZ)
        pathNP = NodePath('throwPath')
        pathNP.reparentTo(startNP)
        pathNP.setScale(render, 1.0)
        pathNP.setPos(0, 50, 0)
        if self.attack in ('clipontie', 'powertie', 'halfwindsor'):
            self.weapon.setHpr(pathNP.getHpr(render))
        if projectile == True:
            self.throwTrajectory = ProjectileInterval(self.weapon, startPos=self.suit.find('**/joint_Rhold').getPos(render), endPos=pathNP.getPos(render), gravityMult=0.7, duration=1.0)
        else:
            self.weapon.setH(pathNP.getH(render))
            self.throwTrajectory = LerpPosInterval(self.weapon, duration=0.5, pos=pathNP.getPos(render), startPos=startNP.getPos(render) + (0, 3, 0))
        self.throwTrajectory.start()
        self.weapon_state = 'released'
        startNP.removeNode()
        del startNP
        pathNP.removeNode()
        del pathNP

    def interruptAttack(self):
        if self.throwTrajectory:
            if self.throwTrajectory.isStopped():
                self.delWeapon()

    def handleWeaponTouch(self):
        if self.throwTrajectory:
            self.throwTrajectory.pause()
            self.throwTrajectory = None
        self.delWeapon()
        return

    def delWeapon(self):
        if self.weapon:
            self.weapon.removeNode()
            self.weapon = None
        return

    def cleanup(self):
        Attack.cleanup(self)
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        self.weapon_state = None
        if self.weaponSfx:
            self.weaponSfx.stop()
            self.weaponSfx = None
        if self.throwTrajectory:
            self.throwTrajectory.pause()
            self.throwTrajectory = None
        self.delWeapon()
        self.wss = None
        if self.wsnp:
            self.wsnp.node().clearSolids()
            self.wsnp.removeNode()
            self.wsnp = None
        return
class HangUpAttack(Attack):
    notify = directNotify.newCategory('HangUpAttack')
    attack = 'hangup'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.phone = None
        self.receiver = None
        self.collNP = None
        self.phoneSfx = None
        self.hangupSfx = None
        self.shootIval = None
        self.cord = None
        self.receiverOutCord = None
        self.phoneOutCord = None
        return

    def loadAttack(self):
        self.phone = loader.loadModel('phase_3.5/models/props/phone.bam')
        self.phone.setHpr(0, 0, 180)
        if self.suit.type == 'B':
            self.phone.setPos(0.7, 0.15, 0)
        else:
            if self.suit.type == 'C':
                self.phone.setPos(0.25, 0, 0)
        self.receiver = loader.loadModel('phase_3.5/models/props/receiver.bam')
        self.receiver.reparentTo(self.phone)
        self.cord = Rope()
        self.cord.ropeNode.setUseVertexColor(1)
        self.cord.ropeNode.setUseVertexThickness(1)
        self.cord.setup(3, ({'node': self.phone, 'point': (0.8, 0, 0.2), 'color': (0, 0, 0, 1), 'thickness': 1000}, {'node': self.phone, 'point': (2, 0, 0), 'color': (0, 0, 0, 1), 'thickness': 1000}, {'node': self.receiver, 'point': (1.1, 0.25, 0.5), 'color': (0, 0, 0, 1), 'thickness': 1000}), [])
        self.cord.setH(180)
        self.phoneSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup.ogg')
        base.audio3d.attachSoundToObject(self.phoneSfx, self.phone)
        self.hangupSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup_place_down.ogg')
        base.audio3d.attachSoundToObject(self.hangupSfx, self.phone)
        collSphere = CollisionSphere(0, 0, 0, 2)
        collSphere.setTangible(0)
        collNode = CollisionNode('phone_shootout')
        collNode.addSolid(collSphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        self.collNP = self.phone.attachNewNode(collNode)

    def doAttack(self, ts=0):
        self.loadAttack()
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doHangupAttack')
        else:
            name = 'doHangupAttack'
        if self.suit.type == 'A':
            delay2playSound = 1.0
            delayAfterSoundToPlaceDownReceiver = 0.2
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.0
            receiverInHandPos = Point3(-0.5, 0.5, -1)
        else:
            if self.suit.type == 'B':
                delay2playSound = 1.5
                delayAfterSoundToPlaceDownReceiver = 0.7
                delayAfterShootToIgnoreCollisions = 1.0
                delay2PickUpReceiver = 1.5
                receiverInHandPos = Point3(-0.3, 0.5, -0.8)
            else:
                if self.suit.type == 'C':
                    delay2playSound = 1.0
                    delayAfterSoundToPlaceDownReceiver = 1.15
                    delayAfterShootToIgnoreCollisions = 1.0
                    delay2PickUpReceiver = 1.5
                    receiverInHandPos = Point3(-0.3, 0.5, -0.8)
        self.suitTrack = Parallel(name=name)
        self.suitTrack.append(ActorInterval(self.suit, 'phone'))
        self.suitTrack.append(Sequence(Wait(delay2playSound), SoundInterval(self.phoneSfx, duration=2.1), Wait(delayAfterSoundToPlaceDownReceiver), Func(self.receiver.setPos, 0, 0, 0), Func(self.receiver.setH, 0.0), Func(self.receiver.reparentTo, self.phone), Func(self.acceptOnce, 'enter' + self.collNP.node().getName(), self.handleCollision), Func(self.shootOut), Parallel(SoundInterval(self.hangupSfx), Sequence(Wait(delayAfterShootToIgnoreCollisions), Func(self.ignore, 'enter' + self.collNP.node().getName())))))
        self.suitTrack.append(Sequence(Func(self.phone.reparentTo, self.suit.find('**/joint_Lhold')), Func(self.cord.reparentTo, render), Wait(delay2PickUpReceiver), Func(self.receiver.reparentTo, self.suit.find('**/joint_Rhold')), Func(self.receiver.setPos, receiverInHandPos), Func(self.receiver.setH, 270.0)))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def handleCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def shootOut(self):
        pathNode = NodePath('path')
        pathNode.reparentTo(self.suit)
        pathNode.setPos(0, 50, self.phone.getZ(self.suit))
        self.collNP.reparentTo(render)
        self.shootIval = LerpPosInterval(self.collNP, duration=1.0, pos=pathNode.getPos(render), startPos=self.phone.getPos(render))
        self.shootIval.start()
        pathNode.removeNode()
        del pathNode

    def cleanup(self):
        Attack.cleanup(self)
        if self.shootIval:
            self.shootIval.pause()
            self.shootIval = None
        if self.cord:
            self.cord.removeNode()
            self.cord = None
        if self.phone:
            self.phone.removeNode()
            self.phone = None
        if self.receiver:
            self.receiver.removeNode()
            self.receiver = None
        if self.collNP:
            self.collNP.node().clearSolids()
            self.collNP.removeNode()
            self.collNP = None
        if self.phoneSfx:
            self.phoneSfx.stop()
            self.phoneSfx = None
        return
class FountainPenAttack(Attack):
    notify = directNotify.newCategory('FountainPenAttack')
    attack = 'fountainpen'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.pen = None
        self.spray = None
        self.splat = None
        self.spraySfx = None
        self.sprayParticle = None
        self.sprayScaleIval = None
        self.wsnp = None
        return

    def loadAttack(self):
        self.pen = loader.loadModel('phase_5/models/props/pen.bam')
        self.pen.reparentTo(self.suit.find('**/joint_Rhold'))
        self.sprayParticle = ParticleLoader.loadParticleEffect('phase_5/etc/penSpill.ptf')
        self.spray = loader.loadModel('phase_3.5/models/props/spray.bam')
        self.spray.setColor(VBase4(0, 0, 0, 1))
        self.splat = Actor('phase_3.5/models/props/splat-mod.bam', {'chan': 'phase_3.5/models/props/splat-chan.bam'})
        self.splat.setColor(VBase4(0, 0, 0, 1))
        self.sprayScaleIval = LerpScaleInterval(self.spray, duration=0.3, scale=(1,
                                                                                 20,
                                                                                 1), startScale=(1,
                                                                                                 1,
                                                                                                 1))
        sphere = CollisionSphere(0, 0, 0, 0.5)
        sphere.setTangible(0)
        if hasattr(self.suit, 'uniqueName'):
            collName = self.suit.uniqueName('fountainPenCollNode')
        else:
            collName = 'fountainPenCollNode'
        collNode = CollisionNode(collName)
        collNode.addSolid(sphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        self.wsnp = self.spray.attachNewNode(collNode)
        self.wsnp.setY(1)

    def doAttack(self, ts=0):
        self.loadAttack()
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doFountainPenAttack')
        else:
            name = 'doFountainPenAttack'
        self.suitTrack = Parallel(name=name)
        self.suitTrack.append(ActorInterval(self.suit, 'fountainpen'))
        self.suitTrack.append(Sequence(Wait(1.2), Func(self.acceptOnce, 'enter' + self.wsnp.node().getName(), self.handleSprayCollision), Func(self.playWeaponSound), Func(self.attachSpray), Func(self.sprayParticle.start, self.pen.find('**/joint_toSpray'), self.pen.find('**/joint_toSpray')), self.sprayScaleIval, Wait(0.5), Func(self.sprayParticle.cleanup), Func(self.spray.setScale, 1), Func(self.spray.reparentTo, hidden), Func(self.ignore, 'enter' + self.wsnp.node().getName())))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def attachSpray(self):
        self.spray.reparentTo(self.pen.find('**/joint_toSpray'))
        pos = self.spray.getPos(render)
        hpr = self.spray.getHpr(render)
        self.spray.reparentTo(render)
        self.spray.setPos(pos)
        self.spray.setHpr(hpr)
        self.spray.setP(0)
        if self.suit.type == 'C':
            self.spray.setH(self.spray.getH() + 7.5)
        self.spray.setTwoSided(True)

    def handleSprayCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)
        self.sprayScaleIval.pause()

    def playWeaponSound(self):
        self.spraySfx = base.audio3d.loadSfx('phase_5/audio/sfx/SA_fountain_pen.ogg')
        base.audio3d.attachSoundToObject(self.spraySfx, self.pen)
        self.spraySfx.play()

    def cleanup(self):
        Attack.cleanup(self)
        if self.wsnp:
            self.wsnp.node().clearSolids()
            self.wsnp.removeNode()
            self.wsnp = None
        if self.pen:
            self.pen.removeNode()
            self.pen = None
        if self.sprayParticle:
            self.sprayParticle.cleanup()
            self.sprayParticle = None
        if self.spray:
            self.spray.removeNode()
            self.spray = None
        if self.splat:
            self.splat.cleanup()
            self.splat = None
        if self.sprayScaleIval:
            self.sprayScaleIval.pause()
            self.sprayScaleIval = None
        self.spraySfx = None
        return
예제 #14
0
class DistributedToon(Toon.Toon, DistributedAvatar, DistributedSmoothNode,
                      DelayDeletable):
    def __init__(self, cr):
        try:
            self.DistributedToon_initialized
            return
        except:
            self.DistributedToon_initialized = 1

        Toon.Toon.__init__(self, cr)
        DistributedAvatar.__init__(self, cr)
        DistributedSmoothNode.__init__(self, cr)
        self.questManager = QuestManager.QuestManager()
        self.token = -1
        self.ghost = 0
        self.puInventory = []
        self.equippedPU = -1
        self.backpack = None
        self.animState2animId = {}
        self.battleMeter = None
        for index in range(len(self.animFSM.getStates())):
            self.animState2animId[self.animFSM.getStates()
                                  [index].getName()] = index

        self.animId2animState = {
            v: k
            for k, v in self.animState2animId.items()
        }
        self.headMeter = None
        self.firstTimeChangingHP = True
        self.quests = []
        self.tier = None
        self.questHistory = None
        self.busy = 1
        self.friends = None
        self.tutDone = 0
        self.hoodsDiscovered = []
        self.teleportAccess = []
        self.lastHood = 0
        self.defaultShard = 0
        self.dmgFadeIval = None
        self.tunnelTrack = None
        return

    def goThroughTunnel(self, toZone, inOrOut, requestStatus=None):
        if self.tunnelTrack:
            self.ignore(self.tunnelTrack.getDoneEvent())
            self.tunnelTrack.finish()
            self.tunnelTrack = None
        linkTunnel = LinkTunnel.getTunnelThatGoesToZone(toZone)
        if not linkTunnel:
            return
        self.tunnelTrack = Parallel(
            name=self.uniqueName('Place.goThroughTunnel'))
        if inOrOut == 0:
            pivotPoint = linkTunnel.inPivotPoint
            pivotPointNode = linkTunnel.tunnel.attachNewNode(
                'tunnelPivotPoint')
            pivotPointNode.setPos(pivotPoint)
            self.stopSmooth()
            self.wrtReparentTo(pivotPointNode)
            if linkTunnel.__class__.__name__ == 'SafeZoneLinkTunnel':
                self.setHpr(180, 0, 0)
            else:
                self.setHpr(0, 0, 0)
            if base.localAvatar.doId == self.doId:
                doneMethod = self._handleWentInTunnel
                extraArgs = [requestStatus]
                self.walkControls.setCollisionsActive(0)
                camera.wrtReparentTo(linkTunnel.tunnel)
                currCamPos = camera.getPos()
                currCamHpr = camera.getHpr()
                tunnelCamPos = linkTunnel.camPos
                tunnelCamHpr = linkTunnel.camHpr
                self.tunnelTrack.append(
                    LerpPosInterval(camera,
                                    duration=0.7,
                                    pos=tunnelCamPos,
                                    startPos=currCamPos,
                                    blendType='easeOut'))
                self.tunnelTrack.append(
                    LerpQuatInterval(camera,
                                     duration=0.7,
                                     quat=tunnelCamHpr,
                                     startHpr=currCamHpr,
                                     blendType='easeOut'))
            exitSeq = Sequence(Func(self.loop, 'run'))
            if base.localAvatar.doId == self.doId:
                exitSeq.append(Wait(2.0))
                exitSeq.append(Func(base.transitions.irisOut))
            self.tunnelTrack.append(exitSeq)
            self.tunnelTrack.append(
                Sequence(
                    LerpHprInterval(pivotPointNode,
                                    duration=2.0,
                                    hpr=linkTunnel.inPivotEndHpr,
                                    startHpr=linkTunnel.inPivotStartHpr),
                    LerpPosInterval(pivotPointNode,
                                    duration=1.0,
                                    pos=(linkTunnel.inPivotEndX,
                                         pivotPointNode.getY(),
                                         pivotPointNode.getZ()),
                                    startPos=(linkTunnel.inPivotStartX,
                                              pivotPointNode.getY(),
                                              pivotPointNode.getZ()))))
        else:
            if inOrOut == 1:
                pivotPoint = linkTunnel.outPivotPoint
                pivotPointNode = linkTunnel.tunnel.attachNewNode(
                    'tunnelPivotPoint')
                pivotPointNode.setPos(pivotPoint)
                pivotPointNode.setHpr(linkTunnel.outPivotStartHpr)
                if base.localAvatar.doId == self.doId:
                    base.localAvatar.walkControls.setCollisionsActive(0)
                    base.localAvatar.detachCamera()
                    camera.reparentTo(linkTunnel.tunnel)
                    tunnelCamPos = linkTunnel.camPos
                    tunnelCamHpr = linkTunnel.camHpr
                    camera.setPos(tunnelCamPos)
                    camera.setHpr(tunnelCamHpr)
                    doneMethod = self._handleCameOutTunnel
                    extraArgs = []
                self.reparentTo(pivotPointNode)
                self.setHpr(linkTunnel.toonOutHpr)
                self.setPos(linkTunnel.toonOutPos)
                exitSeq = Sequence(
                    Func(self.loop, 'run'),
                    LerpPosInterval(pivotPointNode,
                                    duration=1.0,
                                    pos=(linkTunnel.outPivotEndX,
                                         pivotPointNode.getY(),
                                         pivotPointNode.getZ()),
                                    startPos=(linkTunnel.outPivotStartX,
                                              pivotPointNode.getY(),
                                              pivotPointNode.getZ())),
                    LerpHprInterval(pivotPointNode,
                                    duration=2.0,
                                    hpr=linkTunnel.outPivotEndHpr,
                                    startHpr=linkTunnel.outPivotStartHpr),
                    Func(self.wrtReparentTo, render), Func(self.startSmooth))
                self.tunnelTrack.append(exitSeq)
        if base.localAvatar.doId == self.doId:
            self.tunnelTrack.setDoneEvent(self.tunnelTrack.getName())
            self.acceptOnce(self.tunnelTrack.getDoneEvent(), doneMethod,
                            extraArgs)
        self.tunnelTrack.start()
        return

    def setupNameTag(self, tempName=None):
        Toon.Toon.setupNameTag(self, tempName)
        self.nametag.getNametag3d().setClickEvent('toonClicked', [self.doId])
        self.nametag.getNametag2d().setClickEvent('toonClicked', [self.doId])

    def setDefaultShard(self, shardId):
        self.defaultShard = shardId

    def getDefaultShard(self):
        return self.defaultShard

    def doSmoothTask(self, task):
        self.smoother.computeAndApplySmoothPosHpr(self, self)
        if not hasattr(base, 'localAvatar'):
            return task.done
        if self.doId != base.localAvatar.doId:
            self.setSpeed(self.smoother.getSmoothForwardVelocity(),
                          self.smoother.getSmoothRotationalVelocity(),
                          self.smoother.getSmoothLateralVelocity())
        return task.cont

    def setLastHood(self, zoneId):
        self.lastHood = zoneId

    def b_setLastHood(self, zoneId):
        self.sendUpdate('setLastHood', [zoneId])
        self.setLastHood(zoneId)

    def getLastHood(self):
        return self.lastHood

    def setTeleportAccess(self, array):
        self.teleportAccess = array

    def getTeleportAccess(self):
        return self.teleportAccess

    def setHoodsDiscovered(self, array):
        self.hoodsDiscovered = array

    def b_setHoodsDiscovered(self, array):
        self.sendUpdate('setHoodsDiscovered', [array])
        self.setHoodsDiscovered(array)

    def getHoodsDiscovered(self):
        return self.hoodsDiscovered

    def setTutorialCompleted(self, value):
        self.tutDone = value

    def getTutorialCompleted(self):
        return self.tutDone

    def setFriendsList(self, friends):
        self.friends = friends

    def getFriendsList(self):
        return self.friends

    def setBusy(self, busy):
        self.busy = busy

    def getBusy(self):
        return self.busy

    def setTier(self, tier):
        self.tier = tier

    def getTier(self):
        return self.tier

    def setQuestHistory(self, array):
        self.questHistory = array

    def getQuestHistrory(self):
        return self.questHistory

    def setQuests(self, questIds, currentObjectives,
                  currentObjectivesProgress):
        self.quests = [questIds, currentObjectives, currentObjectivesProgress]
        self.questManager.makeQuestsFromData()

    def getQuests(self):
        return self.quests

    def maybeMakeHeadMeter(self):
        if base.localAvatar.doId != self.doId:
            if self.health < self.getMaxHealth():
                if not self.headMeter:
                    self.__makeHeadMeter()

    def __makeHeadMeter(self):
        self.headMeter = LaffOMeter(forRender=True)
        r, g, b, _ = self.getHeadColor()
        animal = self.getAnimal()
        maxHp = self.getMaxHealth()
        hp = self.getHealth()
        self.headMeter.generate(r, g, b, animal, maxHP=maxHp, initialHP=hp)
        self.headMeter.reparentTo(self)
        self.headMeter.setZ(self.getHeight() + 2)
        self.headMeter.setScale(0.4)
        self.headMeter.setBillboardAxis()
        self.__updateHeadMeter()

    def __removeHeadMeter(self):
        if self.headMeter:
            self.headMeter.disable()
            self.headMeter.delete()
            self.headMeter = None
        return

    def __updateHeadMeter(self):
        if self.headMeter:
            self.headMeter.updateMeter(self.getHealth())

    def setHealth(self, health):
        if self.doId != base.localAvatar.doId:
            if not self.firstTimeChangingHP:
                if health < self.getMaxHealth():
                    if not self.headMeter:
                        self.__makeHeadMeter()
                    else:
                        self.__updateHeadMeter()
                else:
                    self.__removeHeadMeter()
        self.health = health
        self.firstTimeChangingHP = False

    def d_createBattleMeter(self):
        self.sendUpdate('makeBattleMeter', [])

    def b_createBattleMeter(self):
        self.makeBattleMeter()
        self.d_createBattleMeter()

    def d_cleanupBattleMeter(self):
        self.sendUpdate('destroyBattleMeter', [])

    def b_cleanupBattleMeter(self):
        self.destroyBattleMeter()
        self.d_cleanupBattleMeter()

    def makeBattleMeter(self):
        if self.getHealth() < self.getMaxHealth():
            if not self.battleMeter:
                self.battleMeter = LaffOMeter()
                r, g, b, _ = self.getHeadColor()
                animal = self.getAnimal()
                maxHp = self.getMaxHealth()
                hp = self.getHealth()
                self.battleMeter.generate(r,
                                          g,
                                          b,
                                          animal,
                                          maxHP=maxHp,
                                          initialHP=hp)
                self.battleMeter.reparentTo(self)
                self.battleMeter.setZ(self.getHeight() + 5)
                self.battleMeter.setScale(0.5)
                self.battleMeter.start()

    def destroyBattleMeter(self):
        if self.battleMeter:
            self.battleMeter.stop()
            self.battleMeter.disable()
            self.battleMeter.delete()
            self.battleMeter = None
        return

    def setEquippedPU(self, index):
        self.equippedPU = index

    def getEquippedPU(self):
        return self.equippedPU

    def setPUInventory(self, array):
        self.puInventory = array

    def getPUInventory(self):
        return self.puInventory

    def setGhost(self, value):
        self.ghost = value
        if value:
            self.ghostOn()
        else:
            self.ghostOff()

    def d_setGhost(self, value):
        self.sendUpdate('setGhost', [value])

    def b_setGhost(self, value):
        self.d_setGhost(value)
        self.setGhost(value)

    def getGhost(self):
        return self.ghost

    def setDNAStrand(self, dnaStrand):
        Toon.Toon.setDNAStrand(self, dnaStrand)
        self.maybeMakeHeadMeter()

    def d_setDNAStrand(self, dnaStrand):
        self.sendUpdate('setDNAStrand', [dnaStrand])

    def b_setDNAStrand(self, dnaStrand):
        self.setDNAStrand(dnaStrand)
        self.d_setDNAStrand(dnaStrand)

    def lookAtObject(self, h, p, r, blink=1):
        if self.getPart('head').getHpr() == (h, p, r):
            return
        Toon.Toon.lerpLookAt(self, self.getPart('head'), tuple((h, p, r)))
        if blink:
            self.stopBlink()
            maxBlinks = random.randint(1, 2)
            numBlinks = 0
            delay = 0
            for blink in range(maxBlinks):
                if numBlinks == 0:
                    taskMgr.add(self.doBlink, self.uniqueName('blinkOnTurn'))
                else:
                    delay += 0.22
                    taskMgr.doMethodLater(delay, self.doBlink,
                                          self.doBlinkTaskName)
                numBlinks += 1

            taskMgr.doMethodLater(delay, self.__startBlinkAfterLook,
                                  self.uniqueName('sBAL'))

    def __startBlinkAfterLook(self, task):
        self.startBlink()
        return task.done

    def toonUp(self):
        pass

    def b_lookAtObject(self, h, p, r, blink=1):
        self.d_lookAtObject(h, p, r, blink)
        self.lookAtObject(h, p, r, blink)

    def d_lookAtObject(self, h, p, r, blink=1):
        self.sendUpdate('lookAtObject', [h, p, r, blink])

    def setChat(self, chat):
        chat = ChatGlobals.filterChat(chat, self.animal)
        Toon.Toon.setChat(self, chat)

    def setTarget(self, gagId, targetId):
        gag = self.backpack.getGagByID(gagId)
        target = self.cr.doId2do.get(targetId, None)
        gag.setTarget(target)
        return

    def trapActivate(self, gagId, avId, entityId, suitId):
        sender = self.cr.doId2do.get(avId, None)
        suit = self.cr.doId2do.get(suitId, None)
        if sender:
            backpack = sender.getBackpack()
            trapGag = backpack.getGagByID(gagId)
            if backpack and trapGag:
                entity = None
                if hasattr(trapGag, 'getEntities') and 0 <= entityId <= len(
                        trapGag.getEntities()) - 1:
                    entity = trapGag.getEntities()[entityId]
                    trapGag.onActivate(entity, suit)
        return

    def b_trapActivate(self, gagId, avId, entityId, suitId):
        self.trapActivate(gagId, avId, entityId, suitId)
        self.d_trapActivate(gagId, avId, entityId, suitId)

    def d_trapActivate(self, gagId, avId, entityId, suitId):
        self.sendUpdate('trapActivate', [gagId, avId, entityId, suitId])

    def gagCollision(self, gagId):
        gag = self.backpack.getGagByID(gagId)
        gag.doCollision()

    def b_gagCollision(self, gagId):
        self.sendUpdate('gagCollision', [gagId])
        self.gagCollision(gagId)

    def gagActivate(self, gagId):
        gag = self.backpack.getGagByID(gagId)
        if hasattr(gag, 'activate'):
            gag.activate()

    def b_gagActivate(self, gagId):
        self.sendUpdate('gagActivate', [gagId])
        self.gagActivate(gagId)

    def setDropLoc(self, gagId, x, y, z):
        gag = self.backpack.getGagByID(gagId)
        gag.setEndPos(x, y, z)

    def setGagPos(self, gagId, x, y, z):
        pos = Point3(x, y, z)
        gag = self.backpack.getGagByID(gagId)
        ent = gag.getGag()
        if ent:
            ent.setPos(pos)

    def setThrowPower(self, gagId, power):
        gag = self.backpack.getGagByID(gagId)
        if gag:
            gag.setPower(power)

    def gagStart(self, gagId):
        gag = self.backpack.getGagByID(gagId)
        if gag:
            gag.start()

    def b_gagStart(self, gagId):
        self.sendUpdate('gagStart', [gagId])
        self.gagStart(gagId)

    def gagThrow(self, gagId):
        gag = self.backpack.getGagByID(gagId)
        if gag:
            gag.throw()

    def b_gagThrow(self, gagId):
        self.sendUpdate('gagThrow', [gagId])
        self.gagThrow(gagId)

    def gagRelease(self, gagId):
        gag = self.backpack.getGagByID(gagId)
        if gag and hasattr(gag, 'name'):
            gag.release()

    def b_gagRelease(self, gagId):
        self.sendUpdate('gagRelease', [gagId])
        self.gagRelease(gagId)

    def setSplatPos(self, gagId, x, y, z):
        splatGag = self.backpack.getGagByID(gagId)
        if splatGag:
            splatGag.setSplatPos(x, y, z)

    def d_setSplatPos(self, gagId, x, y, z):
        self.sendUpdate('setSplatPos', [gagId, x, y, z])

    def gagBuild(self, gagId):
        gag = self.backpack.getGagByID(gagId)
        if gag:
            gag.build()

    def b_gagBuild(self, gagId):
        self.gagBuild(gagId)
        self.sendUpdate('gagBuild', [gagId])

    def handleSuitAttack(self, attack_id, suit_id):
        attack = SuitAttacks.SuitAttackLengths.keys()[attack_id]
        if attack == 'canned':
            sfx = base.audio3d.loadSfx(
                'phase_5/audio/sfx/SA_canned_impact_only.ogg')
            base.audio3d.attachSoundToObject(sfx, self)
            SoundInterval(sfx, node=self).start()
        else:
            if attack == 'playhardball':
                sfx = base.audio3d.loadSfx(
                    'phase_5/audio/sfx/SA_hardball_impact_only_alt.ogg')
                base.audio3d.attachSoundToObject(sfx, self)
                SoundInterval(sfx, node=self).start()
            else:
                if attack == 'clipontie':
                    sfx = base.audio3d.loadSfx(
                        'phase_5/audio/sfx/SA_powertie_impact.ogg')
                    base.audio3d.attachSoundToObject(sfx, self)
                    SoundInterval(sfx, node=self).start()
        if not self.isDead():
            if attack in ('fountainpen', ):
                self.getPart('head').setColorScale(0, 0, 0, 1)
                Sequence(Wait(3.0), Func(self.resetHeadColor)).start()
        if self.dmgFadeIval:
            self.dmgFadeIval.finish()
            self.dmgFadeIval = None
        geomNode = self.getGeomNode()
        self.dmgFadeIval = Sequence(
            Func(geomNode.setTransparency, 1),
            LerpColorScaleInterval(geomNode,
                                   0.3, (1, 1, 1, 0.5), (1, 1, 1, 1),
                                   blendType='easeOut'),
            LerpColorScaleInterval(geomNode,
                                   0.3, (1, 1, 1, 1), (1, 1, 1, 0.5),
                                   blendType='easeIn'),
            Func(geomNode.setTransparency, 0))
        self.dmgFadeIval.start()
        return

    def resetHeadColor(self):
        head = self.getPart('head')
        if head:
            head.setColorScale(1, 1, 1, 1)

    def b_handleSuitAttack(self, attack_id, suit_id):
        self.handleSuitAttack(attack_id, suit_id)
        self.b_lookAtObject(0, 0, 0, blink=1)
        self.sendUpdate('handleSuitAttack', [attack_id, suit_id])

    def equip(self, gagId):
        if self.backpack:
            self.backpack.setCurrentGag(gagId)

    def unEquip(self):
        if self.backpack:
            self.backpack.setCurrentGag(None)
        return

    def b_unEquip(self):
        self.unEquip()
        self.sendUpdate('unEquip', [])

    def b_equip(self, gag_id):
        self.equip(gag_id)
        self.sendUpdate('equip', [gag_id])

    def getBackpack(self):
        return self.backpack

    def setLoadout(self, gagIds):
        if self.backpack:
            loadout = []
            for i in range(len(gagIds)):
                gagId = gagIds[i]
                gag = self.backpack.getGagByID(gagId)
                if gag:
                    loadout.append(gag)

            self.backpack.setLoadout(loadout)

    def setBackpackAmmo(self, gagIds, ammoList):
        if not self.backpack:
            self.backpack = Backpack(self)
        for i in xrange(len(gagIds)):
            gagId = gagIds[i]
            ammo = ammoList[i]
            if not self.backpack.hasGag(gagId):
                self.backpack.addGag(
                    gagId, ammo,
                    GagGlobals.getGagData(gagId).get('maxSupply'))
            else:
                self.backpack.setSupply(gagId, ammo)

    def getBackpackAmmo(self):
        return ([], [])

    def setGagAmmo(self, gagId, ammo):
        self.backpack.setSupply(gagId, ammo)

    def setMoney(self, money):
        self.money = money

    def getMoney(self):
        return self.money

    def setAdminToken(self, value):
        self.token = value
        if value > -1:
            Toon.Toon.setAdminToken(self, value)
        else:
            Toon.Toon.removeAdminToken(self)

    def getAdminToken(self):
        return self.token

    def setAnimState(self, anim, timestamp=None, callback=None, extraArgs=[]):
        self.anim = anim
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        if type(anim) == types.IntType:
            anim = self.animId2animState[anim]
        if self.animFSM.getStateNamed(anim):
            self.animFSM.request(anim, [ts, callback, extraArgs])
        return

    def b_setAnimState(self, anim):
        self.d_setAnimState(anim)
        self.setAnimState(anim, None)
        return

    def d_setAnimState(self, anim):
        if type(anim) == types.StringType:
            anim = self.animState2animId[anim]
        timestamp = globalClockDelta.getFrameNetworkTime()
        self.sendUpdate('setAnimState', [anim, timestamp])

    def getAnimState(self):
        return self.anim

    def setName(self, name):
        Toon.Toon.setName(self, name)
        if self.cr.isShowingPlayerIds:
            self.showAvId()

    def d_setName(self, name):
        self.sendUpdate('setName', [name])

    def b_setName(self, name):
        self.d_setName(name)
        self.setName(name)

    def showAvId(self):
        self.setDisplayName(self.getName() + '\n' + str(self.doId))

    def showName(self):
        self.setDisplayName(self.getName())

    def setDisplayName(self, name):
        self.setupNameTag(tempName=name)

    def wrtReparentTo(self, parent):
        DistributedSmoothNode.wrtReparentTo(self, parent)

    def announceHealthAndPlaySound(self, level, hp):
        DistributedAvatar.announceHealth(self, level, hp)
        hpSfx = base.audio3d.loadSfx('phase_11/audio/sfx/LB_toonup.ogg')
        base.audio3d.attachSoundToObject(hpSfx, self)
        SoundInterval(hpSfx, node=self).start()
        del hpSfx

    def announceGenerate(self):
        DistributedAvatar.announceGenerate(self)
        if self.animFSM.getCurrentState().getName() == 'off':
            self.setAnimState('neutral')
        self.startBlink()

    def generate(self):
        DistributedAvatar.generate(self)
        DistributedSmoothNode.generate(self)
        self.startSmooth()

    def disable(self):
        if self.tunnelTrack:
            self.ignore(self.tunnelTrack.getDoneEvent())
            self.tunnelTrack.finish()
            self.tunnelTrack = None
        if self.dmgFadeIval:
            self.dmgFadeIval.finish()
            self.dmgFadeIval = None
        self.busy = None
        taskMgr.remove(self.uniqueName('sBAL'))
        taskMgr.remove(self.uniqueName('blinkOnTurn'))
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        self.stopBlink()
        self.__removeHeadMeter()
        self.ignore('showAvId')
        self.ignore('showName')
        self.token = None
        self.stopSmooth()
        Toon.Toon.disable(self)
        DistributedAvatar.disable(self)
        return

    def delete(self):
        try:
            self.DistributedToon_deleted
        except:
            self.DistributedToon_deleted = 1
            self.tutDone = None
            self.defaultShard = None
            Toon.Toon.delete(self)
            DistributedAvatar.delete(self)
            DistributedSmoothNode.delete(self)

        return
예제 #15
0
class TrainController:
    """Object to control the locomotive.

    Implements changing the locomotive speed and animation.
    Also manages moving the locomotive along motion paths.

    Args:
        model (panda3d.core.NodePath): The locomotive model.
    """
    def __init__(self, model):
        self._model = model
        self._move_snd, self._stop_snd, self._brake_snd = self._set_sounds(
            model)

        self._move_anim_int = model.actorInterval("move_forward", playRate=14)
        self._move_anim_int.loop()
        # parallel with the Train model and camera move intervals
        self._move_par = Parallel()
        self._is_stopped = False
        self._outing_available = None
        self._move_snd_volume = 1
        self._garland = None
        self._confetti = None
        self._confetti_cooldown = False
        self._confetti_snd = None

        self.on_et = False
        self.critical_damage = False
        self.max_speed = 1

    @property
    def current_speed(self):
        """Current locomotive speed.

        Returns
            float: Current locomotive speed (play rate).
        """
        return 0 if self._is_stopped else self._move_anim_int.getPlayRate()

    def _change_speed(self, diff, task):
        """Actually change the locomotive speed.

        Args:
            diff (float): Coefficient to change the Train speed.
        """
        if self._is_stopped and diff > 0:
            self.start_move()

        new_rate = round(self._move_anim_int.getPlayRate() + diff, 2)
        if (self.on_et  # don't stop on enemy territory
                and diff < 0
                # stop on enemy territory only
                # in case of critical damage
                and not self.critical_damage and new_rate < MIN_SPEED):
            return task.again

        if new_rate > self.max_speed and diff > 0:
            return task.again

        # change speed
        if 0 < new_rate <= 1:
            self._move_anim_int.setPlayRate(new_rate)
            self._move_par.setPlayRate(new_rate)

            new_snd_rate = new_rate * 1.2
            if 0.25 <= new_snd_rate <= 1:
                self._move_snd.setPlayRate(new_snd_rate)

            return task.again

        if new_rate == 0:
            self._stop_move()

        return task.done

    def _change_speed_delayed(self, diff):
        """Start changing the locomotive speed.

        To make speed changing smoother delayed task is used.

        Args:
            diff (float): Coefficient to change speed.
        """
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._change_speed,
            "change_train_speed",
            extraArgs=[diff],
            appendTask=True,
        )

    def _finish_stopping(self, task):
        """Finish stopping the damaged Train."""
        taskMgr.remove("stop_train")  # noqa: F821
        base.train.stop_sparks()  # noqa: F821

        base.main_menu.show(is_game_over=True)  # noqa: F821
        return task.done

    def _set_sounds(self, model):
        """Set interactive locomotive sounds.

        Args:
            model (panda3d.core.NodePath): The locomotive model.

        Returns:
            (panda3d.core.AudioSound...):
                Interactive locomotive sounds.
        """
        move_snd = base.sound_mgr.loadSfx(
            "sounds/train/ride.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(move_snd, model)  # noqa: F821
        move_snd.setLoop(True)
        move_snd.setVolume(0)
        move_snd.play()

        stop_snd = base.sound_mgr.loadSfx(
            "sounds/train/stop.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(stop_snd, model)  # noqa: F821

        brake_snd = base.sound_mgr.loadSfx(
            "sounds/train/brake.ogg")  # noqa: F821
        brake_snd.setLoop(True)
        base.sound_mgr.attachSoundToObject(brake_snd, model)  # noqa: F821
        return move_snd, stop_snd, brake_snd

    def start_move_sound(self):
        """Start playing the move sound."""
        self._move_snd.setVolume(1)

    def _shot_flapper(self):
        """Play an effect and sound of a flapper."""
        if not self._confetti_cooldown and self._confetti is not None:
            for effect in self._confetti:
                effect.start(self._model, render)  # noqa: F821
                effect.softStart()

            taskMgr.doMethodLater(  # noqa: F821
                1.97, self._stop_flapper, "stop_flapper")
            self._confetti_cooldown = True
            self._confetti_snd.play()

    def _stop_flapper(self, task):
        """Stop all the flapper effects."""
        for effect in self._confetti or []:
            effect.softStop()

        self._confetti_cooldown = False
        return task.done

    def _stop_move(self):
        """Stop the Train movement."""
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._stop_snd.play,
            "train_stop_snd",
            extraArgs=[])
        base.train.stop_sparks()  # noqa: F821
        self._move_par.pause()
        self._move_anim_int.pause()
        taskMgr.doMethodLater(  # noqa: F821
            0.7,
            base.train.stop,
            "release_steam",
            extraArgs=[]  # noqa: F821
        )

        self._is_stopped = True
        taskMgr.doMethodLater(  # noqa: F821
            0.07,
            drown_snd,
            "drown_move_snd",
            extraArgs=[self._move_snd],
            appendTask=True,
        )
        if self._outing_available:
            base.world.start_outing(self._outing_available)  # noqa: F821
            self._outing_available = None

    def _switch_garland(self):
        """Christmas related method.

        Enables or removes the locomotive garland
        and an ability to shot a flapper.
        """
        if self._garland is None:
            self._garland = loader.loadModel(address("garland"))  # noqa: F821
            self._garland.reparentTo(self._model)

            self._confetti = []

            for color in ("red", "blue", "green"):
                confetti = ParticleEffect()
                confetti.loadConfig("effects/confetti_{}.ptf".format(color))
                confetti.setPos(0, 0.32, 0.29)
                self._confetti.append(confetti)

            self._confetti_snd = base.sound_mgr.loadSfx(  # noqa: F821
                "sounds/train/flapper.ogg")
            base.sound_mgr.attachSoundToObject(  # noqa: F821
                self._confetti_snd, self._model)
        else:
            self._garland.removeNode()
            self._garland = None

            for effect in self._confetti:
                effect.softStop()
                effect.cleanup()

            self._confetti = None
            base.sound_mgr.detach_sound(self._confetti_snd)  # noqa: F821
            self._confetti_snd = None

    def brake_down_to(self, target):
        """Slow down the Train to the given speed.

        Args:
            target (float): Target speed.
        """
        if self._brake_snd.status() == AudioSound.PLAYING:
            self._brake_snd.setVolume(1)
        else:
            self._brake_snd.play()
            taskMgr.doMethodLater(  # noqa: F821
                3,
                drown_snd,
                "drown_brake_snd",
                extraArgs=[self._brake_snd],
                appendTask=True,
            )
        self.slow_down_to(target)

    def drown_move_snd(self):
        """Reduce the main move sound volume."""
        if self._move_snd_volume == 1:
            self._move_snd.setVolume(0.5)
            self._move_snd_volume = 0.5

    def move_along_block(self, block, train_np, do_turn):
        """Start the locomotive move intervals for the given block.

        There are two intervals: the locomotive movement
        and synchronous camera movement.

        Args:
            block (world.block.Block):
                The World block to move along.
            train_np (panda3d.core.NodePath): Train node.
            do_turn (int):
                0 if default direction was chosen,
                1 if a turn is needed.
        """
        self.on_et = block.enemy_territory
        self._outing_available = block.outing_available
        # use speed value from the last block
        rate = self._move_par.getPlayRate() if self._move_par else 1

        is_fork = block.name in ("r_fork", "l_fork", "exit_from_fork")

        self._move_par = Parallel(
            MopathInterval(  # locomotive movement
                block.path[do_turn] if is_fork else block.path,
                self._model,
                duration=4.4,
                name="current_path",
            ),
            MopathInterval(  # camera movement
                block.cam_path[do_turn] if is_fork else block.cam_path,
                train_np,
                duration=4.4,
                name="current_camera_path",
            ),
        )
        self._move_par.setDoneEvent("block_finished")
        self._move_par.start()
        self._move_par.setPlayRate(rate)

    def load_speed(self, speed):
        """Load previously saved locomotive speed.

        Args:
            speed (float): Rate to set for animation, move and sounds.
        """
        self._move_par.setPlayRate(speed)
        self._move_anim_int.setPlayRate(speed)
        self._move_snd.setPlayRate(min(max(0.25, speed * 1.2), 1))
        if not speed:
            self._move_snd.stop()
            self._is_stopped = True

    def pause_movement(self):
        """Make a movement pause (used when a tutorial page is shown)."""
        self._move_par.pause()
        self._move_anim_int.pause()
        self._move_snd.stop()

    def set_controls(self, train):
        """Configure the locomotive control keys.

        Args:
            train (train.Train): The locomotive object.
        """
        # speed smoothly changes while holding w/s keys
        base.accept("w", self._change_speed_delayed, [0.05])  # noqa: F821
        base.accept("s", self._change_speed_delayed, [-0.05])  # noqa: F821
        base.accept("w-up", taskMgr.remove,
                    ["change_train_speed"])  # noqa: F821
        base.accept("s-up", taskMgr.remove,
                    ["change_train_speed"])  # noqa: F821
        base.accept("7", self._switch_garland)  # noqa: F821
        base.accept("8", self._shot_flapper)  # noqa: F821
        base.accept("9", base.effects_mgr.love_fog.switch)  # noqa: F821

        base.accept("f", train.toggle_lights)  # noqa: F821

    def start_move(self):
        """Start the Train movement."""
        self._move_par.resume()
        self._move_anim_int.resume()
        self._move_snd.play()
        self._is_stopped = False

    def speed_to_min(self):
        """Accelerate to minimum speed.

        Used when locomotive got on enemy territory.
        """
        taskMgr.remove("change_train_speed")  # noqa: F821
        speed = self._move_anim_int.getPlayRate()
        if speed >= MIN_SPEED:
            return

        # calculate acceleration length
        acc_steps = (MIN_SPEED - speed) / 0.05

        # start accelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._change_speed,
            "speed_up_train",
            extraArgs=[0.05],
            appendTask=True)
        # stop accelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6 * acc_steps + 0.2,
            taskMgr.remove,  # noqa: F821
            "stop_speedind_up",
            extraArgs=["speed_up_train"],
        )

    def slow_down_to(self, target):
        """Slow down the locomotive to the given speed.

        Args:
            target (float): Target speed.
        """
        taskMgr.remove("change_train_speed")  # noqa: F821

        speed = self._move_anim_int.getPlayRate()
        if speed <= target:
            return

        # calculate deceleration length
        acc_steps = (speed - target) / 0.05

        # start decelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._change_speed,
            "slow_down_train",
            extraArgs=[-0.05],
            appendTask=True,
        )
        # stop decelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6 * acc_steps + 0.2,
            taskMgr.remove,  # noqa: F821
            "stop_slowing_down",
            extraArgs=["slow_down_train"],
        )

    def stop(self, urgent=False, place_of_interest=False):
        """Completely stop the locomotive.

        Args:
            urgent (bool):
                If True, deceleration speed will be much higher.
                Used for stopping in places of interest and cities.
            place_of_interest (bool):
                If True, that means the stop is
                initiated by a place of interest.
        """
        base.ignore("w")  # noqa: F821
        base.ignore("s")  # noqa: F821
        taskMgr.remove("change_train_speed")  # noqa: F821

        delay = 0.25 if urgent else 0.6

        # calculate deceleration length
        speed = self._move_anim_int.getPlayRate()
        taskMgr.doMethodLater(  # noqa: F821
            delay,
            self._change_speed,
            "stop_train",
            extraArgs=[-0.05],
            appendTask=True)

        if urgent:
            if place_of_interest:
                taskMgr.doMethodLater(  # noqa: F821
                    delay * (speed / 0.05) + 0.8,
                    base.scenario.start_chapter,  # noqa: F821
                    "start_scenario_chapter",
                )
        else:
            # stop decelerating
            taskMgr.doMethodLater(  # noqa: F821
                delay * (speed / 0.05) + 0.8, self._finish_stopping,
                "finish_stopping")
            taskMgr.doMethodLater(  # noqa: F821
                delay * (speed / 0.05) + 0.2,
                base.world.enemy.stop_ride_anim,  # noqa: F821
                "stop_riding",
            )

    def raise_move_snd(self):
        """Restore full volume of the main move sound."""
        if self._move_snd_volume == 0.5:
            self._move_snd.setVolume(1)
            self._move_snd_volume = 1

    def silence_move_snd(self):
        """Silence the movement sound."""
        self._stop_snd.setVolume(0)
        taskMgr.doMethodLater(  # noqa: F821
            0.1,
            drown_snd,
            "stop_snd",
            extraArgs=[self._move_snd],
            appendTask=True)

    def unset_controls(self):
        """Disable all the locomotive controls."""
        for key in ("w", "s", "w-up", "s-up", "f"):
            base.ignore(key)  # noqa: F821
예제 #16
0
파일: Place.py 프로젝트: coginvasion/src
class Place(StateData):
    notify = directNotify.newCategory('Place')

    def __init__(self, loader, doneEvent):
        StateData.__init__(self, doneEvent)
        self.loader = loader
        self.zoneId = None
        self.track = None
        return

    def maybeUpdateAdminPage(self):
        if self.fsm:
            if self.fsm.getCurrentState():
                if self.fsm.getCurrentState().getName() == 'shtickerBook':
                    if hasattr(self, 'shtickerBookStateData'):
                        if self.shtickerBookStateData.fsm.getCurrentState().getName() == 'adminPage':
                            if base.cr.playGame.suitManager:
                                text2Change2 = 'Turn Suit Spawner '
                                if base.cr.playGame.suitManager.getSpawner():
                                    text2Change2 += 'Off'
                                else:
                                    text2Change2 += 'On'
                                self.shtickerBookStateData.adminPageStateData.suitSpawnerBtn['text'] = text2Change2

    def enterStart(self):
        pass

    def exitStart(self):
        pass

    def enterFinal(self):
        pass

    def exitFinal(self):
        pass

    def enter(self):
        StateData.enter(self)
        base.localAvatar.createChatInput()

    def exit(self):
        base.localAvatar.disableChatInput()
        StateData.exit(self)

    def enterDoorIn(self, distDoor):
        base.localAvatar.attachCamera()
        requestStatus = {}
        requestStatus['zoneId'] = distDoor.getToZone()
        requestStatus['hoodId'] = base.cr.playGame.hood.id
        requestStatus['how'] = 'doorOut'
        requestStatus['shardId'] = None
        requestStatus['doorIndex'] = distDoor.getDoorIndex()
        foundBlock = False
        for interior in base.cr.doFindAll('DistributedToonInterior'):
            if interior.zoneId == base.localAvatar.zoneId:
                foundBlock = True
                requestStatus['block'] = interior.getBlock()
                break

        if not foundBlock:
            requestStatus['block'] = distDoor.getBlock()
        requestStatus['where'] = ZoneUtil.getWhereName(requestStatus['zoneId'])
        requestStatus['loader'] = base.cr.playGame.hood.fsm.getCurrentState().getName()
        requestStatus['avId'] = base.localAvatar.doId
        self.acceptOnce('DistributedDoor_localAvatarWentInDoor', self.handleDoorInDone, [requestStatus])
        self.acceptOnce('DistributedDoor_localAvatarGoingInDoor', base.transitions.irisOut)
        return

    def exitDoorIn(self):
        base.localAvatar.detachCamera()
        self.ignore('DistributedDoor_localAvatarWentInDoor')
        self.ignore('DistributedDoor_localAvatarGoingInDoor')

    def handleDoorInDone(self, requestStatus):
        self.doneStatus = requestStatus
        messenger.send(self.doneEvent)

    def __waitOnDoor(self, door, task):
        if door.ready:
            self.__doorReady(door)
            return task.done
        return task.cont

    def enterDoorOut(self, requestStatus):
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.d_clearSmoothing()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.walkControls.setCollisionsActive(0)
        block = requestStatus['block']
        zoneId = requestStatus['zoneId']
        doorIndex = requestStatus['doorIndex']
        doorToExitFrom = None
        for door in base.cr.doFindAll('DistributedDoor'):
            if door.zoneId == zoneId:
                if door.getBlock() == block:
                    if door.getDoorIndex() == doorIndex:
                        doorToExitFrom = door
                        break

        if not doorToExitFrom:
            self.notify.error('Could not find a DistributedDoor to exit from!')
        elif not doorToExitFrom.ready:
            base.taskMgr.add(self.__waitOnDoor, 'Place.waitOnDoor', extraArgs=[doorToExitFrom], appendTask=True)
            return
        self.__doorReady(doorToExitFrom)
        return

    def __doorReady(self, door):
        door.sendUpdate('requestExit', [])
        self.nextState = 'walk'
        self.acceptOnce('DistributedDoor_localAvatarCameOutOfDoor', self.handleDoorOutDone)

    def exitDoorOut(self):
        base.taskMgr.remove('Place.waitOnDoor')
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()
        self.ignore('DistributedDoor_localAvatarCameOutOfDoor')

    def handleDoorOutDone(self):
        base.transitions.irisIn()
        base.localAvatar.walkControls.setCollisionsActive(1)
        self.fsm.request(self.nextState)

    def enterShtickerBook(self):
        base.localAvatar.attachCamera()
        base.localAvatar.createLaffMeter()
        base.localAvatar.startSmartCamera()
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.b_setAnimState('openBook', self.enterShtickerBookGui)

    def enterShtickerBookGui(self):
        doneEvent = 'shtickerBookDone'
        self.shtickerBookStateData = ShtickerBook(self.fsm, doneEvent)
        self.acceptOnce(doneEvent, self.__shtickerBookDone)
        self.shtickerBookStateData.load()
        self.shtickerBookStateData.enter()
        base.localAvatar.showBookButton(1)
        base.localAvatar.b_setAnimState('readBook')

    def __shtickerBookDone(self):
        doneStatus = self.shtickerBookStateData.getDoneStatus()
        base.localAvatar.hideBookButton()
        self.shtickerBookStateData.exit()
        if doneStatus['mode'] == 'exit':
            base.localAvatar.b_setAnimState('closeBook', self.__handleBookCloseExit)
        elif doneStatus['mode'] == 'teleport':
            base.localAvatar.b_setAnimState('closeBook', self.__handleBookCloseTeleport, [doneStatus])
        elif doneStatus['mode'] == 'resume':
            base.localAvatar.b_setAnimState('closeBook', self.__handleBookCloseResume)
        elif doneStatus['mode'] == 'switchShard':
            base.localAvatar.b_setAnimState('closeBook', self.__handleBookCloseSwitchShard, [doneStatus])

    def __handleBookCloseSwitchShard(self, requestStatus):
        base.localAvatar.b_setAnimState('teleportOut', self.__handleBookSwitchShard, [requestStatus])

    def __handleBookSwitchShard(self, requestStatus):
        params = []
        params.append(requestStatus['shardId'])
        params.append(base.cr.playGame.hood.id)
        params.append(ZoneUtil.getZoneId(base.cr.playGame.hood.id))
        params.append(base.localAvatar.doId)
        base.cr.gameFSM.request('switchShards', params)

    def __handleBookCloseResume(self):
        self.fsm.request('walk')

    def __handleBookCloseTeleport(self, requestStatus):
        self.fsm.request('teleportOut', [requestStatus])

    def __teleportOutDone(self, requestStatus):
        self.doneStatus = requestStatus
        messenger.send(self.doneEvent)

    def __handleBookCloseExit(self):
        base.localAvatar.b_setAnimState('teleportOut', self.__handleBookExitTeleport)

    def __handleBookExitTeleport(self):
        base.transitions.fadeOut(0.0)
        base.cr.gameFSM.request('closeShard')

    def exitShtickerBook(self):
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.disableLaffMeter()
        self.ignore(self.shtickerBookStateData.doneEvent)
        self.shtickerBookStateData.exit()
        self.shtickerBookStateData.unload()
        del self.shtickerBookStateData
        base.localAvatar.hideBookButton()

    def enterStop(self, doNeutral = 1):
        if doNeutral:
            base.localAvatar.b_setAnimState('neutral')
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.createLaffMeter()
        base.localAvatar.createMoney()
        base.localAvatar.enablePies(0)

    def exitStop(self):
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()
        base.localAvatar.disableLaffMeter()
        base.localAvatar.disableMoney()
        base.localAvatar.disablePies()

    def load(self):
        StateData.load(self)
        self.walkDoneEvent = 'walkDone'
        self.walkStateData = PublicWalk(self.fsm, self.walkDoneEvent)
        self.walkStateData.load()

    def unload(self):
        StateData.unload(self)
        del self.walkDoneEvent
        self.walkStateData.unload()
        del self.walkStateData
        del self.loader

    def enterTeleportIn(self, requestStatus):
        if requestStatus['avId'] != base.localAvatar.doId:
            av = base.cr.doId2do.get(requestStatus['avId'])
            if av:
                base.localAvatar.gotoNode(av)
                base.localAvatar.b_setChat('Hi, %s.' % av.getName())
        base.transitions.irisIn()
        self.nextState = requestStatus.get('nextState', 'walk')
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.startPosHprBroadcast()
        globalClock.tick()
        base.localAvatar.b_setAnimState('teleportIn', callback=self.teleportInDone)
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.b_setParent(CIGlobals.SPRender)

    def exitTeleportIn(self):
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()
        base.localAvatar.stopPosHprBroadcast()

    def teleportInDone(self):
        if hasattr(self, 'fsm'):
            self.fsm.request(self.nextState, [1])

    def enterAcknowledgeDeath(self, foo = 0):
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        message = 'You were defeated by the Cogs! Collect treasures in the Playground to refill your Laff meter.'
        self.dialog = GlobalDialog(message=message, style=3, doneEvent='ackDeathDone')
        self.dialog.show()
        self.acceptOnce('ackDeathDone', self.handleAckDeathDone)

    def handleAckDeathDone(self):
        self.fsm.request('walk', [1])

    def exitAcknowledgeDeath(self):
        self.ignore('ackDeathDone')
        self.dialog.cleanup()
        del self.dialog
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()

    def enterDied(self, requestStatus, callback = None):
        if callback == None:
            callback = self.__diedDone
        base.localAvatar.createLaffMeter()
        base.localAvatar.attachCamera()
        base.localAvatar.b_setAnimState('died', callback, [requestStatus])
        return

    def __diedDone(self, requestStatus):
        self.doneStatus = requestStatus
        messenger.send(self.doneEvent)

    def exitDied(self):
        base.localAvatar.disableLaffMeter()
        base.localAvatar.detachCamera()

    def enterWalk(self, teleportIn = 0):
        self.walkStateData.enter()
        if teleportIn == 0:
            self.walkStateData.fsm.request('walking')
        self.acceptOnce(self.walkDoneEvent, self.handleWalkDone)
        self.walkStateData.fsm.request('walking')
        self.watchTunnelSeq = Sequence(Wait(1.0), Func(LinkTunnel.globalAcceptCollisions))
        self.watchTunnelSeq.start()
        base.localAvatar.setBusy(0)
        base.localAvatar.enablePicking()
        base.localAvatar.showFriendButton()

    def exitWalk(self):
        self.walkStateData.exit()
        self.ignore(self.walkDoneEvent)
        if base.cr.playGame.hood.titleText != None:
            base.cr.playGame.hood.hideTitleText()
        self.watchTunnelSeq.pause()
        del self.watchTunnelSeq
        base.localAvatar.setBusy(1)
        base.localAvatar.disablePicking()
        base.localAvatar.hideFriendButton()
        base.localAvatar.friendsList.fsm.requestFinalState()
        base.localAvatar.panel.fsm.requestFinalState()
        return

    def handleWalkDone(self, doneStatus):
        pass

    def enterTeleportOut(self, requestStatus, callback = None):
        if not callback:
            callback = self.__teleportOutDone
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.b_setAnimState('teleportOut', callback, [requestStatus])

    def exitTeleportOut(self):
        base.localAvatar.disableLaffMeter()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()
        base.localAvatar.stopPosHprBroadcast()

    def enterTunnelIn(self, linkTunnel):
        base.localAvatar.attachCamera()
        base.localAvatar.playMovementSfx('run')
        pivotPoint = linkTunnel.inPivotPoint
        self.pivotPointNode = linkTunnel.tunnel.attachNewNode('tunnelPivotPoint')
        self.pivotPointNode.setPos(pivotPoint)
        currCamPos = camera.getPos(linkTunnel.tunnel)
        currCamHpr = camera.getHpr(linkTunnel.tunnel)
        currPos = base.localAvatar.getPos(self.pivotPointNode)
        currHpr = base.localAvatar.getHpr(self.pivotPointNode)
        if linkTunnel.__class__.__name__ == 'SafeZoneLinkTunnel':
            base.localAvatar.setHpr(180, 0, 0)
        else:
            base.localAvatar.setHpr(0, 0, 0)
        base.localAvatar.setPos(currPos)
        base.localAvatar.reparentTo(self.pivotPointNode)
        base.localAvatar.walkControls.setCollisionsActive(0)
        base.localAvatar.detachCamera()
        camera.reparentTo(linkTunnel.tunnel)
        tunnelCamPos = linkTunnel.camPos
        tunnelCamHpr = linkTunnel.camHpr
        self.track = Parallel(LerpPosInterval(camera, duration=0.7, pos=tunnelCamPos, startPos=currCamPos, blendType='easeOut'), LerpQuatInterval(camera, duration=0.7, quat=tunnelCamHpr, startHpr=currCamHpr, blendType='easeOut'), Sequence(Func(base.localAvatar.b_setAnimState, 'run'), Wait(2.0), Func(base.transitions.irisOut)), Sequence(LerpHprInterval(self.pivotPointNode, duration=2.0, hpr=linkTunnel.inPivotEndHpr, startHpr=linkTunnel.inPivotStartHpr), LerpPosInterval(self.pivotPointNode, duration=1.0, pos=(linkTunnel.inPivotEndX, self.pivotPointNode.getY(), self.pivotPointNode.getZ()), startPos=(linkTunnel.inPivotStartX, self.pivotPointNode.getY(), self.pivotPointNode.getZ()))), name='Place.enterTunnelIn')
        requestStatus = {}
        requestStatus['zoneId'] = linkTunnel.data['zoneId']
        if linkTunnel.__class__.__name__ == 'SafeZoneLinkTunnel':
            requestStatus['where'] = 'street'
            requestStatus['loader'] = 'townLoader'
            requestStatus['hoodId'] = base.cr.playGame.hood.id
            requestStatus['shardId'] = None
            requestStatus['avId'] = base.localAvatar.doId
            requestStatus['how'] = 'tunnelOut'
            requestStatus['fromZone'] = base.localAvatar.zoneId
        elif linkTunnel.__class__.__name__ == 'StreetLinkTunnel':
            requestStatus['where'] = 'playground'
            requestStatus['loader'] = 'safeZoneLoader'
            requestStatus['hoodId'] = base.cr.playGame.hood.id
            requestStatus['shardId'] = None
            requestStatus['avId'] = base.localAvatar.doId
            requestStatus['how'] = 'tunnelOut'
            requestStatus['fromZone'] = base.localAvatar.zoneId
        elif linkTunnel.__class__.__name__ == 'NeighborhoodLinkTunnel':
            requestStatus['where'] = 'street'
            requestStatus['loader'] = 'townLoader'
            hoodId = ZoneUtil.getHoodId(linkTunnel.data['zoneId'], 1)
            requestStatus['hoodId'] = hoodId
            requestStatus['shardId'] = None
            requestStatus['avId'] = base.localAvatar.doId
            requestStatus['how'] = 'tunnelOut'
            requestStatus['fromZone'] = base.localAvatar.zoneId
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__handleTunnelInDone, [requestStatus])
        self.track.start()
        return

    def __handleTunnelInDone(self, requestStatus):
        self.doneStatus = requestStatus
        messenger.send(self.doneEvent)

    def exitTunnelIn(self):
        base.localAvatar.playMovementSfx(None)
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        base.localAvatar.reparentTo(render)
        self.pivotPointNode.removeNode()
        del self.pivotPointNode
        base.localAvatar.detachCamera()
        base.localAvatar.walkControls.setCollisionsActive(1)
        return

    def enterTunnelOut(self, requestStatus):
        base.localAvatar.playMovementSfx('run')
        base.transitions.irisIn()
        self.nextState = requestStatus.get('nextState', 'walk')
        linkTunnel = LinkTunnel.getTunnelThatGoesToZone(requestStatus['fromZone'])
        pivotPoint = linkTunnel.outPivotPoint
        self.pivotPointNode = linkTunnel.tunnel.attachNewNode('tunnelPivotPoint')
        self.pivotPointNode.setPos(pivotPoint)
        self.pivotPointNode.setHpr(linkTunnel.outPivotStartHpr)
        base.localAvatar.walkControls.setCollisionsActive(0)
        base.localAvatar.reparentTo(self.pivotPointNode)
        base.localAvatar.setHpr(linkTunnel.toonOutHpr)
        base.localAvatar.setPos(linkTunnel.toonOutPos)
        base.localAvatar.detachCamera()
        camera.reparentTo(linkTunnel.tunnel)
        tunnelCamPos = linkTunnel.camPos
        tunnelCamHpr = linkTunnel.camHpr
        camera.setPos(tunnelCamPos)
        camera.setHpr(tunnelCamHpr)
        self.track = Parallel(Sequence(Func(base.localAvatar.b_setAnimState, 'run'), LerpPosInterval(self.pivotPointNode, duration=1.0, pos=(linkTunnel.outPivotEndX, self.pivotPointNode.getY(), self.pivotPointNode.getZ()), startPos=(linkTunnel.outPivotStartX, self.pivotPointNode.getY(), self.pivotPointNode.getZ())), LerpHprInterval(self.pivotPointNode, duration=2.0, hpr=linkTunnel.outPivotEndHpr, startHpr=linkTunnel.outPivotStartHpr)), name='Place.enterTunnelOut')
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__handleTunnelOutDone)
        self.track.start()

    def __handleTunnelOutDone(self):
        base.localAvatar.walkControls.setCollisionsActive(1)
        self.fsm.request(self.nextState)

    def exitTunnelOut(self):
        base.localAvatar.playMovementSfx(None)
        base.localAvatar.walkControls.setCollisionsActive(1)
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        base.localAvatar.setPos(base.localAvatar.getPos(render))
        base.localAvatar.setHpr(base.localAvatar.getHpr(render))
        base.localAvatar.reparentTo(render)
        self.pivotPointNode.removeNode()
        del self.pivotPointNode
        base.localAvatar.detachCamera()
        del self.nextState
        return
예제 #17
0
class FountainPenAttack(Attack):
    notify = directNotify.newCategory('FountainPenAttack')
    attack = 'fountainpen'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.pen = None
        self.spray = None
        self.splat = None
        self.spraySfx = None
        self.sprayParticle = None
        self.sprayScaleIval = None
        self.wsnp = None
        return

    def loadAttack(self):
        self.pen = loader.loadModel('phase_5/models/props/pen.bam')
        self.pen.reparentTo(self.suit.find('**/joint_Rhold'))
        self.sprayParticle = ParticleLoader.loadParticleEffect('phase_5/etc/penSpill.ptf')
        self.spray = loader.loadModel('phase_3.5/models/props/spray.bam')
        self.spray.setColor(VBase4(0, 0, 0, 1))
        self.splat = Actor('phase_3.5/models/props/splat-mod.bam', {'chan': 'phase_3.5/models/props/splat-chan.bam'})
        self.splat.setColor(VBase4(0, 0, 0, 1))
        self.sprayScaleIval = LerpScaleInterval(self.spray, duration=0.3, scale=(1, 20, 1), startScale=(1, 1, 1))
        sphere = CollisionSphere(0, 0, 0, 0.5)
        sphere.setTangible(0)
        if hasattr(self.suit, 'uniqueName'):
            collName = self.suit.uniqueName('fountainPenCollNode')
        else:
            collName = 'fountainPenCollNode'
        collNode = CollisionNode(collName)
        collNode.addSolid(sphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        self.wsnp = self.spray.attachNewNode(collNode)
        self.wsnp.setY(1)

    def doAttack(self, ts = 0):
        self.loadAttack()
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doFountainPenAttack')
        else:
            name = 'doFountainPenAttack'
        self.suitTrack = Parallel(name=name)
        self.suitTrack.append(ActorInterval(self.suit, 'fountainpen'))
        self.suitTrack.append(Sequence(Wait(1.2), Func(self.acceptOnce, 'enter' + self.wsnp.node().getName(), self.handleSprayCollision), Func(self.playWeaponSound), Func(self.attachSpray), Func(self.sprayParticle.start, self.pen.find('**/joint_toSpray'), self.pen.find('**/joint_toSpray')), self.sprayScaleIval, Wait(0.5), Func(self.sprayParticle.cleanup), Func(self.spray.setScale, 1), Func(self.spray.reparentTo, hidden), Func(self.ignore, 'enter' + self.wsnp.node().getName())))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def attachSpray(self):
        self.spray.reparentTo(self.pen.find('**/joint_toSpray'))
        pos = self.spray.getPos(render)
        hpr = self.spray.getHpr(render)
        self.spray.reparentTo(render)
        self.spray.setPos(pos)
        self.spray.setHpr(hpr)
        self.spray.setP(0)
        if self.suit.type == 'C':
            self.spray.setH(self.spray.getH() + 7.5)
        self.spray.setTwoSided(True)

    def handleSprayCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)
        self.sprayScaleIval.pause()

    def playWeaponSound(self):
        self.spraySfx = base.audio3d.loadSfx('phase_5/audio/sfx/SA_fountain_pen.mp3')
        base.audio3d.attachSoundToObject(self.spraySfx, self.pen)
        self.spraySfx.play()

    def cleanup(self):
        Attack.cleanup(self)
        if self.wsnp:
            self.wsnp.node().clearSolids()
            self.wsnp.removeNode()
            self.wsnp = None
        if self.pen:
            self.pen.removeNode()
            self.pen = None
        if self.sprayParticle:
            self.sprayParticle.cleanup()
            self.sprayParticle = None
        if self.spray:
            self.spray.removeNode()
            self.spray = None
        if self.splat:
            self.splat.cleanup()
            self.splat = None
        if self.sprayScaleIval:
            self.sprayScaleIval.pause()
            self.sprayScaleIval = None
        self.spraySfx = None
        return
예제 #18
0
class DistributedPlayerToon(DistributedToon, DistributedPlayerToonShared):
    notify = directNotify.newCategory('DistributedPlayerToon')

    def __init__(self, cr):
        try:
            self.DistributedPlayerToon_initialized
            return
        except:
            self.DistributedPlayerToon_initialized = 1
        DistributedToon.__init__(self, cr)
        DistributedPlayerToonShared.__init__(self)
        self.role = None
        self.ghost = 0
        self.puInventory = []
        self.equippedPU = -1
        self.backpack = Backpack(self)
        self.battleMeter = None
        self.headMeter = None
        self.firstTimeChangingHP = True

        # Quest-related variables.
        self.quests = ""
        self.tier = None
        self.questHistory = None

        self.busy = 1
        self.friends = None
        self.tutDone = 0
        self.hoodsDiscovered = []
        self.teleportAccess = []
        self.lastHood = 0
        self.defaultShard = 0
        self.tunnelTrack = None
        self.trackExperience = dict(GagGlobals.DefaultTrackExperiences)

        self.takeDmgSfx = base.audio3d.loadSfx(
            'phase_5/audio/sfx/tt_s_ara_cfg_toonHit.ogg')
        base.audio3d.attachSoundToObject(self.takeDmgSfx, self)
        return

    def getHealth(self):
        return DistributedPlayerToonShared.getHealth(self)

    def getMaxHealth(self):
        return DistributedPlayerToonShared.getMaxHealth(self)

    def stopSmooth(self):
        DistributedToon.stopSmooth(self)
        localAvatarReachable = (hasattr(base, 'localAvatar')
                                and base.localAvatar)
        if localAvatarReachable and self.doId != base.localAvatar.doId:
            self.resetTorsoRotation()

    def handleHealthChange(self, hp, oldHp):
        if hp < oldHp and not self.firstTimeChangingHP:
            # We took damage, make oof sound.
            self.takeDmgSfx.play()

    def setHealth(self, health):
        self.handleHealthChange(health, self.getHealth())
        DistributedToon.setHealth(self, health)
        if self.doId != base.localAvatar.doId:
            if not self.firstTimeChangingHP:
                if health < self.getMaxHealth():
                    if not self.headMeter:
                        self.__makeHeadMeter()
                    else:
                        self.__updateHeadMeter()
                else:
                    self.__removeHeadMeter()
        self.firstTimeChangingHP = False

    def announceHealthAndPlaySound(self, level, hp, extraId=-1):
        DistributedToon.announceHealth(self, level, hp, extraId)
        hpSfx = base.audio3d.loadSfx('phase_11/audio/sfx/LB_toonup.ogg')
        base.audio3d.attachSoundToObject(hpSfx, self)
        SoundInterval(hpSfx, node=self).start()
        del hpSfx

    def setChat(self, chat):
        chat = ChatGlobals.filterChat(chat, self.animal)
        DistributedToon.setChat(self, chat)

    def goThroughTunnel(self, toZone, inOrOut, requestStatus=None):
        # inOrOut: 0 = in; 1 = out

        if self.tunnelTrack:
            self.ignore(self.tunnelTrack.getDoneEvent())
            self.tunnelTrack.finish()
            self.tunnelTrack = None

        linkTunnel = LinkTunnel.getTunnelThatGoesToZone(toZone)
        if not linkTunnel:
            return
        self.tunnelTrack = Parallel(
            name=self.uniqueName('Place.goThroughTunnel'))

        if inOrOut == 0:
            # Going in a tunnel!
            pivotPoint = linkTunnel.inPivotPoint
            pivotPointNode = linkTunnel.tunnel.attachNewNode(
                'tunnelPivotPoint')
            pivotPointNode.setPos(pivotPoint)
            pivotPointNode.setHpr(linkTunnel.inPivotStartHpr)

            x, y, z = self.getPos(render)
            surfZ = PhysicsUtils.getNearestGroundSurfaceZ(
                self,
                self.getHeight() + self.getHeight() / 2.0)

            if not surfZ == -1:
                # Let's use the ray-tested surface z-point instead so we don't come out of the tunnel hovering.
                # This is just in case the user jumped into the tunnel, which in that case would mean that they are
                # airborne and we can't depend on their current Z value.
                z = surfZ

            if base.localAvatar.doId == self.doId:
                doneMethod = self._handleWentInTunnel
                extraArgs = [requestStatus]
                base.localAvatar.walkControls.setCollisionsActive(
                    0, andPlaceOnGround=1)
                self.resetHeadHpr(override=True)
                camera.wrtReparentTo(linkTunnel.tunnel)
                currCamPos = camera.getPos()
                currCamHpr = camera.getHpr()
                tunnelCamPos = linkTunnel.camPos
                tunnelCamHpr = linkTunnel.camHpr
                camera.setPos(tunnelCamPos)
                camera.setHpr(tunnelCamHpr)
                self.tunnelTrack.append(
                    LerpPosInterval(camera,
                                    duration=0.7,
                                    pos=tunnelCamPos,
                                    startPos=currCamPos,
                                    blendType='easeOut'))
                self.tunnelTrack.append(
                    LerpQuatInterval(camera,
                                     duration=0.7,
                                     quat=tunnelCamHpr,
                                     startHpr=currCamHpr,
                                     blendType='easeOut'))

            self.wrtReparentTo(pivotPointNode)
            self.setPos(x, y, z)
            self.resetTorsoRotation()
            self.stopLookAround()

            if linkTunnel.__class__.__name__ == "SafeZoneLinkTunnel":
                self.setHpr(180, 0, 0)
            else:
                self.setHpr(0, 0, 0)

            exitSeq = Sequence(Func(self.loop, 'run'))
            if base.localAvatar.doId == self.doId:
                exitSeq.append(Wait(2.0))
                exitSeq.append(Func(base.transitions.irisOut))
            self.tunnelTrack.append(exitSeq)
            self.tunnelTrack.append(
                Sequence(
                    LerpHprInterval(
                        pivotPointNode,
                        duration=2.0,
                        hpr=linkTunnel.inPivotEndHpr,
                        startHpr=linkTunnel.inPivotStartHpr,
                    ),
                    LerpPosInterval(pivotPointNode,
                                    duration=1.0,
                                    pos=(linkTunnel.inPivotEndX,
                                         pivotPointNode.getY(),
                                         pivotPointNode.getZ()),
                                    startPos=(linkTunnel.inPivotStartX,
                                              pivotPointNode.getY(),
                                              pivotPointNode.getZ())),
                    Func(self.reparentTo, hidden)))
        elif inOrOut == 1:

            # Going out!
            pivotPoint = linkTunnel.outPivotPoint
            pivotPointNode = linkTunnel.tunnel.attachNewNode(
                'tunnelPivotPoint')
            pivotPointNode.setPos(pivotPoint)
            pivotPointNode.setHpr(linkTunnel.outPivotStartHpr)

            exitSeq = Sequence()

            if base.localAvatar.doId == self.doId:
                base.localAvatar.walkControls.setCollisionsActive(
                    0, andPlaceOnGround=1)
                base.localAvatar.detachCamera()
                camera.reparentTo(linkTunnel.tunnel)
                tunnelCamPos = linkTunnel.camPos
                tunnelCamHpr = linkTunnel.camHpr
                camera.setPos(tunnelCamPos)
                camera.setHpr(tunnelCamHpr)
                doneMethod = self._handleCameOutTunnel
                extraArgs = []

                exitSeq.append(Func(base.transitions.irisIn))
            else:
                self.stopSmooth()

            self.reparentTo(pivotPointNode)
            self.setHpr(linkTunnel.toonOutHpr)
            self.setPos(linkTunnel.toonOutPos)

            seq = Sequence(
                Func(self.loop, 'run'),
                LerpPosInterval(pivotPointNode,
                                duration=1.0,
                                pos=(linkTunnel.outPivotEndX,
                                     pivotPointNode.getY(),
                                     pivotPointNode.getZ()),
                                startPos=(linkTunnel.outPivotStartX,
                                          pivotPointNode.getY(),
                                          pivotPointNode.getZ())),
                LerpHprInterval(
                    pivotPointNode,
                    duration=2.0,
                    hpr=linkTunnel.outPivotEndHpr,
                    startHpr=linkTunnel.outPivotStartHpr,
                ))
            if base.localAvatar.doId != self.doId:
                seq.append(Func(self.startSmooth))
            seq.append(Func(self.wrtReparentTo, render))
            exitSeq.append(seq)
            self.tunnelTrack.append(exitSeq)

        if base.localAvatar.doId == self.doId:
            self.tunnelTrack.setDoneEvent(self.tunnelTrack.getName())
            self.acceptOnce(self.tunnelTrack.getDoneEvent(), doneMethod,
                            extraArgs)

        self.tunnelTrack.start()

    def setDefaultShard(self, shardId):
        self.defaultShard = shardId

    def getDefaultShard(self):
        return self.defaultShard

    def setLastHood(self, zoneId):
        self.lastHood = zoneId

    def b_setLastHood(self, zoneId):
        self.sendUpdate('setLastHood', [zoneId])
        self.setLastHood(zoneId)

    def getLastHood(self):
        return self.lastHood

    def setTeleportAccess(self, array):
        self.teleportAccess = array

    def getTeleportAccess(self):
        return self.teleportAccess

    def setHoodsDiscovered(self, array):
        self.hoodsDiscovered = array

    def b_setHoodsDiscovered(self, array):
        self.sendUpdate('setHoodsDiscovered', [array])
        self.setHoodsDiscovered(array)

    def getHoodsDiscovered(self):
        return self.hoodsDiscovered

    def setTutorialCompleted(self, value):
        self.tutDone = value

    def getTutorialCompleted(self):
        return self.tutDone

    def setFriendsList(self, friends):
        self.friends = friends

    def getFriendsList(self):
        return self.friends

    def setBusy(self, busy):
        self.busy = busy

    def getBusy(self):
        return self.busy

    def setTier(self, tier):
        self.tier = tier

    def getTier(self):
        return self.tier

    def setQuestHistory(self, array):
        self.questHistory = array

    def getQuestHistory(self):
        return self.questHistory

    def setQuests(self, dataStr):
        self.quests = dataStr

    def getQuests(self):
        return self.quests

    def maybeMakeHeadMeter(self):
        if base.localAvatar.doId != self.doId:
            if self.getHealth() < self.getMaxHealth():
                if not self.headMeter:
                    self.__makeHeadMeter()

    def __makeHeadMeter(self):
        self.headMeter = LaffOMeter(forRender=True)
        r, g, b, _ = self.getHeadColor()
        animal = self.getAnimal()
        maxHp = self.getMaxHealth()
        hp = self.getHealth()
        self.headMeter.generate(r, g, b, animal, maxHP=maxHp, initialHP=hp)
        self.headMeter.reparentTo(self)
        self.headMeter.setZ(self.getHeight() + 2)
        self.headMeter.setScale(0.4)
        self.headMeter.setBillboardAxis()
        self.__updateHeadMeter()

    def __removeHeadMeter(self):
        if self.headMeter:
            self.headMeter.disable()
            self.headMeter.delete()
            self.headMeter = None

    def __updateHeadMeter(self):
        if self.headMeter:
            self.headMeter.updateMeter(self.getHealth())

    def d_createBattleMeter(self):
        self.sendUpdate('makeBattleMeter', [])

    def b_createBattleMeter(self):
        self.makeBattleMeter()
        self.d_createBattleMeter()

    def d_cleanupBattleMeter(self):
        self.sendUpdate('destroyBattleMeter', [])

    def b_cleanupBattleMeter(self):
        self.destroyBattleMeter()
        self.d_cleanupBattleMeter()

    def makeBattleMeter(self):
        if self.getHealth() < self.getMaxHealth():
            if not self.battleMeter:
                self.battleMeter = LaffOMeter()
                r, g, b, _ = self.getHeadColor()
                animal = self.getAnimal()
                maxHp = self.getMaxHealth()
                hp = self.getHealth()
                self.battleMeter.generate(r,
                                          g,
                                          b,
                                          animal,
                                          maxHP=maxHp,
                                          initialHP=hp)
                self.battleMeter.reparentTo(self)
                self.battleMeter.setZ(self.getHeight() + 5)
                self.battleMeter.setScale(0.5)
                self.battleMeter.start()

    def destroyBattleMeter(self):
        if self.battleMeter:
            self.battleMeter.stop()
            self.battleMeter.disable()
            self.battleMeter.delete()
            self.battleMeter = None

    def setEquippedPU(self, index):
        self.equippedPU = index

    def getEquippedPU(self):
        return self.equippedPU

    def setPUInventory(self, array):
        self.puInventory = array

    def getPUInventory(self):
        return self.puInventory

    def setGhost(self, value):
        self.ghost = value
        self.handleGhost(value)

    def d_setGhost(self, value):
        self.sendUpdate("setGhost", [value])

    def b_setGhost(self, value):
        self.d_setGhost(value)
        self.setGhost(value)

    def getGhost(self):
        return self.ghost

    def getBackpack(self):
        return self.backpack

    def setEquippedAttack(self, attackID):
        try:
            self.backpack.setCurrentGag(attackID)
        except:
            # If we couldn't do this, it means that the avatar was most likely disabled.
            pass
        DistributedToon.setEquippedAttack(self, attackID)

    def getCurrentGag(self):
        return self.getEquippedAttack()

    def setLoadout(self, gagIds):
        if self.backpack:
            loadout = []
            for i in range(len(gagIds)):
                gagId = gagIds[i]
                gag = self.backpack.getGagByID(gagId)
                if gag:
                    loadout.append(gag)
            self.backpack.setLoadout(loadout)

    def setBackpackAmmo(self, netString):
        if len(self.attackIds) != 0 or len(self.attacks) != 0:
            self.cleanupAttacks()
            self.clearAttackIds()
        return self.backpack.updateSuppliesFromNetString(netString)

    def getBackpackAmmo(self):
        if self.backpack:
            return self.backpack.netString
        return GagGlobals.getDefaultBackpack().toNetString()

    def setTrackExperience(self, netString):
        self.trackExperience = GagGlobals.getTrackExperienceFromNetString(
            netString)
        if GagGlobals.processTrackData(
                self.trackExperience,
                self.backpack) and self == base.localAvatar:
            if base.localAvatar.invGui:
                base.localAvatar.reloadInvGui()

    def getTrackExperience(self):
        return GagGlobals.trackExperienceToNetString(self.trackExperience)

    def updateAttackAmmo(self, gagId, ammo, maxAmmo, ammo2, maxAmmo2, clip,
                         maxClip):
        if self.useBackpack():
            self.backpack.setSupply(gagId, ammo)
        else:
            DistributedToon.updateAttackAmmo(self, gagId, ammo, maxAmmo, ammo2,
                                             maxAmmo2, clip, maxClip)

    def setMoney(self, money):
        self.money = money

    def getMoney(self):
        return self.money

    def setAccessLevel(self, value):
        prevLevel = self.getAccessLevel()
        self.role = AdminCommands.Roles.get(value, None)

        if prevLevel != AdminCommands.NoAccess:
            # Let's remove any tokens that already are showing up.
            DistributedToon.removeAdminToken(self)

        if self.role:
            # Let's put a new token above our head.
            DistributedToon.setAdminToken(self, self.role.token)

    def getAccessLevel(self):
        return AdminCommands.NoAccess if not self.role else self.role.accessLevel

    def disable(self):
        base.audio3d.detachSound(self.takeDmgSfx)
        self.takeDmgSfx = None
        if self.tunnelTrack:
            self.ignore(self.tunnelTrack.getDoneEvent())
            self.tunnelTrack.finish()
            self.tunnelTrack = None
        self.role = None
        self.ghost = None
        self.puInventory = None
        self.equippedPU = None
        if self.backpack:
            self.backpack.cleanup()
            self.backpack = None
        self.firstTimeChangingHP = None
        self.quests = None
        self.tier = None
        self.questHistory = None
        self.busy = None
        self.friends = None
        self.tutDone = None
        self.hoodsDiscovered = None
        self.teleportAccess = None
        self.lastHood = None
        self.defaultShard = None
        self.trackExperience = None
        self.__removeHeadMeter()
        self.destroyBattleMeter()
        DistributedToon.disable(self)

    def delete(self):
        try:
            self.DistributedPlayerToon_deleted
        except:
            self.DistributedPlayerToon_deleted = 1
            DistributedPlayerToonShared.delete(self)
            del self.takeDmgSfx
            del self.tunnelTrack
            del self.role
            del self.ghost
            del self.puInventory
            del self.equippedPU
            del self.backpack
            del self.firstTimeChangingHP
            del self.quests
            del self.tier
            del self.questHistory
            del self.busy
            del self.friends
            del self.tutDone
            del self.hoodsDiscovered
            del self.teleportAccess
            del self.lastHood
            del self.defaultShard
            del self.trackExperience
            del self.battleMeter
            del self.headMeter
            DistributedToon.delete(self)
        return
class DistributedDodgeballGame(DistributedToonFPSGame, TeamMinigame):
    """The winter dodgeball minigame (client side)"""

    notify = directNotify.newCategory("DistributedDodgeballGame")

    TreeData = [['prop_snow_tree_small_ur',
                 Point3(23.23, 66.52, 7.46)],
                ['prop_snow_tree_small_ul',
                 Point3(-34.03, 88.02, 24.17)],
                ['prop_snow_tree_small_ur',
                 Point3(-54.80, 0, 4.19)],
                ['prop_snow_tree_small_ul',
                 Point3(54.80, -5, 4.19)],
                ['prop_snow_tree_small_ur',
                 Point3(62.71, 62.66, 16.80)],
                ['prop_snow_tree_small_ul',
                 Point3(-23.23, -66.52, 6)],
                ['prop_snow_tree_small_ur',
                 Point3(34.03, -88.02, 23)],
                ['prop_snow_tree_small_ul',
                 Point3(-62.71, -62.66, 16)]]

    SnowballData = [
        Point3(30, 0, 0.75),
        Point3(22.5, 0, 0.75),
        Point3(15, 0, 0.75),
        Point3(7.5, 0, 0.75),
        Point3(0, 0, 0.75),
        Point3(-7.5, 0, 0.75),
        Point3(-15, 0, 0.75),
        Point3(-22.5, 0, 0.75),
        Point3(-30, 0, 0.75)
    ]

    GameSong = "phase_4/audio/bgm/MG_Dodgeball.ogg"
    GameDesc = (
        "Welcome to the north! You have been invited to play dodgeball with the penguins!\n\n"
        "How To Play\nWASD to Move and use the mouse to aim.\nLeft click to Throw!\nThrow a "
        "snowball at a teammate to unfreeze them!\n\nThe team with the most points after {0} rounds wins!"
        .format(MaxRounds))

    InitCamTrans = [Point3(25, 45, 19.5317), Vec3(154.001, -15, 0)]

    SnowBallDmg = 25

    GetSnowBalls = "Pick up a snowball from the center!"

    Team2OtherBarrier = {BLUE: "red_barrier_coll", RED: "blue_barrier_coll"}

    def __init__(self, cr):
        try:
            self.DistributedDodgeballGame_initialized
            return
        except:
            self.DistributedDodgeballGame_initialized = 1

        DistributedToonFPSGame.__init__(self, cr)

        TeamMinigame.__init__(
            self, "BlueSnow", ('phase_4/maps/db_blue_neutral.png',
                               'phase_4/maps/db_blue_hover.png',
                               'phase_4/maps/db_blue_hover.png'), "RedIce",
            ('phase_4/maps/db_red_neutral.png',
             'phase_4/maps/db_red_hover.png', 'phase_4/maps/db_red_hover.png'))

        self.fsm.addState(
            State('chooseTeam', self.enterChooseTeam, self.exitChooseTeam,
                  ['waitForOthers']))
        self.fsm.addState(
            State('scrollBy', self.enterScrollBy, self.exitScrollBy,
                  ['countdown']))
        self.fsm.addState(
            State('countdown', self.enterCountdown, self.exitCountdown,
                  ['play']))
        self.fsm.addState(
            State('announceGameOver', self.enterAnnGameOver,
                  self.exitAnnGameOver, ['displayWinners', 'countdown']))
        self.fsm.addState(
            State('displayWinners', self.enterDisplayWinners,
                  self.exitDisplayWinners, ['gameOver']))
        self.fsm.getStateNamed('waitForOthers').addTransition('chooseTeam')
        self.fsm.getStateNamed('waitForOthers').addTransition('scrollBy')
        self.fsm.getStateNamed('play').addTransition('announceGameOver')

        self.firstPerson = DodgeballFirstPerson(self)

        self.scrollBySeq = None
        self.infoText = None

        self.redScoreLbl = None
        self.blueScoreLbl = None

        self.barrierIsStashed = False

        self.mySpawnPoint = 0

        self.infoText = getAlertText()

        self.spawnPointsByTeam = {
            BLUE: [[Point3(5, 15, 0), Vec3(180, 0, 0)],
                   [Point3(15, 15, 0), Vec3(180, 0, 0)],
                   [Point3(-5, 15, 0), Vec3(180, 0, 0)],
                   [Point3(-15, 15, 0), Vec3(180, 0, 0)]],
            RED: [[Point3(5, -15, 0), Vec3(0, 0, 0)],
                  [Point3(15, -15, 0), Vec3(0, 0, 0)],
                  [Point3(-5, -15, 0), Vec3(0, 0, 0)],
                  [Point3(-15, -15, 0), Vec3(0, 0, 0)]]
        }

        self.winnerMusic = base.loadMusic(
            'phase_9/audio/bgm/encntr_hall_of_fame.ogg')
        self.loserMusic = base.loadMusic(
            'phase_9/audio/bgm/encntr_sting_announce.ogg')
        self.danceSound = base.loadSfx('phase_3.5/audio/sfx/ENC_Win.ogg')

        # Environment vars
        self.arena = None
        self.olc = None
        self.trees = []
        self.snowballs = []

    def snowballPickupResp(self, flag, idx):
        self.firstPerson.snowballPickupResp(flag, idx)

    def roundOver(self, time=0):
        teams = [BLUE, RED]
        teams.sort(key=lambda team: self.scoreByTeam[team], reverse=True)
        self.winnerTeam = teams[0]
        base.localAvatar.disableAvatarControls()
        self.firstPerson.end()
        self.deleteTimer()
        self.fsm.request('announceGameOver', [time])

    def getTeamDNAColor(self, team):
        if team == TEAM1:
            return ToonDNA.colorDNA2color['18']
        elif team == TEAM2:
            return ToonDNA.colorDNA2color['02']

    def enterDisplayWinners(self):
        base.localAvatar.stopLookAround()
        base.localAvatar.resetHeadHpr()
        base.localAvatar.getGeomNode().show()
        camera.reparentTo(render)
        camera.setPos((-2.5, 12, 3.5))
        camera.setHpr((-175.074, -5.47218, 0))

        base.transitions.fadeIn()

        base.playSfx(self.danceSound, looping=1)

        if self.winnerTeam == self.team:
            base.playMusic(self.winnerMusic)
        else:
            base.playMusic(self.loserMusic)

        winnerPositions = [(-2, 0, 0), (2, 0, 0), (6, 0, 0), (-6, 0, 0)]
        loserPositions = [(-3.5, -10, 0), (-1.5, -15, 0), (3.0, -8, 0),
                          (5.5, -12, 0)]
        for team in [RED, BLUE]:
            for avId in self.playerListByTeam[team]:
                av = self.cr.doId2do.get(avId)
                if av:
                    av.stopSmooth()
                    av.setHpr(0, 0, 0)
                    if team == self.winnerTeam:
                        posList = winnerPositions
                        av.setAnimState("off")
                        av.stop()
                        if not self.getRemoteAvatar(avId).isFrozen:
                            av.loop("win")
                    else:
                        posList = loserPositions
                        av.setAnimState('off')
                        av.stop()
                        if not self.getRemoteAvatar(avId).isFrozen:
                            av.loop("pout")
                    pos = random.choice(posList)
                    posList.remove(pos)
                    av.setPos(pos)

        if self.winnerTeam == self.team:
            text = "YOU WIN!"
        else:
            text = "YOU LOSE!"
        self.gameOverLbl.setText(text)

        self.track = Sequence(Wait(2.0), Func(self.gameOverLbl.setScale, 0.01),
                              Func(self.gameOverLbl.show),
                              getAlertPulse(self.gameOverLbl, 0.27, 0.25))
        self.track.start()

        base.localAvatar.collisionsOff()

    def exitDisplayWinners(self):
        base.localAvatar.collisionsOn()
        base.transitions.noTransitions()
        self.danceSound.stop()
        if hasattr(self, 'track'):
            self.track.finish()
            self.track = None
        self.gameOverLbl.hide()

    def __prepareForNextRound(self):
        for av in self.remoteAvatars:
            av.unFreeze()
        pos, hpr = self.spawnPointsByTeam[self.team][self.mySpawnPoint]
        base.localAvatar.setPos(pos)
        base.localAvatar.setHpr(hpr)
        self.playMinigameMusic()
        self.fsm.request('countdown')

    def enterAnnGameOver(self, timeRanOut=0):
        self.firstPerson.vModel.hide()

        text = getGameText()
        text.setBin('gui-popup', 60)

        ival = Sequence()

        if timeRanOut:
            ival.append(Func(text.setText, "Time's Up!"))
            ival.append(getRoundIval(text))

        ival.append(
            Func(text.setText, "Round {0} Over!".format(self.getRound())))
        ival.append(getRoundIval(text))

        team = "Red"
        if self.winnerTeam == BLUE:
            team = "Blue"

        if self.round != MaxRounds:
            if self.scoreByTeam[RED] == self.scoreByTeam[BLUE]:
                ival.append(Func(text.setText, "The scores are tied!"))
            else:
                ival.append(
                    Func(text.setText, "{0} is in the lead!".format(team)))
        else:
            ival.append(Func(text.setText, "{0} wins!".format(team)))

        ival.append(getRoundIval(text))
        ival.setDoneEvent(self.uniqueName('annGameOverDone'))
        self.acceptOnce(ival.getDoneEvent(), self.__annGameOverTask)
        self.ival = ival
        self.ival.start()
        self.text = text

        base.transitions.fadeScreen()

    def __annGameOverTask(self):
        if self.round == MaxRounds:
            nextState = Func(self.fsm.request, "displayWinners")
        else:
            nextState = Func(self.__prepareForNextRound)

        self.ival = Sequence(base.transitions.getFadeOutIval(), nextState)
        self.ival.start()

    def exitAnnGameOver(self):
        if hasattr(self, 'ival'):
            self.ignore(self.ival.getDoneEvent())
            self.ival.finish()
            del self.ival
        if hasattr(self, 'text'):
            self.text.destroy()
            del self.text

    def teamWon(self, team, time=0):
        TeamMinigame.teamWon(self, team, time)
        base.localAvatar.disableAvatarControls()
        self.firstPerson.end()
        self.deleteTimer()
        self.fsm.request('announceGameOver', [time])

    def incrementTeamScore(self, team):
        TeamMinigame.incrementTeamScore(self, team)
        if team == BLUE:
            self.blueScoreLbl.setText("BLUE: " + str(self.scoreByTeam[team]))
            ToontownIntervals.start(
                ToontownIntervals.getPulseLargerIval(self.blueScoreLbl,
                                                     'blueScorePulse'))
        elif team == RED:
            self.redScoreLbl.setText("RED: " + str(self.scoreByTeam[team]))
            ToontownIntervals.start(
                ToontownIntervals.getPulseLargerIval(self.redScoreLbl,
                                                     'redScorePulse'))

    def getWinterDodgeballScoreText(self, color):
        text = OnscreenText(fg=color,
                            font=CIGlobals.getMinnieFont(),
                            scale=0.15,
                            shadow=(0, 0, 0, 1))
        return text

    def snowballHitWall(self, snowballIndex):
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitWallOrPlayer()
        base.playSfx(snowball.impactSound, node=snowball, volume=1.5)

    def snowballHitGround(self, snowballIndex):
        snowball = self.snowballs[snowballIndex]
        snowball.handleHitGround()
        base.playSfx(snowball.impactSound, node=snowball, volume=1.5)

    def snowballHitPlayer(self, damagedPlayer, throwerTeam, snowballIndex):
        av = self.getRemoteAvatar(damagedPlayer)
        if av:
            if throwerTeam == av.team:
                # Someone on my team hit me. Unfreeze me if I am frozen.
                if av.unFreeze():
                    if damagedPlayer == base.localAvatar.doId:
                        self.showAlert("A team member has unfroze you!")
                        self.firstPerson.camFSM.request('unfrozen')
                        self.sendUpdate('teamMateUnfrozeMe', [self.team])
            else:
                # An enemy hit me. Become frozen if I am not already.
                if av.freeze():
                    if damagedPlayer == base.localAvatar.doId:
                        self.showAlert("You've been frozen by an enemy!")
                        self.firstPerson.camFSM.request('frozen')
                        self.sendUpdate('enemyFrozeMe',
                                        [self.team, throwerTeam])

        snowball = self.snowballs[snowballIndex]
        snowball.handleHitWallOrPlayer()
        base.playSfx(snowball.impactSound, node=snowball, volume=1.5)

    def playerCaughtSnowball(self, snowballIndex, catcherId):
        av = self.getRemoteAvatar(catcherId)
        if av:
            snowball = self.snowballs[snowballIndex]
            snowball.pauseThrowIval()
            snowball.pickup(av)

    def setupRemoteAvatar(self, avId):
        av = RemoteDodgeballAvatar(self, self.cr, avId)
        if avId == self.cr.localAvId:
            self.myRemoteAvatar = av
        self.remoteAvatars.append(av)

    def __getSnowTree(self, path):
        trees = loader.loadModel('phase_8/models/props/snow_trees.bam')
        tree = trees.find('**/' + path)
        tree.find('**/*shadow*').removeNode()
        return tree

    def load(self):
        self.setMinigameMusic(DistributedDodgeballGame.GameSong)
        self.setDescription(DistributedDodgeballGame.GameDesc)
        self.setWinnerPrize(200)
        self.setLoserPrize(0)
        self.createWorld()

        self.blueScoreLbl = self.getWinterDodgeballScoreText(VBase4(
            0, 0, 1, 1))
        self.blueScoreLbl.reparentTo(base.a2dTopLeft)
        self.blueScoreLbl['align'] = TextNode.ALeft
        self.blueScoreLbl.setText('Blue: 0')
        self.blueScoreLbl.setZ(-0.17)
        self.blueScoreLbl.setX(0.05)
        self.blueScoreLbl.hide()

        self.redScoreLbl = self.getWinterDodgeballScoreText(VBase4(1, 0, 0, 1))
        self.redScoreLbl.reparentTo(base.a2dTopLeft)
        self.redScoreLbl['align'] = TextNode.ALeft
        self.redScoreLbl.setText('Red: 0')
        self.redScoreLbl.setZ(-0.35)
        self.redScoreLbl.setX(0.05)
        self.redScoreLbl.hide()

        trans = DistributedDodgeballGame.InitCamTrans
        camera.setPos(trans[0])
        camera.setHpr(trans[1])

        DistributedToonFPSGame.load(self)

    def createWorld(self):
        self.deleteWorld()

        self.arena = loader.loadModel(
            "phase_4/models/minigames/dodgeball_arena.egg")
        self.arena.reparentTo(render)
        self.arena.setScale(0.75)
        self.arena.find('**/team_divider').setBin('ground', 18)
        self.arena.find('**/floor').setBin('ground', 18)
        self.arena.find('**/team_divider_coll').setCollideMask(
            CIGlobals.FloorBitmask)

        for data in DistributedDodgeballGame.TreeData:
            code = data[0]
            pos = data[1]
            tree = self.__getSnowTree(code)
            tree.reparentTo(self.arena)
            tree.setPos(pos)
            self.trees.append(tree)

        for i in xrange(len(DistributedDodgeballGame.SnowballData)):
            snowdata = DistributedDodgeballGame.SnowballData[i]
            snowball = Snowball(self, i)
            snowball.load()
            snowball.reparentTo(render)
            snowball.setPos(snowdata)
            self.snowballs.append(snowball)

        self.olc = ZoneUtil.getOutdoorLightingConfig(ZoneUtil.TheBrrrgh)
        self.olc.setupAndApply()

    def throw(self, snowballIndex, p):
        snowball = self.snowballs[snowballIndex]
        snowball.throw(p)

    def snowballPickup(self, snowballIndex, pickerUpperAvId):
        remoteAv = self.getRemoteAvatar(pickerUpperAvId)
        if remoteAv:
            snowball = self.snowballs[snowballIndex]
            snowball.pickup(remoteAv)

    def deleteWorld(self):
        if self.redScoreLbl:
            self.redScoreLbl.destroy()
            self.redScoreLbl = None
        if self.blueScoreLbl:
            self.blueScoreLbl.destroy()
            self.blueScoreLbl = None
        for snowball in self.snowballs:
            snowball.removeNode()
        self.snowballs = []
        for tree in self.trees:
            tree.removeNode()
        self.trees = []
        if self.olc:
            self.olc.cleanup()
            self.olc = None
        if self.arena:
            self.arena.removeNode()
            self.arena = None
        render.clearFog()

    def enterPlay(self):
        self.createTimer()
        self.redScoreLbl.show()
        self.blueScoreLbl.show()
        self.firstPerson.camFSM.request('unfrozen')

        # Stash the other team's barrier.
        if not self.barrierIsStashed:
            self.arena.find('**/' + self.Team2OtherBarrier[self.team]).stash()
            self.barrierIsStashed = True

    def exitPlay(self):
        self.firstPerson.crosshair.destroy()
        self.firstPerson.crosshair = None
        self.firstPerson.camFSM.request('off')
        DistributedToonFPSGame.exitPlay(self)

    def enterCountdown(self):
        base.transitions.noTransitions()
        self.firstPerson.start()
        self.firstPerson.disableMouse()

        self.setRound(self.getRound() + 1)

        self.infoText.setText(DistributedDodgeballGame.GetSnowBalls)

        self.countdownText = getGameText()
        self.countdownIval = Parallel(Sequence(
            Func(self.countdownText.setText,
                 "Round {0}".format(self.getRound())),
            getRoundIval(self.countdownText),
            Func(self.countdownText.setText, "5"),
            getCountdownIval(self.countdownText),
            Func(self.countdownText.setText, "4"),
            getCountdownIval(self.countdownText),
            Func(self.countdownText.setText, "3"),
            getCountdownIval(self.countdownText),
            Func(self.countdownText.setText, "2"),
            getCountdownIval(self.countdownText),
            Func(self.countdownText.setText, "1"),
            getCountdownIval(self.countdownText)),
                                      getAlertPulse(self.infoText),
                                      name="COUNTDOWNIVAL")
        self.countdownIval.setDoneEvent(self.countdownIval.getName())
        self.acceptOnce(self.countdownIval.getDoneEvent(),
                        self.__handleCountdownDone)
        self.countdownIval.start()

    def __handleCountdownDone(self):
        self.fsm.request('play')

    def exitCountdown(self):
        if hasattr(self, 'countdownIval'):
            self.ignore(self.countdownIval.getDoneEvent())
            self.countdownIval.finish()
            del self.countdownIval
        if hasattr(self, 'countdownText'):
            self.countdownText.destroy()
            del self.countdownText

    def enterScrollBy(self):
        BLUE_START_POS = Point3(-20, 0, 4)
        BLUE_END_POS = Point3(20, 0, 4)
        BLUE_HPR = Vec3(0, 0, 0)

        RED_START_POS = Point3(20, 0, 4)
        RED_END_POS = Point3(-20, 0, 4)
        RED_HPR = Vec3(180, 0, 0)

        self.playMinigameMusic()

        self.scrollBySeq = Sequence(Func(camera.setHpr, BLUE_HPR),
                                    LerpPosInterval(camera,
                                                    duration=5.0,
                                                    pos=BLUE_END_POS,
                                                    startPos=BLUE_START_POS,
                                                    blendType='easeOut'),
                                    Func(base.transitions.fadeOut, 0.4),
                                    Wait(0.5),
                                    Func(base.transitions.fadeIn, 0.4),
                                    Func(camera.setHpr, RED_HPR),
                                    LerpPosInterval(camera,
                                                    duration=5.0,
                                                    pos=RED_END_POS,
                                                    startPos=RED_START_POS,
                                                    blendType='easeOut'),
                                    name="SCROLLBYSEQ")
        self.scrollBySeq.setDoneEvent(self.scrollBySeq.getName())
        self.acceptOnce(self.scrollBySeq.getDoneEvent(),
                        self.__handleScrollByDone)
        self.scrollBySeq.start()

    def __handleScrollByDone(self):
        self.fsm.request('countdown')

    def exitScrollBy(self):
        if self.scrollBySeq:
            self.ignore(self.scrollBySeq.getDoneEvent())
            self.scrollBySeq.finish()
            self.scrollBySeq = None

    def allPlayersReady(self):
        self.fsm.request('scrollBy')

    def chooseUrTeam(self):
        # The AI has told us it's time to choose our team.
        self.fsm.request('chooseTeam')

    def enterChooseTeam(self):
        self.makeSelectionGUI()

    def acceptedIntoTeam(self, spawnPoint):
        TeamMinigame.acceptedIntoTeam(self)

        self.sendUpdate('readyToStart')
        self.fsm.request('waitForOthers')

        self.mySpawnPoint = spawnPoint

        pos, hpr = self.spawnPointsByTeam[self.team][spawnPoint]
        base.localAvatar.setPos(pos)
        base.localAvatar.setHpr(hpr)

    def exitChooseTeam(self):
        self.destroySelectionGUI()

    def announceGenerate(self):
        DistributedToonFPSGame.announceGenerate(self)
        base.camLens.setMinFov(CIGlobals.GunGameFOV / (4. / 3.))
        self.load()

    def disable(self):
        base.camLens.setMinFov(CIGlobals.DefaultCameraFov / (4. / 3.))
        self.fsm.requestFinalState()
        self.deleteWorld()
        self.trees = None
        self.snowballs = None
        self.spawnPointsByTeam = None
        if self.firstPerson:
            self.firstPerson.reallyEnd()
            self.firstPerson.cleanup()
            self.firstPerson = None
        self.scrollBySeq = None
        self.winnerMusic = None
        self.loserMusic = None
        self.danceSound = None
        self.infoText = None
        base.localAvatar.setWalkSpeedNormal()
        DistributedToonFPSGame.disable(self)
예제 #20
0
class DistributedTutorial(DistributedObject):
    notify = directNotify.newCategory('DistributedTutorial')
    GUIDE_NAME = 'Professor Prepostera'
    GUIDE_START_POS = (5, 10, -0.5)
    GUIDE_WATCH_POS = (12.4, 27.92, 0)
    GUIDE_WATCH_HPR = (41.63, 0, 0)
    GUIDE_INTRO_SPEECH = ['Hey, looks like you made it!',
     'So, welcome to OToontown.',
     'OToontown is short for Old Toontown, or Toontown from the past.',
     'Not long ago, Toons used to live present day Toontown.',
     'Unfortunately, the Cogs planned a mega-invasion that was sure to be a complete takeover of Toontown and make all Toons go sad for good.',
     "There was no way we could have let that happen, so we built a time machine, and sent every Toon back in time to OToontown, where Cogs didn't exist yet.",
     'The Cogs completely took over present day Toontown, and turned it into what they wanted it to be - a business metropolis.',
     'Toons happily live and play in OToontown now, but we want to learn about present day Toontown...',
     ' ...or as we now call it, CogTropolis.',
     "We've built time machines that send Toons back to CogTropolis to fight Cogs and to see what the Cogs have done.",
     "We know that the Cogs took over Toontown and turned it into a grey business city, but we don't know how they did it.",
     'Shopkeepers around OToontown will reward you for finding evidence that may help solve the mystery of how the Cogs turned Toontown into CogTropolis.',
     'Before you are able to head to CogTropolis, you need to be trained for battle.',
     'The Cogs have become much more skilled battlers and no longer wait for you to throw a gag before attacking you.',
     'This is much more difficult for Toons, and it may take some time to get used to.',
     "I'm going to give you 4 gags to start...",
     'A Toon-Up megaphone, a cupcake, a fruit pie slice, and a cream pie slice.',
     'Equip Gags in your loadout to use by pressing the corresponding key on your keyboard.',
     'You can use or throw the Gag that you have equipped by pressing the Delete key...',
     ' ...or by pressing the Throw Gag button at the top of your screen.',
     'Also, use the Arrow Keys on your keyboard to move, and press CTRL to jump.',
     "I'm going to summon one of our dummy bots for you to practice battling.",
     "Click your mouse when you're ready."]
    GUIDE_PT2_INFO = ["Now it'll get a tad bit tougher.",
     'This next dummy bot will be walking around.',
     'This will test your aiming skills.',
     "Click your mouse when you're ready."]
    GUIDE_PT3_INFO = ['This final dummy bot will walk around and try to attack you at times.', 'Defeat this Cog, and you should be ready to go.']
    GUIDE_DONE = ['Wow, you did great!', "You're definitely ready for battle in CogTropolis.", 'Click your mouse to head to OToontown.']
    GUIDE_START_TRAINING = "Alright! Let's do this!"

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM.ClassicFSM('TutorialFSM', [State.State('off', self.enterOff, self.exitOff),
         State.State('newPlayerEmerge', self.enterPlayerEmerge, self.exitPlayerEmerge, ['off', 'introSpeech']),
         State.State('introSpeech', self.enterGuideIntroSpeech, self.exitGuideIntroSpeech, ['off', 'introSpeech2Training']),
         State.State('introSpeech2Training', self.enterIntroSpeech2Training, self.exitIntroSpeech2Training, ['off', 'training1']),
         State.State('training1', self.enterTrainingPT1, self.exitTrainingPT1, ['off', 'training2info']),
         State.State('training2info', self.enterTraining2Info, self.exitTraining2Info, ['off', 'training2']),
         State.State('training2', self.enterTrainingPT2, self.exitTrainingPT2, ['off', 'training3info']),
         State.State('training3info', self.enterTraining3Info, self.exitTraining3Info, ['off', 'training3']),
         State.State('training3', self.enterTrainingPT3, self.exitTrainingPT3, ['off', 'trainingDone']),
         State.State('trainingDone', self.enterTrainingDone, self.exitTrainingDone, ['off', 'leaveTutorial']),
         State.State('leaveTutorial', self.enterLeaveTutorial, self.exitLeaveTutorial, ['off'])], 'off', 'off')
        self.fsm.enterInitialState()
        self.dnaStore = DNAStorage()
        self.streetGeom = None
        self.sky = None
        self.skyUtil = SkyUtil()
        self.guide = None
        self.music = None
        self.battleMusic = None
        self.playerCamPos = None
        self.playerCamHpr = None
        return

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def introStuff(self):
        base.localAvatar.getGeomNode().hide()
        base.localAvatar.setPos(0, 0, -0.5)
        base.localAvatar.setHpr(0, 0, 0)
        self.guide.setPos(self.GUIDE_START_POS)
        self.guide.headsUp(base.localAvatar)
        base.localAvatar.attachCamera()

    def enterPlayerEmerge(self):
        self.introStuff()
        self.guide.loop('neutral')
        base.transitions.irisIn()
        base.taskMgr.doMethodLater(1.0, self.__playerEmerge, 'playerEmergeTask')

    def __playerEmerge(self, task):
        base.localAvatar.setAnimState('teleportIn', callback=self.__playerEmergeFinished)
        return Task.done

    def __playerEmergeFinished(self):
        base.localAvatar.setAnimState('neutral')
        self.fsm.request('introSpeech')

    def exitPlayerEmerge(self):
        base.localAvatar.detachCamera()
        base.taskMgr.remove('playerEmergeTask')
        base.transitions.noTransitions()

    def enterGuideIntroSpeech(self):
        base.localAvatar.attachCamera()
        renderPos = base.camera.getPos(render)
        renderHpr = base.camera.getHpr(render)
        base.localAvatar.detachCamera()
        endPos = base.localAvatar.getPos(render) + (0, 0, 4)
        base.camera.setPos(endPos)
        base.camera.lookAt(self.guide, 0, 0, 3)
        endHpr = base.camera.getHpr(render)
        base.camera.setPos(renderPos)
        base.camera.setHpr(renderHpr)
        self.chatIndex = -1
        self.doNextIntroSpeech()
        self.camMoveTrack = Sequence(Parallel(LerpPosInterval(base.camera, duration=3.0, pos=endPos, startPos=renderPos, blendType='easeOut'), LerpQuatInterval(base.camera, duration=3.0, hpr=endHpr, startHpr=renderHpr, blendType='easeOut')), Func(base.localAvatar.getGeomNode().hide))
        self.camMoveTrack.start()

    def __finishedReadingGuideIntroSpeech(self):
        self.guide.autoClearChat = True
        self.guide.setChat(self.GUIDE_START_TRAINING)
        self.fsm.request('introSpeech2Training')

    def doNextIntroSpeech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_INTRO_SPEECH):
            self.__finishedReadingGuideIntroSpeech()
            return
        self.guide.setChat(self.GUIDE_INTRO_SPEECH[self.chatIndex])
        Sequence(Wait(0.1), Func(self.acceptOnce, 'mouse1-up', self.doNextIntroSpeech)).start()

    def exitGuideIntroSpeech(self):
        self.camMoveTrack.finish()
        base.localAvatar.getGeomNode().show()
        del self.camMoveTrack
        del self.chatIndex

    def enterIntroSpeech2Training(self):
        startCamPos = base.camera.getPos(render)
        startCamHpr = base.camera.getHpr(render)
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        base.localAvatar.attachCamera()
        endCamPos = base.camera.getPos(render)
        endCamHpr = base.camera.getHpr(render)
        base.localAvatar.detachCamera()
        startHpr = self.guide.getHpr(render)
        self.guide.headsUp(self.GUIDE_WATCH_POS)
        endHpr = self.guide.getHpr(render)
        self.guide.loop('run')
        self.camMoveIval = Parallel(LerpPosInterval(base.camera, duration=2.0, pos=endCamPos, startPos=startCamPos, blendType='easeOut'), LerpQuatInterval(base.camera, duration=2.0, hpr=endCamHpr, startHpr=startCamHpr, blendType='easeOut'), Sequence(LerpPosInterval(self.guide, duration=2.0, pos=self.GUIDE_WATCH_POS, startPos=self.guide.getPos(render)), Func(self.guide.loop, 'walk'), LerpHprInterval(self.guide, duration=1.0, hpr=self.GUIDE_WATCH_HPR, startHpr=endHpr), Func(self.guide.loop, 'neutral')), LerpHprInterval(self.guide, duration=1.0, hpr=endHpr, startHpr=startHpr))
        self.camMoveIval.setDoneEvent('introSpeech2TrainingDone')
        self.acceptOnce('introSpeech2TrainingDone', self.__handleIS2TDone)
        self.camMoveIval.start()

    def __handleIS2TDone(self):
        self.fsm.request('training1')

    def exitIntroSpeech2Training(self):
        self.ignore('introSpeech2TrainingDone')
        self.camMoveIval.finish()
        del self.camMoveIval

    def enterTrainingPT1(self):
        self.music.stop()
        base.playMusic(self.battleMusic, volume=0.8, looping=1)
        self.sendUpdate('makeSuit', [0])
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.startBlink()
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.collisionsOn()
        base.localAvatar.enableAvatarControls()
        base.localAvatar.enablePies(1)
        Whisper().createSystemMessage('This should be pretty simple. Just throw a gag at this dummy bot to defeat it.')

    def suitNoHealth(self, index):
        if index == 0:
            Whisper().createSystemMessage('Good job, {0}!'.format(base.localAvatar.getName()))
        elif index == 1:
            Whisper().createSystemMessage("Wow, you're doing very well!")

    def suitExploded(self, index):
        if index == 0:
            Whisper().createSystemMessage('Pick up the jellybean that he dropped. You can use them to buy more gags for your Toon.')
        self.battleMusic.stop()
        base.playMusic(self.music, looping=1, volume=0.8)

    def pickedUpJellybean(self):
        if self.fsm.getCurrentState().getName() == 'training1':
            self.fsm.request('training2info')
        elif self.fsm.getCurrentState().getName() == 'training2':
            self.fsm.request('training3info')
        elif self.fsm.getCurrentState().getName() == 'training3':
            self.fsm.request('trainingDone')

    def exitTrainingPT1(self):
        base.localAvatar.lastState = None
        base.localAvatar.disableAvatarControls()
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.stopBlink()
        base.localAvatar.collisionsOff()
        base.localAvatar.controlManager.placeOnFloor()
        base.localAvatar.disablePies()
        return

    def enterTraining2Info(self):
        base.camera.setPos(3.09, 37.16, 3.93)
        base.camera.setHpr(225, 0, 0)
        self.guide.autoClearChat = False
        self.chatIndex = -1
        self.doNextTraining2Speech()

    def __finishedReadingGuideTraining2Speech(self):
        self.guide.autoClearChat = True
        self.guide.clearChat()
        self.fsm.request('training2')

    def doNextTraining2Speech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_PT2_INFO):
            self.__finishedReadingGuideTraining2Speech()
            return
        self.guide.setChat(self.GUIDE_PT2_INFO[self.chatIndex])
        Sequence(Wait(0.1), Func(self.acceptOnce, 'mouse1-up', self.doNextTraining2Speech)).start()

    def exitTraining2Info(self):
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        del self.chatIndex

    def enterTrainingPT2(self):
        self.music.stop()
        base.playMusic(self.battleMusic, volume=0.8, looping=1)
        self.sendUpdate('makeSuit', [1])
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.startBlink()
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.collisionsOn()
        base.localAvatar.enableAvatarControls()
        base.localAvatar.enablePies(1)

    def exitTrainingPT2(self):
        base.localAvatar.lastState = None
        base.localAvatar.disableAvatarControls()
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.stopBlink()
        base.localAvatar.collisionsOff()
        base.localAvatar.controlManager.placeOnFloor()
        base.localAvatar.disablePies()
        return

    def enterTraining3Info(self):
        base.camera.setPos(3.09, 37.16, 3.93)
        base.camera.setHpr(225, 0, 0)
        self.guide.autoClearChat = False
        self.chatIndex = -1
        self.doNextTraining3Speech()

    def __finishedReadingGuideTraining3Speech(self):
        self.guide.autoClearChat = True
        self.guide.clearChat()
        self.fsm.request('training3')

    def doNextTraining3Speech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_PT3_INFO):
            self.__finishedReadingGuideTraining3Speech()
            return
        self.guide.setChat(self.GUIDE_PT3_INFO[self.chatIndex])
        Sequence(Wait(0.1), Func(self.acceptOnce, 'mouse1-up', self.doNextTraining3Speech)).start()

    def exitTraining3Info(self):
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        del self.chatIndex

    def enterTrainingPT3(self):
        self.music.stop()
        base.playMusic(self.battleMusic, volume=0.8, looping=1)
        self.sendUpdate('makeSuit', [2])
        base.localAvatar.startPosHprBroadcast()
        base.localAvatar.d_broadcastPositionNow()
        base.localAvatar.startBlink()
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.collisionsOn()
        base.localAvatar.enableAvatarControls()
        base.localAvatar.enablePies(1)

    def exitTrainingPT3(self):
        base.localAvatar.lastState = None
        base.localAvatar.disableAvatarControls()
        base.localAvatar.detachCamera()
        base.localAvatar.stopSmartCamera()
        base.localAvatar.stopPosHprBroadcast()
        base.localAvatar.stopBlink()
        base.localAvatar.collisionsOff()
        base.localAvatar.controlManager.placeOnFloor()
        base.localAvatar.disablePies()
        return

    def enterTrainingDone(self):
        base.camera.setPos(3.09, 37.16, 3.93)
        base.camera.setHpr(225, 0, 0)
        self.guide.autoClearChat = False
        self.chatIndex = -1
        self.doNextTrainingDoneSpeech()

    def __finishedReadingGuideTrainingDoneSpeech(self):
        self.guide.autoClearChat = True
        self.guide.clearChat()
        self.fsm.request('leaveTutorial')

    def doNextTrainingDoneSpeech(self):
        self.chatIndex += 1
        if self.chatIndex >= len(self.GUIDE_DONE):
            self.__finishedReadingGuideTrainingDoneSpeech()
            return
        self.guide.setChat(self.GUIDE_DONE[self.chatIndex])
        Sequence(Wait(0.1), Func(self.acceptOnce, 'mouse1-up', self.doNextTrainingDoneSpeech)).start()

    def exitTrainingDone(self):
        base.camera.setPosHpr(0, 0, 0, 0, 0, 0)
        del self.chatIndex

    def enterLeaveTutorial(self):
        base.localAvatar.attachCamera()
        base.localAvatar.startSmartCamera()
        base.localAvatar.b_setAnimState('teleportOut', callback=self.__teleOutDone)

    def __teleOutDone(self):
        zoneId = CIGlobals.ToontownCentralId
        hoodId = CIGlobals.ToontownCentral
        whereName = 'playground'
        avId = base.localAvatar.doId
        loaderName = 'safeZoneLoader'
        self.sendUpdate('finishedTutorial')
        self.cr.playGame.fsm.request('quietZone', [{'zoneId': zoneId,
          'hoodId': hoodId,
          'where': whereName,
          'how': 'teleportIn',
          'avId': avId,
          'shardId': None,
          'loader': loaderName}])
        return

    def exitLeaveTutorial(self):
        base.localAvatar.stopSmartCamera()
        base.localAvatar.detachCamera()

    def announceGenerate(self):
        DistributedObject.announceGenerate(self)
        base.transitions.fadeScreen(0.0)
        self.guide = Toon(base.cr)
        self.guide.autoClearChat = False
        self.guide.parseDNAStrand(NPCGlobals.NPC_DNA[self.GUIDE_NAME])
        self.guide.setName(self.GUIDE_NAME)
        self.guide.generateToon()
        self.guide.startBlink()
        self.guide.reparentTo(render)
        base.localAvatar.reparentTo(render)
        loader.loadDNAFile(self.dnaStore, 'phase_3.5/dna/storage_tutorial.dna')
        node = loader.loadDNAFile(self.dnaStore, 'phase_3.5/dna/tutorial_street.dna')
        if node.getNumParents() == 1:
            self.streetGeom = NodePath(node.getParent(0))
            self.streetGeom.reparentTo(hidden)
        else:
            self.streetGeom = hidden.attachNewNode(node)
        self.streetGeom.flattenMedium()
        gsg = base.win.getGsg()
        if gsg:
            self.streetGeom.prepareScene(gsg)
        self.streetGeom.reparentTo(render)
        self.streetGeom.setPos(20.5, -20, 0)
        self.streetGeom.setH(90)
        self.sky = loader.loadModel('phase_3.5/models/props/TT_sky.bam')
        self.skyUtil.startSky(self.sky)
        self.sky.reparentTo(camera)
        ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ)
        self.sky.node().setEffect(ce)
        self.music = base.loadMusic('phase_3.5/audio/bgm/TC_SZ.mid')
        base.playMusic(self.music, volume=0.8, looping=1)
        self.battleMusic = base.loadMusic('phase_3.5/audio/bgm/encntr_general_bg.mid')
        self.fsm.request('newPlayerEmerge')
        base.localAvatar.inTutorial = True

    def disable(self):
        self.fsm.requestFinalState()
        del self.fsm
        if self.guide:
            self.guide.disable()
            self.guide.delete()
            self.guide = None
        if self.streetGeom:
            self.streetGeom.removeNode()
            self.streetGeom = None
        if self.sky:
            self.sky.removeNode()
            self.sky = None
        if self.music:
            self.music.stop()
            self.music = None
        if self.battleMusic:
            self.battleMusic.stop()
            self.battleMusic = None
        self.dnaStore = None
        self.skyUtil = None
        base.localAvatar.inTutorial = False
        DistributedObject.disable(self)
        return