예제 #1
0
class AvChooser(StateData):
    notify = directNotify.newCategory('AvChooser')

    def __init__(self, parentFSM):
        StateData.__init__(self, 'avChooseDone')
        self.avChooseFSM = ClassicFSM('avChoose', [
            State('getToonData', self.enterGetToonData, self.exitGetToonData),
            State('avChoose', self.enterAvChoose, self.exitAvChoose),
            State('waitForToonDelResponse', self.enterWaitForToonDelResponse,
                  self.exitWaitForToonDelResponse),
            State('off', self.enterOff, self.exitOff)
        ], 'off', 'off')
        self.avChooseFSM.enterInitialState()
        self.parentFSM = parentFSM
        self.parentFSM.getStateNamed('avChoose').addChild(self.avChooseFSM)
        self.pickAToon = None
        self.setAvatarsNone()
        return

    def enter(self):
        StateData.enter(self)
        base.transitions.noTransitions()
        self.avChooseFSM.request('getToonData')

    def exit(self):
        StateData.exit(self)
        self.setAvatarsNone()
        self.avChooseFSM.requestFinalState()

    def setAvatarsNone(self):
        self.avChoices = []

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterGetToonData(self):
        self.acceptOnce(base.cr.csm.getSetAvatarsEvent(), self.handleToonData)
        base.cr.csm.d_requestAvatars()

    def handleToonData(self, avatarList):
        for av in avatarList:
            avId = av[0]
            dna = av[1]
            name = av[2]
            slot = av[3]
            choice = AvChoice(dna, name, slot, avId)
            self.avChoices.append(choice)

        self.avChooseFSM.request('avChoose')

    def exitGetToonData(self):
        self.ignore(base.cr.csm.getSetAvatarsEvent())

    def enterAvChoose(self):
        if base.cr.holidayManager.getHoliday() == HolidayType.CHRISTMAS:
            base.cr.music.stop()
            base.cr.music = base.loadMusic(CIGlobals.getHolidayTheme())
            base.cr.music.setLoop(True)
            base.cr.music.setVolume(0.75)
            base.cr.music.play()
            whisper = WhisperPopup(HolidayGlobals.CHRISTMAS_TIME,
                                   CIGlobals.getToonFont(),
                                   ChatGlobals.WTSystem)
            whisper.manage(base.marginManager)
        self.pickAToon = CharSelection(self)
        self.pickAToon.load()

    def enterWaitForToonDelResponse(self, avId):
        self.acceptOnce(base.cr.csm.getToonDeletedEvent(),
                        self.handleDeleteToonResp)
        base.cr.csm.sendDeleteToon(avId)

    def exitWaitForToonDelResponse(self):
        self.ignore(base.cr.csm.getToonDeletedEvent())

    def hasToonInSlot(self, slot):
        if self.getAvChoiceBySlot(slot) != None:
            return True
        return False
        return

    def getNameInSlot(self, slot):
        return self.getAvChoiceBySlot(slot).getName()

    def getNameFromAvId(self, avId):
        for avChoice in self.avChoices:
            if avChoice.getAvId() == avId:
                return avChoice.getName()

    def getAvChoiceBySlot(self, slot):
        for avChoice in self.avChoices:
            if avChoice.getSlot() == slot:
                return avChoice

        return

    def getHeadInfo(self, slot):
        dna = self.getAvChoiceBySlot(slot).getDNA()
        self.pickAToon.dna.setDNAStrand(dna)
        return [
            self.pickAToon.dna.getGender(),
            self.pickAToon.dna.getAnimal(),
            self.pickAToon.dna.getHead(),
            self.pickAToon.dna.getHeadColor()
        ]

    def handleDeleteToonResp(self):
        base.cr.loginFSM.request('avChoose')

    def exitAvChoose(self):
        self.pickAToon.unload()
        self.pickAToon = None
        return
예제 #2
0
class TownLoader(StateData):
    notify = directNotify.newCategory('TownLoader')

    def __init__(self, hood, parentFSMState, doneEvent):
        self.hood = hood
        self.parentFSMState = parentFSMState
        StateData.__init__(self, doneEvent)
        self.fsm = ClassicFSM('TownLoader', [State('start', self.enterStart, self.exitStart, ['quietZone', 'street']),
         State('street', self.enterStreet, self.exitStreet, ['quietZone']),
         State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']),
         State('quietZone', self.enterQuietZone, self.exitQuietZone, ['street', 'toonInterior']),
         State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final')
        self.branchZone = None
        self.canonicalBranchZone = None
        self.placeDoneEvent = 'placeDone'
        self.linkTunnels = []
        return

    def findAndMakeLinkTunnels(self, requestStatus):
        for tunnel in self.geom.findAllMatches('**/*linktunnel*'):
            dnaRootStr = tunnel.getName()
            zone = LinkTunnel.getZoneFromDNARootStr(dnaRootStr)
            zone = LinkTunnel.maybeFixZone(zone)
            tunnelClass = LinkTunnel.getRecommendedTunnelClassFromZone(zone)
            link = tunnelClass(tunnel, dnaRootStr)
            self.linkTunnels.append(link)

    def load(self, zoneId):
        StateData.load(self)
        self.zoneId = zoneId
        self.branchZone = ZoneUtil.getBranchZone(zoneId)
        self.canonicalBranchZone = ZoneUtil.getCanonicalBranchZone(zoneId)
        self.music = base.loadMusic(self.musicFile)
        self.interiorMusic = base.loadMusic(self.interiorMusicFile)

    def unload(self):
        self.parentFSMState.removeChild(self.fsm)
        del self.parentFSMState
        del self.fsm
        del self.streetClass
        self.landmarkBlocks.removeNode()
        del self.landmarkBlocks
        self.hood.dnaStore.resetSuitPoints()
        self.hood.dnaStore.resetBattleCells()
        del self.hood
        del self.nodeDict
        del self.zoneDict
        del self.fadeInDict
        del self.fadeOutDict
        del self.nodeList
        self.geom.removeNode()
        del self.geom
        del self.music
        del self.interiorMusic
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        StateData.unload(self)

    def enter(self, requestStatus):
        StateData.enter(self)
        self.findAndMakeLinkTunnels(requestStatus)
        self.fsm.enterInitialState()
        self.setState(requestStatus['where'], requestStatus)

    def exit(self):
        self.fsm.requestFinalState()
        self.ignoreAll()
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        StateData.exit(self)

    def setState(self, state, requestStatus):
        self.fsm.request(state, [requestStatus])

    def enterStart(self):
        pass

    def exitStart(self):
        pass

    def enterStreet(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.streetDone)
        self.place = self.streetClass(self, self.fsm, self.placeDoneEvent)
        self.place.load()

    def exitStreet(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def streetDone(self):
        self.requestStatus = self.place.doneStatus
        status = self.place.doneStatus
        if status['loader'] == 'townLoader' and ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone and status['shardId'] == None or status['how'] == 'doorOut':
            self.fsm.request('quietZone', [status])
        else:
            self.doneStatus = status
            messenger.send(self.doneEvent)
        return

    def enterToonInterior(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone)
        self.place = ToonInterior.ToonInterior(self, self.fsm, self.placeDoneEvent)
        self.place.load()

    def exitToonInterior(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def enterThePlace(self, requestStatus):
        base.cr.playGame.setPlace(self.place)
        self.place.enter(requestStatus)

    def handleToonInteriorDone(self):
        status = self.place.doneStatus
        if status['loader'] == 'townLoader' and ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone and status['shardId'] == None or status['how'] == 'doorOut':
            self.fsm.request('quietZone', [status])
        else:
            self.doneStatus = status
            messenger.send(self.doneEvent)
        return

    def enterQuietZone(self, requestStatus):
        self.fsm.request(requestStatus['where'], [requestStatus], exitCurrent=0)
        self.quietZoneDoneEvent = uniqueName('quietZoneDone')
        self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone)
        self.quietZoneStateData = QuietZoneState(self.quietZoneDoneEvent)
        self.quietZoneStateData.load()
        self.quietZoneStateData.enter(requestStatus)

    def exitQuietZone(self):
        self.ignore(self.quietZoneDoneEvent)
        del self.quietZoneDoneEvent
        self.quietZoneStateData.exit()
        self.quietZoneStateData.unload()
        self.quietZoneStateData = None
        return

    def handleQuietZoneDone(self):
        status = self.quietZoneStateData.getRequestStatus()
        self.exitQuietZone()
        self.enterThePlace(status)

    def enterFinal(self):
        pass

    def exitFinal(self):
        pass

    def createHood(self, dnaFile, loadStorage = 1):
        if loadStorage:
            loader.loadDNAFile(self.hood.dnaStore, 'phase_5/dna/storage_town.dna')
            loader.loadDNAFile(self.hood.dnaStore, self.townStorageDNAFile)
        node = loader.loadDNAFile(self.hood.dnaStore, dnaFile)
        if node.getNumParents() == 1:
            self.geom = NodePath(node.getParent(0))
            self.geom.reparentTo(hidden)
        else:
            self.geom = hidden.attachNewNode(node)
        self.makeDictionaries(self.hood.dnaStore)
        self.reparentLandmarkBlockNodes()
        self.renameFloorPolys(self.nodeList)
        gsg = base.win.getGsg()
        if gsg:
            self.geom.prepareScene(gsg)
        self.geom.flattenLight()
        self.geom.setName('town_top_level')

    def reparentLandmarkBlockNodes(self):
        bucket = self.landmarkBlocks = hidden.attachNewNode('landmarkBlocks')
        npc = self.geom.findAllMatches('**/sb*:*_landmark_*_DNARoot')
        for i in xrange(npc.getNumPaths()):
            nodePath = npc.getPath(i)
            nodePath.wrtReparentTo(bucket)

        npc = self.geom.findAllMatches('**/sb*:*animated_building*_DNARoot')
        for i in xrange(npc.getNumPaths()):
            nodePath = npc.getPath(i)
            nodePath.wrtReparentTo(bucket)

    def makeDictionaries(self, dnaStore):
        self.nodeDict = {}
        self.zoneDict = {}
        self.zoneVisDict = {}
        self.nodeList = []
        self.fadeInDict = {}
        self.fadeOutDict = {}
        a1 = Vec4(1, 1, 1, 1)
        a0 = Vec4(1, 1, 1, 0)
        numVisGroups = dnaStore.getNumDNAVisGroupsAI()
        for i in xrange(numVisGroups):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            visGroup = dnaStore.getDNAVisGroupAI(i)
            groupName = base.cr.hoodMgr.extractGroupName(groupFullName)
            zoneId = int(groupName)
            zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
            groupNode = self.geom.find('**/' + groupFullName)
            if groupNode.isEmpty():
                continue
            else:
                if ':' in groupName:
                    groupName = '%s%s' % (zoneId, groupName[groupName.index(':'):])
                else:
                    groupName = '%s' % zoneId
                groupNode.setName(groupName)
            groupNode.flattenMedium()
            self.nodeDict[zoneId] = []
            self.nodeList.append(groupNode)
            self.zoneDict[zoneId] = groupNode
            visibles = []
            for i in xrange(visGroup.getNumVisibles()):
                visibles.append(int(visGroup.visibles[i]))

            visibles.append(ZoneUtil.getBranchZone(zoneId))
            self.zoneVisDict[zoneId] = visibles
            fadeDuration = 0.5
            self.fadeOutDict[groupNode] = Sequence(Func(groupNode.setTransparency, 1), LerpColorScaleInterval(groupNode, fadeDuration, a0, startColorScale=a1), Func(groupNode.clearColorScale), Func(groupNode.clearTransparency), Func(groupNode.stash), name='fadeZone-' + str(zoneId), autoPause=1)
            self.fadeInDict[groupNode] = Sequence(Func(groupNode.unstash), Func(groupNode.setTransparency, 1), LerpColorScaleInterval(groupNode, fadeDuration, a1, startColorScale=a0), Func(groupNode.clearColorScale), Func(groupNode.clearTransparency), name='fadeZone-' + str(zoneId), autoPause=1)

        for i in xrange(numVisGroups):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            zoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName))
            zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
            for j in xrange(dnaStore.getNumVisiblesInDNAVisGroup(i)):
                visName = dnaStore.getVisibleName(i, j)
                groupName = base.cr.hoodMgr.extractGroupName(visName)
                nextZoneId = int(groupName)
                nextZoneId = ZoneUtil.getTrueZoneId(nextZoneId, self.zoneId)
                visNode = self.zoneDict[nextZoneId]
                self.nodeDict[zoneId].append(visNode)

        self.hood.dnaStore.resetPlaceNodes()
        self.hood.dnaStore.resetDNAGroups()
        self.hood.dnaStore.resetDNAVisGroups()
        self.hood.dnaStore.resetDNAVisGroupsAI()

    def renameFloorPolys(self, nodeList):
        for i in nodeList:
            collNodePaths = i.findAllMatches('**/+CollisionNode')
            numCollNodePaths = collNodePaths.getNumPaths()
            visGroupName = i.node().getName()
            for j in xrange(numCollNodePaths):
                collNodePath = collNodePaths.getPath(j)
                bitMask = collNodePath.node().getIntoCollideMask()
                if bitMask.getBit(1):
                    collNodePath.node().setName(visGroupName)
class DistributedSuit(Suit, DistributedAvatar, DistributedSmoothNode, DelayDeletable):
    notify = directNotify.newCategory("DistributedSuit")

    def __init__(self, cr):
        try:
            self.DistributedSuit_initialized
            return
        except:
            self.DistributedSuit_initialized = 1
        Suit.__init__(self)
        DistributedAvatar.__init__(self, cr)
        DistributedSmoothNode.__init__(self, cr)

        self.suitFSM = ClassicFSM('DistributedSuit',
            [
                State('off', self.enterSuitOff, self.exitSuitOff),
                State('walking', self.enterWalking, self.exitWalking),
                State('flyingDown', self.enterFlyingDown, self.exitFlyingDown),
                State('flyingUp', self.enterFlyingUp, self.exitFlyingUp),
                State('bossFlying', self.enterBossFlying, self.exitBossFlying)
            ],
            'off', 'off'
        )
        self.suitFSM.enterInitialState()
        self.makeStateDict()
        self.makeAnimStateDict()

        # These are just default values, we'll set them later on.
        self.anim = None
        self.state = "alive"
        self.health = None
        self.type = None
        self.team = None
        self.head = None
        self.skeleton = 0
        self.battle = None
        self.suitState = None
        self.startPoint = None
        self.endPoint = None
        self.moveIval = None
        self.walkPaused = None
        self.animIval = None
        self.level = None
        return

    def d_disableMovement(self, wantRay = False):
        self.sendUpdate('disableMovement', [])
        self.interruptAttack()
        if not wantRay:
            Suit.disableRay(self)

    def d_enableMovement(self):
        self.sendUpdate('enableMovement', [])
        Suit.initializeRay(self, self.avatarType, 2)

    def startRay(self):
        Suit.initializeRay(self, self.avatarType, 2)

    def setLevel(self, level):
        self.level = level

    def getLevel(self):
        return self.level

    def makeStateDict(self):
        self.suitState2stateIndex = {}
        for state in self.suitFSM.getStates():
            self.suitState2stateIndex[state.getName()] = self.suitFSM.getStates().index(state)
        self.stateIndex2suitState = {v: k for k, v in self.suitState2stateIndex.items()}

    def makeAnimStateDict(self):
        self.animState2animId = {}
        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()}

    def setLatePos(self, x, y):
        self.setX(x)
        self.setY(y)

    def enterAttack(self, attack, target, ts = 0):
        Suit.enterAttack(self, attack, target, ts)
        if target:
            self.headsUp(target)

    def setSuitState(self, index, startPoint, endPoint, timestamp = None):
        if timestamp != None:
            ts = globalClockDelta.localElapsedTime(timestamp)
        else:
            ts = 0.0

        self.suitState = self.stateIndex2suitState[index]
        self.startPoint = startPoint
        self.endPoint = endPoint

        self.suitFSM.request(self.suitState, [startPoint, endPoint, ts])

    def getSuitState(self):
        return self.suitState

    def enterWalking(self, startIndex, endIndex, ts = 0.0):
        durationFactor = 0.2
        if startIndex > -1:
            startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
            startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint]
        else:
            startPos = self.getPos(render)
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]

        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None

        self.moveIval = NPCWalkInterval(self, endPos, durationFactor, startPos, fluid = 1)
        self.moveIval.start(ts)
        self.animFSM.request('walk')

    def exitWalking(self):
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        if not self.isDead():
            self.animFSM.request('off')

    def enterFlyingDown(self, startIndex, endIndex, ts = 0.0):
        duration = 3
        startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
        startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint] + (0, 0, 50)
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.moveIval = LerpPosInterval(self, duration = duration, pos = endPos, startPos = startPos, fluid = 1)
        self.moveIval.start(ts)
        self.animFSM.request('flydown', [ts])
        yaw = random.uniform(0.0, 360.0)
        self.setH(yaw)

    def exitFlyingDown(self):
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.animFSM.request('off')

    def enterFlyingUp(self, startIndex, endIndex, ts = 0.0):
        duration = 3
        startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint] + (0, 0, 50)

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

        self.moveIval = LerpPosInterval(self, duration = duration, pos = endPos, startPos = startPos, fluid = 1)
        self.moveIval.start(ts)
        self.animFSM.request('flyaway', [ts])

    def exitFlyingUp(self):
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.animFSM.request('off')

    def enterBossFlying(self, startIndex, endIndex, ts = 0.0):
        duration = 3.5
        startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]

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

        self.animIval = Sequence(
            Func(self.animFSM.request, 'flyaway', [ts]),
            Wait(1.0),
            Func(self.animFSM.request, 'flydown', [ts])
        )

        self.moveIval = Sequence(
            Wait(0.5),
            Func(self.headsUp, endPos),
            ProjectileInterval(
                self,
                startPos = startPos,
                endPos = endPos,
                gravityMult = 0.25,
                duration = duration
            )
        )
        self.moveIval.start(ts)
        self.animIval.start(ts)

    def exitBossFlying(self):
        if self.animIval:
            self.animIval.finish()
            self.animIval = None
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.animFSM.request('off')

    def enterSuitOff(self, foo1 = None, foo2 = None, foo3 = None):
        pass

    def exitSuitOff(self):
        pass

    #def setWalking(self, value, startPos, endPath, timestamp = None):
    #
    #
    #	self.value = value
    #	self.startPath = startPath
    #	self.endPath = endPath
    #
    #	if self.walkIval:
    #		self.walkIval.finish()
    #		self.walkIval = None
    #
    #	if value:
    #		durationFactor = 0.2
    #		path = CIGlobals.SuitSpawnPoints.values()[endPath]
    #		self.walkIval = NPCWalkInterval(self, path, startPos = self.getPos(render), name = pathName, durationFactor = durationFactor, fluid = 1)

    def setBattle(self, battle):
        self.battle = battle

    def getBattle(self):
        return self.battle

    def printPos(self, task):
        print self.getPos(render)
        print self.getHpr(render)
        return task.cont

    def announceHealth(self, level, hp):
        DistributedAvatar.announceHealth(self, level, hp)
        if level == 1:
            healthSfx = base.audio3d.loadSfx("phase_3/audio/sfx/health.mp3")
            base.audio3d.attachSoundToObject(healthSfx, self)
            SoundInterval(healthSfx).start()
            del healthSfx

    def setSuit(self, suitType, head, team, skeleton):
        for obj in self.cr.doId2do.values():
            if obj.zoneId == self.zoneId:
                if obj.__class__.__name__ == "DistributedCogBattle":
                    # This has to be the Cog Battle we're in because it's in the same zone.
                    self.setBattle(obj)
        hp = CIGlobals.SuitHealthAmounts[head]
        Suit.generateSuit(self, suitType, head, team, hp, skeleton)

    def getSuit(self):
        return tuple((self.type, self.head, self.team, self.skeleton))

    def setName(self, name):
        Suit.setName(self, name, self.head)

    def setHealth(self, health):
        DistributedAvatar.setHealth(self, health)
        self.updateHealthBar(health)

    def setAnimState(self, anim, timestamp = None):
        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])

    def doAttack(self, attackId, avId, timestamp = None):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        attackName = SuitAttacks.SuitAttackLengths.keys()[attackId]
        avatar = self.cr.doId2do.get(avId)
        self.animFSM.request('attack', [attackName, avatar, ts])

    def throwObject(self):
        self.acceptOnce("enter" + self.wsnp.node().getName(), self.__handleWeaponCollision)
        Suit.throwObject(self)

    def __handleWeaponCollision(self, entry):
        self.sendUpdate('toonHitByWeapon', [self.attack, base.localAvatar.doId])
        base.localAvatar.handleHitByWeapon(self.attack, self)
        self.b_handleWeaponTouch()

    def b_handleWeaponTouch(self):
        self.sendUpdate('handleWeaponTouch', [])
        self.handleWeaponTouch()

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

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

    def disable(self):
        if self.suitTrack != None:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.stopSmooth()
        self.suitFSM.requestFinalState()
        self.suitFSM = None
        self.suitState2stateIndex = None
        self.stateIndex2suitState = None
        self.anim = None
        self.state = None
        self.health = None
        self.type = None
        self.team = None
        self.head = None
        self.skeleton = None
        self.battle = None
        Suit.disable(self)
        DistributedAvatar.disable(self)

    def delete(self):
        Suit.delete(self)
        DistributedAvatar.delete(self)
        DistributedSmoothNode.delete(self)
예제 #4
0
class DistributedMinigame(DistributedObject.DistributedObject, Timer.Timer):
    def __init__(self, cr):
        try:
            self.DistributedMinigame_initialized
            return
        except:
            self.DistributedMinigame_initialized = 1

        DistributedObject.DistributedObject.__init__(self, cr)
        Timer.Timer.__init__(self)
        self.headPanels = HeadPanels()
        self.finalScoreUI = FinalScoreGUI()
        self.fsm = ClassicFSM('DistributedMinigame', [
            State('start', self.enterStart, self.exitStart, ['waitForOthers']),
            State('waitForOthers', self.enterWaitForOthers,
                  self.exitWaitForOthers, ['play']),
            State('play', self.enterPlay, self.exitPlay, ['gameOver']),
            State('gameOver', self.enterGameOver, self.exitGameOver, ['off']),
            State('off', self.enterOff, self.exitOff)
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.cr = cr
        self.localAv = base.localAvatar
        self.localAvId = self.localAv.doId
        self.musicPath = 'phase_4/audio/bgm/trolley_song.mid'
        self.winSfx = base.loadSfx('phase_4/audio/sfx/MG_win.ogg')
        self.loseSfx = base.loadSfx('phase_4/audio/sfx/MG_lose.ogg')
        self.prizeHigh = base.loadSfx('phase_6/audio/sfx/KART_Applause_1.ogg')
        self.prizeLow = base.loadSfx('phase_6/audio/sfx/KART_Applause_4.ogg')
        self.music = None
        self.description = ''
        self.descDialog = None
        self.winnerPrize = 0
        self.loserPrize = 0
        self.winnerMsg = 'Winner!\nYou have earned: %s Jellybeans'
        self.loserMsg = 'Loser!\nYou have earned: %s Jellybeans'
        self.allWinnerMsgs = [
            'Nice try!\nYou have earned: %s', 'Good job!\nYou have earned: %s',
            'Way to go!\nYou have earned: %s', 'Awesome!\nYou have earned: %s'
        ]
        self.timer = None
        self.timeLbl = None
        self.alertText = None
        self.alertPulse = None
        self.popupSound = None
        self.gameOverLbl = OnscreenText(text="TIME'S\nUP!",
                                        scale=0.25,
                                        font=CIGlobals.getMickeyFont(),
                                        fg=(1, 0, 0, 1))
        self.gameOverLbl.setBin('gui-popup', 60)
        self.gameOverLbl.hide()
        return

    def getTeamDNAColor(self, team):
        pass

    def showAlert(self, text):
        self.stopPulse()
        base.playSfx(self.popupSound)
        self.alertText.setText(text)
        self.alertPulse = getAlertPulse(self.alertText)
        self.alertPulse.start()

    def stopPulse(self):
        if self.alertPulse:
            self.alertPulse.finish()
            self.alertPulse = None
        return

    def enterFinalScores(self):
        self.finalScoreUI.load()
        self.finalScoreUI.showFinalScores()

    def exitFinalScores(self):
        self.finalScoreUI.hideFinalScores()
        self.finalScoreUI.unload()

    def finalScores(self, avIdList, scoreList):
        self.finalScoreUI.handleFinalScores(avIdList, scoreList)

    def generateHeadPanel(self, gender, head, headtype, color, doId, name):
        self.headPanels.generate(gender, head, headtype, color, doId, name)

    def updateHeadPanelValue(self, doId, direction):
        self.headPanels.updateValue(doId, direction)

    def setTimerTime(self, time):
        self.setTime(time)

    def createTimer(self):
        Timer.Timer.load(self)

    def deleteTimer(self):
        Timer.Timer.unload(self)

    def setDescription(self, desc):
        self.description = desc

    def getDescription(self):
        return self.description

    def enterStart(self):
        self.descDialog = GlobalDialog(style=3,
                                       message=self.getDescription(),
                                       doneEvent='gameDescAck')
        self.acceptOnce('gameDescAck', self.handleDescAck)

    def handleDescAck(self):
        self.d_ready()
        self.fsm.request('waitForOthers')

    def exitStart(self):
        self.ignore('gameDescAck')
        self.descDialog.cleanup()
        del self.descDialog

    def enterWaitForOthers(self):
        self.waitLbl = DirectLabel(text='Waiting for other players...',
                                   relief=None,
                                   text_fg=(1, 1, 1, 1),
                                   text_scale=0.08,
                                   text_shadow=(0, 0, 0, 1))
        return

    def exitWaitForOthers(self):
        self.waitLbl.destroy()
        del self.waitLbl

    def setLoserPrize(self, prize):
        self.loserPrize = prize

    def setWinnerPrize(self, prize):
        self.winnerPrize = prize

    def getLoserPrize(self):
        return self.loserPrize

    def getWinnerPrize(self):
        return self.winnerPrize

    def winner(self):
        self.winSfx.play()
        self.localAv.b_setAnimState('happy')
        Sequence(Wait(3.5), Func(self.displayGameOver, 'winner')).start()

    def showPrize(self, amt):
        self.winSfx.play()
        self.localAv.b_setAnimState('happy')
        Sequence(Wait(3.5), Func(self.displayGameOver, 'showPrize',
                                 amt)).start()

    def loser(self):
        self.loseSfx.play()
        self.localAv.b_setAnimState('neutral')
        Sequence(Wait(3.5), Func(self.displayGameOver, 'loser')).start()

    def displayGameOver(self, scenario, amt=None):
        if scenario == 'winner':
            msg = self.winnerMsg % self.winnerPrize
            self.prizeHigh.play()
        else:
            if scenario == 'loser':
                msg = self.loserMsg % self.loserPrize
                self.prizeLow.play()
            else:
                if scenario == 'showPrize':
                    msg = random.choice(self.allWinnerMsgs) % amt
                    self.prizeHigh.play()
        self.gameOverDialog = GlobalDialog(message=msg,
                                           style=3,
                                           doneEvent='gameOverAck')
        self.acceptOnce('gameOverAck', self.__handleGameOverAck)
        self.gameOverDialog.show()

    def deleteGameOverDialog(self):
        self.ignore('gameOverAck')
        if hasattr(self, 'gameOverDialog'):
            self.gameOverDialog.cleanup()
            del self.gameOverDialog

    def __handleGameOverAck(self):
        self.fsm.requestFinalState()
        Sequence(Func(base.transitions.irisOut, 1.0), Wait(1.2),
                 Func(self.d_leaving),
                 Func(self.headBackToMinigameArea)).start()

    def headBackToMinigameArea(self):
        whereName = ZoneUtil.getWhereName(CIGlobals.MinigameAreaId)
        loaderName = ZoneUtil.getLoaderName(CIGlobals.MinigameAreaId)
        requestStatus = {
            'zoneId': CIGlobals.MinigameAreaId,
            'hoodId': CIGlobals.MinigameArea,
            'where': whereName,
            'how': 'teleportIn',
            'avId': base.localAvatar.doId,
            'shardId': None,
            'loader': loaderName
        }
        self.cr.playGame.hood.fsm.request('quietZone', [requestStatus])
        return

    def abort(self):
        self.headBackToMinigameArea()

    def load(self, showDesc=True):
        if showDesc:
            self.fsm.request('start')
        base.transitions.irisIn()

    def d_leaving(self):
        self.sendUpdate('leaving', [])

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

    def enterPlay(self):
        self.playMinigameMusic()

    def exitPlay(self):
        self.stopMinigameMusic()

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterGameOver(self, winner, winnerDoId, allPrize):
        if winner:
            if self.localAvId in winnerDoId:
                self.winner()
            else:
                self.loser()
        else:
            self.showPrize(allPrize)

    def exitGameOver(self):
        self.deleteGameOverDialog()

    def gameOver(self, winner=0, winnerDoId=[], allPrize=0):
        self.fsm.request('gameOver', [winner, winnerDoId, allPrize])

    def setMinigameMusic(self, path):
        self.musicPath = path

    def getMinigameMusic(self):
        return self.musicPath

    def playMinigameMusic(self):
        self.stopMinigameMusic()
        self.music = base.loadMusic(self.musicPath)
        self.music.setLoop(True)
        self.music.setVolume(0.8)
        self.music.play()

    def stopMinigameMusic(self):
        if self.music:
            self.music.stop()
            self.music = None
        return

    def d_ready(self):
        self.sendUpdate('ready', [])

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        base.minigame = self
        self.alertText = getAlertText()
        self.popupSound = base.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.ogg')
        NametagGlobals.setWant2dNametags(False)

    def disable(self):
        base.localAvatar.getGeomNode().setColorScale(VBase4(1, 1, 1, 1))
        if hasattr(self, 'gameOverLbl') and self.gameOverLbl:
            self.gameOverLbl.destroy()
        self.gameOverLbl = None
        NametagGlobals.setWant2dNametags(True)
        base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0)
        self.fsm.requestFinalState()
        del self.fsm
        self.winSfx = None
        self.loseSfx = None
        self.prizeHigh = None
        self.prizeLow = None
        self.headPanels.delete()
        self.headPanels = None
        self.finalScoreUI.unload()
        self.finalScoreUI = None
        base.minigame = None
        DistributedObject.DistributedObject.disable(self)
        return
class CameraShyFirstPerson(FirstPerson):
    toonInFocusColor = VBase4(0.25, 1.0, 0.25, 1.0)
    toonOutOfFocusColor = VBase4(1.0, 1.0, 1.0, 1.0)
    fullyChargedState = 5

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

    def enterOff(self):
        pass

    def exitOff(self):
        pass

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

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

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

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

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

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

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

    def __traverse(self, task):
        if not base.mouseWatcherNode.hasMouse():
            return task.cont
        mpos = base.mouseWatcherNode.getMouse()
        self.focusRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
        self.focusTrav.traverse(render)
        if self.focusHandler.getNumEntries() > 0:
            self.focusHandler.sortEntries()
            firstObj = self.focusHandler.getEntry(0).getIntoNodePath()
            avId = firstObj.getParent().getPythonTag('player')
            avatar = self.mg.cr.doId2do.get(avId)
            toonInFoc = False
            if avatar:
                remoteAvatar = self.mg.getRemoteAvatar(avatar.doId)
                if remoteAvatar:
                    toonInFoc = True
                    self.__handleToonInFocus(avatar)
            if not toonInFoc:
                self.toonToTakePicOf = None
                self.hasToonInFocus = False
                if self.cameraFocus.getColorScale() == self.toonInFocusColor:
                    self.cameraFocus.setColorScale(self.toonOutOfFocusColor)
        return task.cont

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

    def start(self):
        self.fullyChargedSound = base.loadSfx(
            'phase_4/audio/sfx/MG_pairing_match.ogg')
        self.rechargeSound = base.loadSfx(
            'phase_4/audio/sfx/MG_sfx_travel_game_blue_arrow.ogg')
        self.batteryFrame = DirectFrame(parent=base.a2dBottomRight,
                                        pos=(-0.2, 0, 0.1),
                                        scale=(0.8, 0, 1))
        self.batteryBg = OnscreenImage(
            image='phase_4/maps/battery_charge_frame.png',
            parent=self.batteryFrame)
        self.batteryBg.setTransparency(1)
        self.batteryBg.setX(0.03)
        self.batteryBg.setScale(0.17, 0, 0.05)
        self.batteryBar = DirectWaitBar(value=0,
                                        range=5,
                                        barColor=(1, 1, 1, 1),
                                        relief=None,
                                        scale=(0.12, 0.0, 0.3),
                                        parent=self.batteryFrame)
        self.cameraFocus = loader.loadModel(
            'phase_4/models/minigames/photo_game_viewfinder.bam')
        self.cameraFocus.reparentTo(base.aspect2d)
        self.focusTrav = CollisionTraverser('CSFP.focusTrav')
        ray = CollisionRay()
        rayNode = CollisionNode('CSFP.rayNode')
        rayNode.addSolid(ray)
        rayNode.setCollideMask(BitMask32(0))
        rayNode.setFromCollideMask(CIGlobals.WallBitmask)
        self.focusRay = ray
        self.focusRayNode = base.camera.attachNewNode(rayNode)
        self.focusHandler = CollisionHandlerQueue()
        self.focusTrav.addCollider(self.focusRayNode, self.focusHandler)
        base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed,
                                                   0.0,
                                                   CIGlobals.ToonReverseSpeed,
                                                   CIGlobals.ToonRotateSpeed)
        FirstPerson.start(self)
        return

    def reallyStart(self):
        taskMgr.add(self.__traverse, 'CSFP.__traverse')
        self.camFSM.request('recharge')
        base.localAvatar.startTrackAnimToSpeed()
        FirstPerson.reallyStart(self)

    def end(self):
        self.camFSM.request('off')
        taskMgr.remove('movementTask')
        taskMgr.remove('CSFP.__traverse')
        FirstPerson.end(self)

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

    def cleanup(self):
        self.camFSM.requestFinalState()
        self.camFSM = None
        FirstPerson.cleanup(self)
        return
예제 #6
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()
예제 #7
0
파일: BRWater.py 프로젝트: coginvasion/src
class BRWater:
    notify = directNotify.newCategory('BRWater')

    def __init__(self, playground):
        self.playground = playground
        self.fsm = ClassicFSM('BRWater', [State('off', self.enterOff, self.exitOff),
         State('freezeUp', self.enterFreezeUp, self.exitFreezeUp),
         State('coolDown', self.enterCoolDown, self.exitCoolDown),
         State('frozen', self.enterFrozen, self.exitFrozen)], 'off', 'off')
        self.fsm.enterInitialState()
        self.freezeUpSfx = base.loadSfx('phase_8/audio/sfx/freeze_up.mp3')
        self.frozenSfxArray = [base.loadSfx('phase_8/audio/sfx/frozen_1.mp3'), base.loadSfx('phase_8/audio/sfx/frozen_2.mp3'), base.loadSfx('phase_8/audio/sfx/frozen_3.mp3')]
        self.coolSfxArray = [base.loadSfx('phase_8/audio/sfx/cool_down_1.mp3'), base.loadSfx('phase_8/audio/sfx/cool_down_2.mp3')]
        self.freezeUpSfx.setVolume(12)
        for sfx in self.frozenSfxArray:
            sfx.setVolume(12)

        for sfx in self.coolSfxArray:
            sfx.setVolume(12)

    def attachSound(self, sound):
        base.localAvatar.audio3d.attachSoundToObject(sound, base.localAvatar)

    def enterOff(self):
        self.playground.startWaterWatch()

    def exitOff(self):
        self.playground.stopWaterWatch()

    def loadIceCube(self):
        self.iceCube = loader.loadModel('phase_8/models/props/icecube.bam')
        for node in self.iceCube.findAllMatches('**/billboard*'):
            node.removeNode()

        for node in self.iceCube.findAllMatches('**/drop_shadow*'):
            node.removeNode()

        for node in self.iceCube.findAllMatches('**/prop_mailboxcollisions*'):
            node.removeNode()

        self.iceCube.reparentTo(base.localAvatar)
        self.iceCube.setScale(1.2, 1.0, base.localAvatar.getHeight() / 1.7)
        self.iceCube.setTransparency(1)
        self.iceCube.setColorScale(0.76, 0.76, 1.0, 0.0)

    def unloadIceCube(self):
        self.iceCube.removeNode()
        del self.iceCube

    def enterFreezeUp(self):
        length = 1.0
        base.playSfx(self.freezeUpSfx)
        self.fucsIval = Sequence(LerpColorScaleInterval(base.localAvatar.getGeomNode(), duration=length, colorScale=VBase4(0.5, 0.5, 1.0, 1.0), startColorScale=base.localAvatar.getGeomNode().getColorScale(), blendType='easeOut'), Func(self.fsm.request, 'frozen'))
        self.fucsIval.start()
        self.playground.startWaterWatch(0)

    def exitFreezeUp(self):
        self.fucsIval.pause()
        del self.fucsIval
        self.playground.stopWaterWatch()

    def enterFrozen(self):
        self.loadIceCube()
        base.cr.playGame.getPlace().fsm.request('stop', [0])
        base.localAvatar.stop()
        base.playSfx(choice(self.frozenSfxArray))
        self.iccsIval = LerpColorScaleInterval(self.iceCube, duration=0.5, colorScale=VBase4(0.76, 0.76, 1.0, 1.0), startColorScale=self.iceCube.getColorScale(), blendType='easeInOut')
        self.iccsIval.start()
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)
        self.frame = DirectFrame(pos=(0, 0, 0.7))
        self.powerBar = DirectWaitBar(frameColor=(1, 1, 1, 1), range=100, value=0, scale=(0.4, 0.5, 0.25), parent=self.frame, barColor=(0.55, 0.7, 1.0, 1.0))
        self.label = OnscreenText(text='SHAKE MOUSE', shadow=(0, 0, 0, 1), fg=(0.55, 0.7, 1.0, 1.0), pos=(0, -0.1, 0), parent=self.frame)
        taskMgr.add(self.__watchMouseMovement, 'BRWater-watchMouseMovement')
        taskMgr.add(self.__lowerPowerBar, 'BRWater-lowerPowerBar')
        mw = base.mouseWatcherNode
        if mw.hasMouse():
            self.lastMouseX = mw.getMouseX()

    def __lowerPowerBar(self, task):
        if self.powerBar['value'] <= 0:
            self.powerBar.update(0)
        decrement = 1
        self.powerBar.update(self.powerBar['value'] - decrement)
        task.delayTime = 0.1
        return task.again

    def __watchMouseMovement(self, task):
        if self.powerBar['value'] >= self.powerBar['range']:
            self.fsm.request('coolDown', [1])
            return task.done
        mw = base.mouseWatcherNode
        if mw.hasMouse():
            if not self.lastMouseX or self.lastMouseX != mw.getMouseX():
                value = 3 * self.lastMouseX - mw.getMouseX()
                self.lastMouseX = mw.getMouseX()
                self.powerBar.update(self.powerBar['value'] + abs(value))
        return task.cont

    def exitFrozen(self):
        props = WindowProperties()
        props.setCursorHidden(False)
        base.win.requestProperties(props)
        self.iccsIval.pause()
        del self.iccsIval
        self.unloadIceCube()
        taskMgr.remove('BRWater-lowerPowerBar')
        taskMgr.remove('BRWater-watchMouseMovement')
        self.label.destroy()
        del self.label
        self.powerBar.destroy()
        del self.powerBar
        self.frame.destroy()
        del self.frame
        del self.lastMouseX
        base.cr.playGame.getPlace().fsm.request('walk')
        base.localAvatar.b_setAnimState('neutral')

    def enterCoolDown(self, fromFrozen = 0):
        if fromFrozen:
            self.loadIceCube()
            self.iceCube.setColorScale(0.76, 0.76, 1.0, 1.0)
            self.iccdIval = LerpColorScaleInterval(self.iceCube, duration=0.5, colorScale=VBase4(0.76, 0.76, 1.0, 0.0), startColorScale=self.iceCube.getColorScale(), blendType='easeInOut')
            self.iccdIval.start()
        length = 1.0
        base.playSfx(choice(self.coolSfxArray))
        self.cdcsIval = Sequence(LerpColorScaleInterval(base.localAvatar.getGeomNode(), duration=length, colorScale=VBase4(1.0, 1.0, 1.0, 1.0), startColorScale=base.localAvatar.getGeomNode().getColorScale(), blendType='easeOut'), Func(self.fsm.request, 'off'))
        self.cdcsIval.start()

    def exitCoolDown(self):
        if hasattr(self, 'iccdIval'):
            self.iccdIval.pause()
            del self.iccdIval
            self.unloadIceCube()
        self.cdcsIval.pause()
        del self.cdcsIval

    def cleanup(self):
        self.fsm.requestFinalState()
        self.playground.stopWaterWatch()
        del self.fsm
        del self.freezeUpSfx
        del self.frozenSfxArray
        del self.coolSfxArray
        del self.playground
예제 #8
0
class RaceGameMovement(DirectObject):
    MINIMUM_POWER = 0.3
    MINIMUM_KEY_DELAY = 0.015
    POWER_FACTOR = 2.5
    defaultBoostBarColor = (0.4, 0.4, 0.7, 1.0)
    fullBoostBarColor = (0.0, 0.0, 0.7, 1.0)

    def __init__(self, avatar):
        DirectObject.__init__(self)
        self.avatar = avatar
        self.power = self.MINIMUM_POWER
        self.boost = 0.0
        self.fsm = ClassicFSM('RaceGameMovement', [State('off', self.enterOff, self.exitOff, ['run']),
         State('run', self.enterRun, self.exitRun, ['fall', 'off']),
         State('fall', self.enterFall, self.exitFall, ['run', 'off']),
         State('final', self.enterFinal, self.exitFinal)], 'off', 'final')
        self.fsm.enterInitialState()
        self.boostFSM = ClassicFSM('Boost', [State('off', self.enterBoostOff, self.exitBoostOff, ['boost']), State('boost', self.enterBoost, self.exitBoost)], 'off', 'off')
        self.boostFSM.enterInitialState()
        self.keysPressed = {'arrow_left': 0,
         'arrow_right': 0}
        self.startTime = 0.0
        self.endTime = 0.0
        self.fallTrack = None
        self.isStopped = True
        self.isBoosting = False
        return

    def setBoosting(self, value):
        self.isBoosting = value

    def getBoosting(self):
        return self.isBoosting

    def enterBoostOff(self):
        self.ignore('control')

    def exitBoostOff(self):
        pass

    def createGui(self):
        """ Create the GUI that will tell the client how much
        running power they have. """
        self.powerFrame = DirectFrame()
        self.powerBg = OnscreenImage(image=DGG.getDefaultDialogGeom(), scale=(0.5, 1.0, 0.5), pos=(1.02, 0, 0.7), parent=self.powerFrame, color=CIGlobals.DialogColor)
        self.powerBar = DirectWaitBar(barColor=(0, 0.7, 0, 1), range=20.0, value=0, parent=self.powerFrame, scale=(0.15, 0, 1.1), pos=(1.02, 0, 0.66))
        self.powerBar.setR(-90)
        self.powerTitle = DirectLabel(text='POWER', text_scale=0.08, pos=(1.02, 0, 0.85), relief=None, parent=self.powerFrame, text_fg=(1, 1, 0, 1), text_font=CIGlobals.getMickeyFont())
        self.boostFrame = DirectFrame()
        self.boostBg = OnscreenImage(image=DGG.getDefaultDialogGeom(), scale=(0.5, 1.0, 0.5), pos=(0.45, 0, 0.7), parent=self.boostFrame, color=CIGlobals.DialogColor)
        self.boostBar = DirectWaitBar(barColor=self.defaultBoostBarColor, range=10, value=0, parent=self.boostFrame, scale=(0.15, 0, 1.1), pos=(0.45, 0, 0.66))
        self.boostBar.setR(-90)
        self.boostTitle = DirectLabel(text='BOOST', text_scale=0.08, pos=(0.45, 0, 0.85), relief=None, parent=self.boostFrame, text_fg=(1, 1, 0, 1), text_font=CIGlobals.getMickeyFont())
        self.boostFullLbl = DirectLabel(text='BOOST READY', text_scale=0.065, pos=(0.45, 0, 0.3), relief=None, parent=self.boostFrame, text_fg=self.fullBoostBarColor, text_shadow=(0.4, 0.4, 0.4, 1.0), text_font=CIGlobals.getToonFont())
        self.boostFullLbl.hide()
        return

    def deleteGui(self):
        """ Delete the GUI that will tell the client how much
        running power they have. """
        self.powerFrame.destroy()
        self.powerBg.destroy()
        self.powerBar.destroy()
        self.powerTitle.destroy()
        self.boostFrame.destroy()
        self.boostBg.destroy()
        self.boostBar.destroy()
        self.boostTitle.destroy()
        self.boostFullLbl.destroy()

    def enableArrowKeys(self):
        """ Enable the arrow keys to increase movement power. """
        self.accept('arrow_left', self.keyPressed, ['arrow_left'])
        self.accept('arrow_right', self.keyPressed, ['arrow_right'])

    def disableArrowKeys(self):
        """ Disable the arrow keys to increase movement power. """
        self.ignore('arrow_left')
        self.ignore('arrow_right')

    def boostKeyPressed(self):
        self.boostFullLbl.hide()
        self.boostFSM.request('boost')

    def keyPressed(self, key):
        """ Figure out which key was pressed and increment the movement power
        if the values of both keys are 1. Also, make the avatar fall down if
        the time between key presses is too fast or the avatar pressed the same
        key more than once."""
        self.stopDelayTimer()
        if self.keysPressed[key] == 1 or self.isTooFast():
            self.resetKeys()
            self.fsm.request('fall')
            return
        self.keysPressed[key] = 1
        if self.keysPressed['arrow_left'] == 1 and self.keysPressed['arrow_right'] == 1:
            self.resetKeys()
            self.changePower()
            self.restartDelayTimer()

    def enterBoost(self):
        self.disableArrowKeys()
        self.resetKeys()
        base.localAvatar.b_setAnimState('swim')
        self.power = 30.0
        taskMgr.add(self.boostTask, 'boostTask')
        self.boostSfx = base.loadSfx('phase_6/audio/sfx/KART_turboLoop.wav')
        base.playSfx(self.boostSfx, looping=1)

    def exitBoost(self):
        self.enableArrowKeys()
        base.localAvatar.b_setAnimState('run')
        self.power = 17.0
        taskMgr.remove('boostTask')
        self.boostSfx.stop()
        del self.boostSfx

    def boostTask(self, task):
        if self.boostBar['value'] <= 0.0:
            self.boostFSM.request('off')
            self.boostBar['barColor'] = self.defaultBoostBarColor
            return task.done
        self.boostBar['value'] -= 0.3
        task.delayTime = 0.05
        return task.again

    def enterFall(self):
        """ The avatar is pressing his arrow keys too fast. Stop running and
        make the avatar fall down. """
        self.avatar.b_setAnimState('fallFWD')
        self.fallTrack = Sequence(Wait(2.5), Func(self.fsm.request, 'run'))
        self.fallTrack.start()

    def exitFall(self):
        self.fallTrack.pause()
        self.fallTrack = None
        return

    def isTooFast(self):
        """ Determine if the delay between key presses in seconds is too low. """
        return self.getDelayTime() < self.MINIMUM_KEY_DELAY

    def resetKeys(self):
        """ Reset the value of the keys. """
        for key in self.keysPressed:
            self.keysPressed[key] = 0

    def changePower(self):
        """ Increase the avatar's movement power. """
        self.power = self.POWER_FACTOR / self.getDelayTime()
        if self.boostBar['barColor'] != self.fullBoostBarColor:
            if self.power >= self.powerBar['range']:
                self.boostBar['value'] += 0.8
                if self.boostBar['value'] >= self.boostBar['range']:
                    self.boostBar['barColor'] = self.fullBoostBarColor
                    self.boostFullLbl.show()
                    self.acceptOnce('control', self.boostKeyPressed)
        print self.power
        self.powerBar.update(self.power)

    def startDelayTimer(self):
        self.startTime = globalClock.getFrameTime()

    def stopDelayTimer(self):
        self.endTime = globalClock.getFrameTime()

    def resetDelayTimer(self):
        self.startTime = 0.0
        self.endTime = 0.0

    def restartDelayTimer(self):
        self.stopDelayTimer()
        self.resetDelayTimer()
        self.startDelayTimer()

    def getDelayTime(self):
        return self.endTime - self.startTime

    def enterRun(self):
        """ Start moving the avatar, make the avatar run, and
        start tracking the delay between key presses. """
        taskMgr.add(self.move, 'move')
        if self.boostBar['barColor'] == self.fullBoostBarColor:
            self.boostFullLbl.show()
            self.acceptOnce('control', self.boostKeyPressed)
        self.startDelayTimer()
        self.enableArrowKeys()
        self.avatar.b_setAnimState('run')
        self.isStopped = False

    def exitRun(self):
        """ Stop moving the avatar, make the avatar stand, and stop
        tracking the delay between key presses. """
        taskMgr.remove('move')
        self.boostFSM.request('off')
        if self.boostBar['barColor'] == self.fullBoostBarColor:
            self.boostFullLbl.hide()
            self.ignore('control')
        self.stopDelayTimer()
        self.resetDelayTimer()
        self.disableArrowKeys()
        self.avatar.b_setAnimState('neutral')
        self.isStopped = True
        self.power = self.MINIMUM_POWER
        self.powerBar.update(0)

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterFinal(self):
        pass

    def exitFinal(self):
        pass

    def decreasePower(self, task):
        """ Decrease power so the avatar does not keep the same amount
        of power while not tapping the arrow keys. """
        if self.power > self.MINIMUM_POWER:
            self.power -= self.POWER_FACTOR / 0.01
            if self.power < self.MINIMUM_POWER:
                self.power = self.MINIMUM_POWER
        task.delayTime = 0.5
        return Task.again

    def move(self, task):
        """ Move the avatar forward on the Y axis with the current amount of power. """
        dt = globalClock.getDt()
        self.avatar.setY(self.avatar.getY() + self.power * dt)
        return Task.cont

    def cleanup(self):
        self.fsm.requestFinalState()
예제 #9
0
class CameraShyFirstPerson(FirstPerson):
    notify = directNotify.newCategory("CameraShyFirstPerson")

    defaultColor = VBase4(1.0, 1.0, 1.0, 1.0)
    toonInFocusColor = VBase4(0.0, 0.7, 0.0, 1.0)
    toonOutOfFocusColor = VBase4(0.25, 1.0, 0.25, 1.0)
    redColor = VBase4(0.8, 0.0, 0.0, 1.0)
    batteryLevelTwoColor = VBase4(0.9, 0.36, 0.0, 1.0)
    batteryLevelThreeColor = VBase4(0.9, 0.9, 0.0, 1.0)
    batteryLevelFourColor = VBase4(1.0, 1.0, 0.0, 1.0)
    batteryLevelFiveColor = VBase4(0.0, 1.0, 0.0, 1.0)
    fullyChargedState = 5

    def __init__(self, mg):
        self.mg = mg
        self.batteryFrame = None
        self.batteryBg = None
        self.batteryBar = None
        self.rechargeSound = None
        self.fullyChargedSound = None

        self.hasToonInFocus = False
        self.toonToTakePicOf = None

        self.cameraRechargeState = None
        self.cameraRechargingLabel = None
        self.cameraFlashSeq = None

        self.viewfinder = None

        self.camFSM = ClassicFSM('CameraFSM', [
            State('off', self.enterOff, self.exitOff),
            State('ready', self.enterCameraReady, self.exitCameraReady),
            State('recharge', self.enterCameraRecharge,
                  self.exitCameraRecharge)
        ], 'off', 'off')
        self.camFSM.enterInitialState()
        FirstPerson.__init__(self)

    def enterOff(self):
        pass

    def exitOff(self):
        pass

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

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

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

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

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

    def __rechargeNextState(self, task):
        if self.cameraRechargeState is None:
            self.cameraRechargeState = -1
        self.cameraRechargeState += 1
        if self.cameraRechargeState > 0:
            base.playSfx(self.rechargeSound)

            if self.cameraRechargeState <= 1:
                self.batteryBar.setColorScale(self.redColor)
            elif self.cameraRechargeState == 2:
                self.batteryBar.setColorScale(self.batteryLevelTwoColor)
            elif self.cameraRechargeState == 3:
                self.batteryBar.setColorScale(self.batteryLevelThreeColor)
            elif self.cameraRechargeState == 4:
                self.batteryBar.setColorScale(self.batteryLevelFourColor)
            else:
                self.batteryBar.setColorScale(self.batteryLevelFiveColor)

        self.batteryBar.update(self.cameraRechargeState)
        if self.cameraRechargeState == self.fullyChargedState:
            base.playSfx(self.fullyChargedSound)
            self.camFSM.request('ready')
            return task.done
        task.delayTime = 1.0
        return task.again

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

    def __traverse(self, task):
        if not base.mouseWatcherNode.hasMouse():
            return task.cont

        toonInFoc = False
        avatar = None

        for av in self.mg.remoteAvatars:
            if av.avId != base.localAvatar.doId:
                if self.viewfinder.isInView(av):
                    self.notify.info("{0} is in our view finder".format(
                        av.avId))
                    avatar = self.mg.cr.doId2do.get(av.avId)
                    break
        if avatar:
            remoteAvatar = self.mg.getRemoteAvatar(avatar.doId)
            if remoteAvatar:
                toonInFoc = True
                self.notify.info("We've got an avatar in focus ({0})".format(
                    avatar.doId))
                self.__handleToonInFocus(avatar)

        if not toonInFoc:
            self.toonToTakePicOf = None
            self.hasToonInFocus = False
            self.notify.info("No avatar in focus")
            if self.viewfinder['image'].getColorScale(
            ) == self.toonInFocusColor:
                self.viewfinder['image'].setColorScale(
                    self.toonOutOfFocusColor)

        return task.cont

    def __handleToonInFocus(self, toon):
        if not self.hasToonInFocus or self.toonToTakePicOf is not None or self.toonToTakePicOf.doId != toon.doId:
            self.toonToTakePicOf = toon
            self.hasToonInFocus = True
            self.viewfinder['image'].setColorScale(self.toonInFocusColor)

    def start(self):
        self.fullyChargedSound = base.loadSfx('phase_4/audio/sfx/ring_get.ogg')
        self.rechargeSound = base.loadSfx(
            'phase_4/audio/sfx/MG_sfx_travel_game_blue_arrow.ogg')
        self.batteryFrame = DirectFrame(parent=base.a2dBottomRight,
                                        pos=(-0.2, 0, 0.1),
                                        scale=(0.8, 0, 1))
        self.batteryBg = OnscreenImage(
            image='phase_4/maps/battery_charge_frame.png',
            parent=self.batteryFrame)
        self.batteryBg.setTransparency(1)
        self.batteryBg.setX(0.03)
        self.batteryBg.setScale(0.17, 0, 0.05)
        self.batteryBg.setColorScale(0, 0, 0, 1)
        self.batteryBar = DirectWaitBar(value=0,
                                        range=5,
                                        barColor=(1, 1, 1, 1),
                                        relief=None,
                                        scale=(0.12, 0.0, 0.3),
                                        parent=self.batteryFrame)

        self.viewfinder = Viewfinder(1.0)

        base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed,
                                                   0.0,
                                                   CIGlobals.ToonReverseSpeed,
                                                   CIGlobals.ToonRotateSpeed)
        FirstPerson.start(self)

    def reallyStart(self):
        taskMgr.add(self.__traverse, "CSFP.__traverse")
        self.camFSM.request('recharge')
        #taskMgr.add(self.movementTask, "movementTask")
        base.localAvatar.startTrackAnimToSpeed()
        FirstPerson.reallyStart(self)

    def end(self):
        self.camFSM.request('off')
        taskMgr.remove("movementTask")
        taskMgr.remove("CSFP.__traverse")
        FirstPerson.end(self)

    def reallyEnd(self):
        self.batteryBar.destroy()
        self.batteryBar = None
        self.batteryBg.destroy()
        self.batteryBg = None
        self.batteryFrame.destroy()
        self.batteryFrame = None
        self.hasToonInFocus = None
        self.toonToTakePicOf = None
        self.fullyChargedSound = None
        self.rechargeSound = None
        self.viewfinder.cleanup()
        self.viewfinder = None
        self.stopCameraFlash()
        FirstPerson.reallyEnd(self)
        base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed,
                                                   CIGlobals.ToonJumpForce,
                                                   CIGlobals.ToonReverseSpeed,
                                                   CIGlobals.ToonRotateSpeed)

    def cleanup(self):
        self.camFSM.requestFinalState()
        self.camFSM = None
        FirstPerson.cleanup(self)
class RaceGameMovement(DirectObject):
    MINIMUM_POWER = 0.3
    MINIMUM_KEY_DELAY = 0.015
    POWER_FACTOR = 2.5
    defaultBoostBarColor = (0.4, 0.4, 0.7, 1.0)
    fullBoostBarColor = (0.0, 0.0, 0.7, 1.0)
    RUN_FACTOR = 20.0
    RUN_MAX = 1.0
    RUN_PR_FACTOR = 1.2

    def __init__(self, avatar):
        DirectObject.__init__(self)
        self.avatar = avatar
        self.power = self.MINIMUM_POWER
        self.boost = 0.0
        self.fsm = ClassicFSM('RaceGameMovement', [
            State('off', self.enterOff, self.exitOff, ['run']),
            State('run', self.enterRun, self.exitRun, ['fall', 'off']),
            State('fall', self.enterFall, self.exitFall, ['run', 'off']),
            State('final', self.enterFinal, self.exitFinal)
        ], 'off', 'final')
        self.fsm.enterInitialState()
        self.boostFSM = ClassicFSM('Boost', [
            State('off', self.enterBoostOff, self.exitBoostOff, ['boost']),
            State('boost', self.enterBoost, self.exitBoost)
        ], 'off', 'off')
        self.boostFSM.enterInitialState()
        self.keysPressed = {"arrow_left": 0, "arrow_right": 0}
        self.startTime = 0.0
        self.endTime = 0.0
        self.fallTrack = None
        self.isStopped = True
        self.isBoosting = False
        return

    def setBoosting(self, value):
        self.isBoosting = value

    def getBoosting(self):
        return self.isBoosting

    def enterBoostOff(self):
        self.ignore("control")

    def exitBoostOff(self):
        pass

    def createGui(self):
        """ Create the GUI that will tell the client how much
        running power they have. """
        self.powerFrame = DirectFrame()
        self.powerBg = OnscreenImage(image=DGG.getDefaultDialogGeom(),
                                     scale=(0.5, 1.0, 0.5),
                                     pos=(1.02, 0, 0.7),
                                     parent=self.powerFrame,
                                     color=CIGlobals.DialogColor)
        self.powerBar = DirectWaitBar(barColor=(0, 0.7, 0, 1),
                                      range=20.0,
                                      value=0,
                                      parent=self.powerFrame,
                                      scale=(0.15, 0, 1.1),
                                      pos=(1.02, 0, 0.66))
        self.powerBar.setR(-90)
        self.powerTitle = DirectLabel(text="POWER",
                                      text_scale=0.08,
                                      pos=(1.02, 0, 0.85),
                                      relief=None,
                                      parent=self.powerFrame,
                                      text_fg=(1, 1, 0, 1),
                                      text_font=CIGlobals.getMickeyFont())

        self.boostFrame = DirectFrame()
        self.boostBg = OnscreenImage(image=DGG.getDefaultDialogGeom(),
                                     scale=(0.5, 1.0, 0.5),
                                     pos=(0.45, 0, 0.7),
                                     parent=self.boostFrame,
                                     color=CIGlobals.DialogColor)
        self.boostBar = DirectWaitBar(barColor=self.defaultBoostBarColor,
                                      range=10,
                                      value=0,
                                      parent=self.boostFrame,
                                      scale=(0.15, 0, 1.1),
                                      pos=(0.45, 0, 0.66))
        self.boostBar.setR(-90)
        self.boostTitle = DirectLabel(text="BOOST",
                                      text_scale=0.08,
                                      pos=(0.45, 0, 0.85),
                                      relief=None,
                                      parent=self.boostFrame,
                                      text_fg=(1, 1, 0, 1),
                                      text_font=CIGlobals.getMickeyFont())
        self.boostFullLbl = DirectLabel(text="BOOST READY",
                                        text_scale=0.065,
                                        pos=(0.45, 0, 0.3),
                                        relief=None,
                                        parent=self.boostFrame,
                                        text_fg=self.fullBoostBarColor,
                                        text_shadow=(0.4, 0.4, 0.4, 1.0),
                                        text_font=CIGlobals.getToonFont())
        self.boostFullLbl.hide()

    def deleteGui(self):
        """ Delete the GUI that will tell the client how much
        running power they have. """
        self.powerFrame.destroy()
        self.powerBg.destroy()
        self.powerBar.destroy()
        self.powerTitle.destroy()
        self.boostFrame.destroy()
        self.boostBg.destroy()
        self.boostBar.destroy()
        self.boostTitle.destroy()
        self.boostFullLbl.destroy()

    def enableArrowKeys(self):
        """ Enable the arrow keys to increase movement power. """
        self.accept("arrow_left", self.keyPressed, ["arrow_left"])
        self.accept("arrow_right", self.keyPressed, ["arrow_right"])

    def disableArrowKeys(self):
        """ Disable the arrow keys to increase movement power. """
        self.ignore("arrow_left")
        self.ignore("arrow_right")

    def boostKeyPressed(self):
        self.boostFullLbl.hide()
        self.boostFSM.request('boost')

    def keyPressed(self, key):
        """ Figure out which key was pressed and increment the movement power
        if the values of both keys are 1. Also, make the avatar fall down if
        the time between key presses is too fast or the avatar pressed the same
        key more than once."""
        self.stopDelayTimer()
        if self.keysPressed[key] == 1 or self.isTooFast():
            self.resetKeys()
            self.fsm.request('fall')
            return
        self.keysPressed[key] = 1
        if self.keysPressed["arrow_left"] == 1 and self.keysPressed[
                "arrow_right"] == 1:
            self.resetKeys()
            self.changePower()
            self.restartDelayTimer()

    def enterBoost(self):
        self.disableArrowKeys()
        self.resetKeys()
        base.localAvatar.b_setAnimState("swim")
        self.power = 30.0
        taskMgr.add(self.boostTask, 'boostTask')
        self.boostSfx = base.loadSfx("phase_6/audio/sfx/KART_turboLoop.ogg")
        base.playSfx(self.boostSfx, looping=1)

    def exitBoost(self):
        self.enableArrowKeys()
        base.localAvatar.b_setAnimState("run")
        self.power = 17.0
        taskMgr.remove('boostTask')
        self.boostSfx.stop()
        del self.boostSfx

    def boostTask(self, task):
        if self.boostBar['value'] <= 0.0:
            self.boostFSM.request('off')
            self.boostBar['barColor'] = self.defaultBoostBarColor
            return task.done
        self.boostBar['value'] -= 0.3
        task.delayTime = 0.05
        return task.again

    def enterFall(self):
        """ The avatar is pressing his arrow keys too fast. Stop running and
        make the avatar fall down. """
        self.avatar.b_setAnimState("fallFWD")
        self.fallTrack = Sequence(Wait(2.5), Func(self.fsm.request, "run"))
        self.fallTrack.start()

    def exitFall(self):
        self.fallTrack.pause()
        self.fallTrack = None

    def isTooFast(self):
        """ Determine if the delay between key presses in seconds is too low. """
        return (self.getDelayTime() < self.MINIMUM_KEY_DELAY)

    def resetKeys(self):
        """ Reset the value of the keys. """
        for key in self.keysPressed:
            self.keysPressed[key] = 0

    def changePower(self):
        """ Increase the avatar's movement power. """
        self.power = self.POWER_FACTOR / self.getDelayTime()
        if self.boostBar['barColor'] != self.fullBoostBarColor:
            if self.power >= self.powerBar['range']:
                self.boostBar['value'] += 0.8
                if self.boostBar['value'] >= self.boostBar['range']:
                    self.boostBar['barColor'] = self.fullBoostBarColor
                    self.boostFullLbl.show()
                    self.acceptOnce("control", self.boostKeyPressed)
        self.powerBar.update(self.power)

    def startDelayTimer(self):
        self.startTime = globalClock.getFrameTime()

    def stopDelayTimer(self):
        self.endTime = globalClock.getFrameTime()

    def resetDelayTimer(self):
        self.startTime = 0.0
        self.endTime = 0.0

    def restartDelayTimer(self):
        self.stopDelayTimer()
        self.resetDelayTimer()
        self.startDelayTimer()

    def getDelayTime(self):
        return self.endTime - self.startTime

    def enterRun(self):
        """ Start moving the avatar, make the avatar run, and
        start tracking the delay between key presses. """
        self.avatar.enableBlend()
        self.avatar.setControlEffect('neutral', self.RUN_MAX)
        self.avatar.setControlEffect('run', 0.0)
        self.avatar.loop('run')
        self.avatar.loop('neutral')
        taskMgr.add(self.move, "move")
        if self.boostBar['barColor'] == self.fullBoostBarColor:
            self.boostFullLbl.show()
            self.acceptOnce("control", self.boostKeyPressed)
        self.startDelayTimer()
        self.enableArrowKeys()
        self.isStopped = False

    def exitRun(self):
        """ Stop moving the avatar, make the avatar stand, and stop
        tracking the delay between key presses. """
        taskMgr.remove("move")
        self.boostFSM.request('off')
        if self.boostBar['barColor'] == self.fullBoostBarColor:
            self.boostFullLbl.hide()
            self.ignore("control")
        self.stopDelayTimer()
        self.resetDelayTimer()
        self.disableArrowKeys()
        self.avatar.disableBlend()
        self.isStopped = True
        self.avatar.setPlayRate(1.0, 'run')
        self.power = self.MINIMUM_POWER
        self.powerBar.update(0)

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterFinal(self):
        pass

    def exitFinal(self):
        pass

    def decreasePower(self, task):
        """ Decrease power so the avatar does not keep the same amount
        of power while not tapping the arrow keys. """
        if self.power > self.MINIMUM_POWER:
            self.power -= self.POWER_FACTOR / 0.01
            if self.power < self.MINIMUM_POWER:
                self.power = self.MINIMUM_POWER
        task.delayTime = 0.5
        return Task.again

    def move(self, task):
        """ Move the avatar forward on the Y axis with the current amount of power. """
        dt = globalClock.getDt()
        runEffectRaw = self.power / self.RUN_FACTOR
        runEffect = min(self.RUN_MAX, runEffectRaw)
        neutralEffect = self.RUN_MAX - runEffect
        self.avatar.setControlEffect('run', runEffect)
        self.avatar.setControlEffect('neutral', neutralEffect)
        if runEffectRaw > self.RUN_MAX:
            print runEffectRaw
            self.avatar.setPlayRate(runEffectRaw * self.RUN_PR_FACTOR, 'run')
        else:
            self.avatar.setPlayRate(1.0, 'run')
        self.avatar.setY(self.avatar.getY() + self.power * dt)
        return Task.cont

    def cleanup(self):
        self.fsm.requestFinalState()
예제 #11
0
class DistributedRaceGame(DistributedMinigame.DistributedMinigame):

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

        DistributedMinigame.DistributedMinigame.__init__(self, cr)
        self.movement = RaceGameMovement.RaceGameMovement(base.localAvatar)
        self.skyUtil = SkyUtil()
        self.raceFSM = ClassicFSM('DistributedRaceGame', [State('race', self.enterRace, self.exitRace), State('raceTransition', self.enterRaceTransition, self.exitRaceTransition), State('off', self.enterRaceOff, self.exitRaceOff)], 'off', 'off')
        self.raceFSM.enterInitialState()
        self.cr = cr
        self.track = None
        self.sky = None
        self.countSfx = base.loadSfx('phase_5/audio/sfx/firehydrant_popup.mp3')
        self.goSfx = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.mp3')
        self.game = CIGlobals.RaceGame
        self.trackPath = 'phase_4/models/minigames/sprint_track.egg'
        self.skyPath = 'phase_3.5/models/props/TT_sky.bam'
        self.lanePos = [(-22.0, -205.0, 0.0),
         (-11.66, -205.0, 0.0),
         (0.0, -205.0, 0.0),
         (-33.66, -205.0, 0.0)]
        self.initialCamPos = {'pos': (41.1, -145.0, 25.88),
         'hpr': (135.0, 345.96, 0.0)}
        self.raceCamPos = (-24.52, -37.22, 25.0)
        self.lane = 0
        return

    def load(self):
        self.deleteWorld()
        self.track = loader.loadModel(self.trackPath)
        self.track.reparentTo(render)
        self.sky = loader.loadModel(self.skyPath)
        self.sky.reparentTo(self.track)
        self.skyUtil.startSky(self.sky)
        self.setMinigameMusic('phase_4/audio/bgm/MG_toontag.mid')
        self.setDescription('Tap the left and right arrow keys repeatedly, in turns, as fast as ' + 'you can to win the race! Every time your power bar hits the top, the boost bar starts' + ' to fill. When the boost bar is full, press CTRL to boost for a few seconds.')
        self.setWinnerPrize(20)
        self.setLoserPrize(5)
        self.d_requestToonLane()
        camera.reparentTo(render)
        camera.setPos(self.initialCamPos['pos'])
        camera.setHpr(self.initialCamPos['hpr'])
        DistributedMinigame.DistributedMinigame.load(self)

    def enterPlay(self):
        DistributedMinigame.DistributedMinigame.enterPlay(self)
        self.raceFSM.request('raceTransition')

    def exitPlay(self):
        DistributedMinigame.DistributedMinigame.exitPlay(self)
        self.raceFSM.request('off')

    def enterRace(self):
        self.startMovement()

    def exitRace(self):
        self.stopMovement()

    def enterRaceOff(self):
        pass

    def exitRaceOff(self):
        pass

    def enterRaceTransition(self):
        self.raceTrans = Sequence(Wait(0.5), Func(self.moveCameraToToon), Wait(4.5), Func(self.moveCameraToTop), Wait(4.5), Func(self.startCountdown))
        self.raceTrans.start()

    def exitRaceTransition(self):
        self.raceTrans.pause()
        del self.raceTrans

    def startMovement(self):
        self.movement.createGui()
        self.movement.fsm.request('run')

    def enterGameOver(self, winner = 0, winnerDoId = 0, allPrize = 0):
        self.raceFSM.request('off')
        DistributedMinigame.DistributedMinigame.enterGameOver(self, winner, winnerDoId, allPrize)

    def stopMovement(self):
        self.movement.cleanup()
        self.movement.deleteGui()

    def startCountdown(self):
        """ Start the countdown to the start of the race. """
        self.countdownLbl = DirectLabel(text='', text_scale=0.3, text_font=CIGlobals.getMickeyFont(), text_fg=(1, 1, 0, 1), pos=(0, 0, 0.5))
        Sequence(Func(self.setCountdownText, '3'), Wait(1.0), Func(self.setCountdownText, '2'), Wait(1.0), Func(self.setCountdownText, '1'), Wait(1.0), Func(self.setCountdownText, 'GO!'), Wait(1.5), Func(self.deleteCountdownLabel)).start()

    def setCountdownText(self, number):
        self.countdownLbl['text'] = number
        if number == 'GO!':
            self.countdownLbl['text_fg'] = (0, 1, 0, 1)
            self.goSfx.play()
            self.raceFSM.request('race')
        else:
            self.countSfx.play()

    def deleteCountdownLabel(self):
        self.countdownLbl.destroy()
        del self.countdownLbl

    def moveCameraToToon(self):
        camPInt = LerpPosInterval(camera, duration=3.0, pos=self.localAv.getPos(render) + (0, 15, 3), startPos=camera.getPos(render), blendType='easeInOut')
        camQInt = camera.quatInterval(3.0, hpr=Vec3(180, 0, 0), blendType='easeInOut')
        camPInt.start()
        camQInt.start()

    def moveCameraToTop(self):
        camera.setPos(camera.getPos(self.localAv))
        camera.reparentTo(self.localAv)
        oldPos = camera.getPos()
        camera.setPos(self.raceCamPos)
        oldHpr = camera.getHpr()
        camera.lookAt(self.localAv.getPart('head'))
        newHpr = camera.getHpr()
        camera.setHpr(oldHpr)
        camera.setPos(oldPos)
        camPInt = LerpPosInterval(camera, duration=3.0, pos=self.raceCamPos, startPos=oldPos, blendType='easeInOut')
        camQInt = camera.quatInterval(3.0, hpr=newHpr, blendType='easeInOut')
        camPInt.start()
        camQInt.start()

    def deleteWorld(self):
        if self.track:
            self.track.removeNode()
            self.track = None
        if self.sky:
            self.skyUtil.stopSky()
            self.sky.removeNode()
            self.sky = None
        return

    def setToonLane(self, lane):
        self.lane = lane
        base.localAvatar.setPos(self.lanePos[lane])
        base.localAvatar.setHpr(0, 0, 0)

    def getToonLane(self):
        return self.lane

    def d_requestToonLane(self):
        self.sendUpdate('requestToonLane', [])

    def announceGenerate(self):
        DistributedMinigame.DistributedMinigame.announceGenerate(self)
        self.load()

    def disable(self):
        DistributedMinigame.DistributedMinigame.disable(self)
        self.deleteWorld()
        self.raceFSM.requestFinalState()
        del self.raceFSM
        self.countSfx = None
        self.goSfx = None
        return
예제 #12
0
class DistributedMinigame(DistributedObject.DistributedObject, Timer.Timer):

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

        DistributedObject.DistributedObject.__init__(self, cr)
        Timer.Timer.__init__(self)
        self.headPanels = HeadPanels()
        self.finalScoreUI = FinalScoreGUI()
        self.fsm = ClassicFSM('DistributedMinigame', [State('start', self.enterStart, self.exitStart, ['waitForOthers']),
         State('waitForOthers', self.enterWaitForOthers, self.exitWaitForOthers, ['play']),
         State('play', self.enterPlay, self.exitPlay, ['gameOver']),
         State('gameOver', self.enterGameOver, self.exitGameOver, ['off']),
         State('off', self.enterOff, self.exitOff)], 'off', 'off')
        self.fsm.enterInitialState()
        self.cr = cr
        self.localAv = base.localAvatar
        self.localAvId = self.localAv.doId
        self.musicPath = 'phase_4/audio/bgm/trolley_song.mid'
        self.winSfx = base.loadSfx('phase_4/audio/sfx/MG_win.mp3')
        self.loseSfx = base.loadSfx('phase_4/audio/sfx/MG_lose.mp3')
        self.prizeHigh = base.loadSfx('phase_6/audio/sfx/KART_Applause_1.mp3')
        self.prizeLow = base.loadSfx('phase_6/audio/sfx/KART_Applause_4.mp3')
        self.music = None
        self.description = ''
        self.descDialog = None
        self.winnerPrize = 0
        self.loserPrize = 0
        self.winnerMsg = 'Winner!\nYou have earned: %s'
        self.loserMsg = 'Loser!\nYou have earned: %s'
        self.allWinnerMsgs = ['Nice try!\nYou have earned: %s',
         'Good job!\nYou have earned: %s',
         'Way to go!\nYou have earned: %s',
         'Awesome!\nYou have earned: %s']
        self.timer = None
        self.timeLbl = None
        return

    def enterFinalScores(self):
        self.finalScoreUI.load()
        self.finalScoreUI.showFinalScores()

    def exitFinalScores(self):
        self.finalScoreUI.hideFinalScores()
        self.finalScoreUI.unload()

    def finalScores(self, avIdList, scoreList):
        self.finalScoreUI.handleFinalScores(avIdList, scoreList)

    def generateHeadPanel(self, gender, head, headtype, color, doId, name):
        self.headPanels.generate(gender, head, headtype, color, doId, name)

    def updateHeadPanelValue(self, doId, direction):
        self.headPanels.updateValue(doId, direction)

    def setTimerTime(self, time):
        self.setTime(time)

    def createTimer(self):
        Timer.Timer.load(self)

    def deleteTimer(self):
        Timer.Timer.unload(self)

    def setDescription(self, desc):
        self.description = desc

    def getDescription(self):
        return self.description

    def enterStart(self):
        self.descDialog = GlobalDialog(style=3, message=self.getDescription(), doneEvent='gameDescAck')
        self.acceptOnce('gameDescAck', self.handleDescAck)

    def handleDescAck(self):
        self.d_ready()
        self.fsm.request('waitForOthers')

    def exitStart(self):
        self.ignore('gameDescAck')
        self.descDialog.cleanup()
        del self.descDialog

    def enterWaitForOthers(self):
        self.waitLbl = DirectLabel(text='Waiting for other players...', relief=None, text_fg=(1, 1, 1, 1), text_scale=0.08, text_shadow=(0, 0, 0, 1))
        return

    def exitWaitForOthers(self):
        self.waitLbl.destroy()
        del self.waitLbl

    def setLoserPrize(self, prize):
        self.loserPrize = prize

    def setWinnerPrize(self, prize):
        self.winnerPrize = prize

    def getLoserPrize(self):
        return self.loserPrize

    def getWinnerPrize(self):
        return self.winnerPrize

    def winner(self):
        self.winSfx.play()
        self.localAv.b_setAnimState('happy')
        Sequence(Wait(3.5), Func(self.displayGameOver, 'winner')).start()

    def showPrize(self, amt):
        self.winSfx.play()
        self.localAv.b_setAnimState('happy')
        Sequence(Wait(3.5), Func(self.displayGameOver, 'showPrize', amt)).start()

    def loser(self):
        self.loseSfx.play()
        self.localAv.b_setAnimState('neutral')
        Sequence(Wait(3.5), Func(self.displayGameOver, 'loser')).start()

    def displayGameOver(self, scenario, amt = None):
        if scenario == 'winner':
            msg = self.winnerMsg % self.winnerPrize
            self.prizeHigh.play()
        elif scenario == 'loser':
            msg = self.loserMsg % self.loserPrize
            self.prizeLow.play()
        elif scenario == 'showPrize':
            msg = random.choice(self.allWinnerMsgs) % amt
            self.prizeHigh.play()
        self.gameOverDialog = GlobalDialog(message=msg, style=3, doneEvent='gameOverAck')
        self.acceptOnce('gameOverAck', self.__handleGameOverAck)
        self.gameOverDialog.show()

    def deleteGameOverDialog(self):
        self.ignore('gameOverAck')
        if hasattr(self, 'gameOverDialog'):
            self.gameOverDialog.cleanup()
            del self.gameOverDialog

    def __handleGameOverAck(self):
        self.fsm.requestFinalState()
        Sequence(Func(base.transitions.irisOut, 1.0), Wait(1.2), Func(self.d_leaving), Func(self.headBackToMinigameArea)).start()

    def headBackToMinigameArea(self):
        whereName = ZoneUtil.getWhereName(CIGlobals.MinigameAreaId)
        loaderName = ZoneUtil.getLoaderName(CIGlobals.MinigameAreaId)
        requestStatus = {'zoneId': CIGlobals.MinigameAreaId,
         'hoodId': CIGlobals.MinigameArea,
         'where': whereName,
         'how': 'teleportIn',
         'avId': base.localAvatar.doId,
         'shardId': None,
         'loader': loaderName}
        self.cr.playGame.hood.fsm.request('quietZone', [requestStatus])
        return

    def abort(self):
        self.headBackToMinigameArea()

    def load(self):
        self.fsm.request('start')
        base.transitions.irisIn()

    def d_leaving(self):
        """ Tell the AI that we are leaving. """
        self.sendUpdate('leaving', [])

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

    def enterPlay(self):
        self.playMinigameMusic()

    def exitPlay(self):
        self.stopMinigameMusic()

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterGameOver(self, winner, winnerDoId, allPrize):
        if winner:
            if self.localAvId in winnerDoId:
                self.winner()
            else:
                self.loser()
        else:
            self.showPrize(allPrize)

    def exitGameOver(self):
        self.deleteGameOverDialog()

    def gameOver(self, winner = 0, winnerDoId = [], allPrize = 0):
        self.fsm.request('gameOver', [winner, winnerDoId, allPrize])

    def setMinigameMusic(self, path):
        self.musicPath = path

    def getMinigameMusic(self):
        return self.musicPath

    def playMinigameMusic(self):
        self.stopMinigameMusic()
        self.music = base.loadMusic(self.musicPath)
        self.music.setLoop(True)
        self.music.setVolume(0.7)
        self.music.play()

    def stopMinigameMusic(self):
        if self.music:
            self.music.stop()
            self.music = None
        return

    def d_ready(self):
        self.sendUpdate('ready', [])

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0)
        self.fsm.requestFinalState()
        del self.fsm
        self.winSfx = None
        self.loseSfx = None
        self.prizeHigh = None
        self.prizeLow = None
        self.headPanels.delete()
        self.headPanels = None
        self.finalScoreUI.unload()
        self.finalScoreUI = None
        return
class AdminPage(BookPage):
    def __init__(self, book):
        BookPage.__init__(self, book, 'Admin Panel')
        self.fsm = ClassicFSM(
            'AdminPage',
            [
                State('off', self.enterOff, self.exitOff),
                State('basePage', self.enterBasePage, self.exitBasePage),
                State('kickSection', self.enterKickSection,
                      self.exitKickSection),
                #State('clickOnToon', self.enterClickOnToon, self.exitClickOnToon),
                State('sysMsgSection', self.enterSysMsgSection,
                      self.exitSysMsgSection)
            ],
            'off',
            'off')
        self.fsm.enterInitialState()

    def load(self):
        BookPage.load(self)
        icons = loader.loadModel('phase_4/models/gui/tfa_images.bam')
        self.icon = icons.find('**/hq-dialog-image')
        icons.detachNode()

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enter(self):
        BookPage.enter(self)
        self.fsm.request('basePage')

    def exit(self):
        self.fsm.requestFinalState()
        BookPage.exit(self)

    def unload(self):
        del self.book
        del self.fsm
        BookPage.unload(self)

    def enterSysMsgSection(self):
        geom = CIGlobals.getDefaultBtnGeom()
        self.infoLbl = OnscreenText(
            text="Inform all online players about something.", pos=(0, 0.45))
        self.msgEntry = DirectEntry(
            initialText="System Message...",
            scale=0.055,
            width=15,
            numLines=4,
            command=self.sendSystemMessageCommand,
            focusInCommand=base.localAvatar.chatInput.disableKeyboardShortcuts,
            focusOutCommand=base.localAvatar.chatInput.enableKeyboardShortcuts,
            pos=(-0.4, 0, 0))
        self.sendBtn = DirectButton(
            geom=geom,
            text_scale=0.04,
            relief=None,
            scale=1.0,
            text="Send",
            pos=(0, 0, -0.35),
            text_pos=(0, -0.01),
            command=self.sendSystemMessageCommand,
        )
        self.cancelBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text="Cancel",
                                      pos=(-0.45, 0.15, -0.55),
                                      text_pos=(0, -0.01),
                                      command=self.fsm.request,
                                      extraArgs=['basePage'])

    # Occasionally, extra arguments are sent and this extra variable must be here to capture them.
    def sendSystemMessageCommand(self, _=None):
        msg = self.msgEntry.get()
        DISTRICT_WIDE_MSG(msg)
        self.fsm.request('basePage')

    def exitSysMsgSection(self):
        self.infoLbl.destroy()
        del self.infoLbl
        self.msgEntry.destroy()
        del self.msgEntry
        self.sendBtn.destroy()
        del self.sendBtn
        self.cancelBtn.destroy()
        del self.cancelBtn

    def enterKickSection(self):
        geom = CIGlobals.getDefaultBtnGeom()
        self.infoLbl = OnscreenText(text="Kick or Ban?", pos=(0, 0.45))
        self.kickBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text="Kick",
                                    pos=(0, 0, 0.1),
                                    text_pos=(0, -0.01),
                                    command=self.book.finishedResume,
                                    extraArgs=[KickBanDialog, [0]])
        self.banBtn = DirectButton(geom=geom,
                                   text_scale=0.04,
                                   relief=None,
                                   scale=1.0,
                                   text="Ban",
                                   pos=(0, 0, 0.0),
                                   text_pos=(0, -0.01),
                                   command=self.book.finishedResume,
                                   extraArgs=[KickBanDialog, [1]])
        self.cancelBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text="Cancel",
                                      pos=(-0.45, 0.15, -0.45),
                                      text_pos=(0, -0.01),
                                      command=self.fsm.request,
                                      extraArgs=['basePage'])

    def exitKickSection(self):
        self.banBtn.destroy()
        del self.banBtn
        self.infoLbl.destroy()
        del self.infoLbl
        self.cancelBtn.destroy()
        del self.cancelBtn
        self.kickBtn.destroy()
        del self.kickBtn

    def enterBasePage(self):
        geom = CIGlobals.getDefaultBtnGeom()
        self.ghostBtn = DirectButton(geom=geom,
                                     text_scale=0.04,
                                     relief=None,
                                     scale=1.0,
                                     text="Toggle Ghost",
                                     pos=(-0.45, 0.15, 0.5),
                                     text_pos=(0, -0.01),
                                     command=TOGGLE_GHOST)
        self.bgBtn = DirectButton(geom=geom,
                                  text_scale=0.04,
                                  relief=None,
                                  scale=1.0,
                                  text="Toggle Background",
                                  pos=(-0.45, 0.15, 0.40),
                                  text_pos=(0, -0.01),
                                  command=self.toggleBackground)
        self.idBtn = DirectButton(geom=geom,
                                  text_scale=0.04,
                                  relief=None,
                                  scale=1.0,
                                  text="Toggle Player Ids",
                                  pos=(-0.45, 0.15, 0.3),
                                  text_pos=(0, -0.01),
                                  command=TOGGLE_PLAYER_IDS)
        self.kickBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text="Kick/Ban Player",
                                    pos=(0.45, 0.15, 0.5),
                                    text_pos=(0, -0.01),
                                    command=self.openKickPage)
        self.systemMsgBtn = DirectButton(geom=geom,
                                         text_scale=0.04,
                                         relief=None,
                                         scale=1.0,
                                         text="System Message",
                                         pos=(-0.45, 0.15, 0.1),
                                         text_pos=(0, -0.01),
                                         command=self.openSysMsgPage)
        self.oobeBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text="Toggle OOBE",
                                    pos=(-0.45, 0.15, 0.2),
                                    text_pos=(0, -0.01),
                                    command=base.oobe)
        self.directBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text="Start DIRECT",
                                      pos=(-0.45, 0.15, 0.1),
                                      text_pos=(0, -0.01),
                                      command=self.doStartDirect)
        self.pstatsBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text="Toggle PStats",
                                      pos=(-0.45, 0.15, 0.0),
                                      text_pos=(0, -0.01),
                                      command=self.togglePStats)
        self.pingBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text="Toggle Ping",
                                    pos=(-0.45, 0.15, -0.1),
                                    text_pos=(0, -0.01),
                                    command=base.cr.togglePing)
        self.tokenBtn = DirectButton(geom=geom,
                                     text_scale=0.04,
                                     relief=None,
                                     scale=1.0,
                                     text="Modify Access Level",
                                     pos=(0.45, 0.15, 0.4),
                                     text_pos=(0, -0.01),
                                     command=self.book.finishedResume,
                                     extraArgs=[AdminTokenDialog, []])
        self.worldBtn = DirectButton(geom=geom,
                                     text_scale=0.04,
                                     relief=None,
                                     scale=1.0,
                                     text="Give World Access",
                                     pos=(0.45, 0.15, 0.3),
                                     text_pos=(0, -0.01),
                                     command=self.book.finishedResume,
                                     extraArgs=[WorldAccessDialog, []])
        self.allGagsBtn = DirectButton(geom=geom,
                                       text_scale=0.04,
                                       relief=None,
                                       scale=1.0,
                                       text="Restock All Gags",
                                       pos=(0.45, 0.15, 0.2),
                                       text_pos=(0, -0.01),
                                       command=SEND_REQ_UNLOCK_GAGS)

        self.allLaffBtn = DirectButton(geom=geom,
                                       text_scale=0.04,
                                       relief=None,
                                       scale=1.0,
                                       text="Refill Laff",
                                       pos=(0.45, 0.15, 0.1),
                                       text_pos=(0, -0.01),
                                       command=SEND_REQ_REFILL_LAFF)

        self.physDbgBtn = DirectButton(geom=geom,
                                       text_scale=0.039,
                                       relief=None,
                                       scale=1.0,
                                       text="Toggle Physics Debug",
                                       pos=(0.45, 0.15, 0.0),
                                       text_pos=(0, -0.01),
                                       command=self.togglePhysDbg)
        self.analyzeBtn = DirectButton(geom=geom,
                                       text_scale=0.04,
                                       relief=None,
                                       scale=1.0,
                                       text="Analyze Scene",
                                       pos=(0.45, 0.15, -0.1),
                                       text_pos=(0, -0.01),
                                       command=self.doAnalyzeScene)
        self.listBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text="List Scene",
                                    pos=(0.45, 0.15, -0.2),
                                    text_pos=(0, -0.01),
                                    command=render.ls)
        self.noClipBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text="Toggle No Clip",
                                      pos=(0.45, 0.15, -0.3),
                                      text_pos=(0, -0.01),
                                      command=self.toggleNoClip)
        base.cr.playGame.getPlace().maybeUpdateAdminPage()
        del geom

    def doStartDirect(self):
        base.startTk()
        base.startDirect()

    def doAnalyzeScene(self):
        render.analyze()

        ls = LineStream()
        sga = SceneGraphAnalyzer()
        sga.addNode(render.node())
        sga.write(ls)
        text = ""
        while ls.isTextAvailable():
            text += ls.getLine() + "\n"
        self.acceptOnce('analyzedone', self.__handleAnalyzeDone)
        self.analyzeDlg = GlobalDialog(message=text,
                                       style=Ok,
                                       doneEvent='analyzedone',
                                       text_scale=0.05)
        self.analyzeDlg.show()

    def __handleAnalyzeDone(self):
        self.analyzeDlg.cleanup()
        del self.analyzeDlg

    def toggleNoClip(self):
        ncl = not base.localAvatar.walkControls.controller.noClip
        base.localAvatar.walkControls.controller.noClip = ncl

        if ncl:
            base.cr.myDistrict.systemMessage("No Clip Enabled")
        else:
            base.cr.myDistrict.systemMessage("No Clip Disabled")

    def togglePhysDbg(self):
        base.setPhysicsDebug(not base.physicsDbgFlag)

    def togglePStats(self):
        if PStatClient.isConnected():
            PStatClient.disconnect()
        else:
            # in production, show stats viewer on the server
            if base.config.GetBool("pstats-view-on-server", False):
                PStatClient.connect("127.0.0.1" if not metadata.IS_PRODUCTION
                                    else "gameserver.coginvasion.online")
            else:
                PStatClient.connect("127.0.0.1")

    def toggleBackground(self):
        if render.isHidden():
            render.show()
        else:
            render.hide()
        if self.book.isBackgroundHidden():
            self.book.show()
            self.book.setBackgroundHidden(False)
        else:
            self.book.hide()
            self.book.setBackgroundHidden(True)

    def openKickPage(self):
        self.fsm.request('kickSection')

    def openSysMsgPage(self):
        self.fsm.request('sysMsgSection')

    def exitBasePage(self):
        self.noClipBtn.destroy()
        del self.noClipBtn
        self.systemMsgBtn.destroy()
        del self.systemMsgBtn
        self.idBtn.destroy()
        del self.idBtn
        self.kickBtn.destroy()
        del self.kickBtn
        self.bgBtn.destroy()
        del self.bgBtn
        self.ghostBtn.destroy()
        del self.ghostBtn
        self.oobeBtn.destroy()
        del self.oobeBtn
        self.tokenBtn.destroy()
        del self.tokenBtn
        self.worldBtn.destroy()
        del self.worldBtn
        self.allGagsBtn.destroy()
        del self.allGagsBtn
        self.allLaffBtn.destroy()
        del self.allLaffBtn
        self.physDbgBtn.destroy()
        del self.physDbgBtn
        self.analyzeBtn.destroy()
        del self.analyzeBtn
        if hasattr(self, 'analyzeDlg'):
            self.ignore('analyzedone')
            self.analyzeDlg.cleanup()
            del self.analyzeDlg
        self.directBtn.destroy()
        del self.directBtn
        self.listBtn.destroy()
        del self.listBtn
        self.pstatsBtn.destroy()
        del self.pstatsBtn
        self.pingBtn.destroy()
        del self.pingBtn
예제 #14
0
class DistributedBoat(DistributedObject):
    notify = directNotify.newCategory("DistributedBoat")

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM('DistributedBoat', [
            State('off', self.enterOff, self.exitOff),
            State('eastToWest', self.enterEastToWest, self.exitEastToWest),
            State('westToEast', self.enterWestToEast, self.exitWestToEast)
        ], 'off', 'off')
        self.boat = None
        self.eastPier = None
        self.eastPierPath = 'east_pier'
        self.westPier = None
        self.westPierPath = 'west_pier'
        self.pierUpP = 0.0
        self.pierDownP = -45.0
        self.fogHorn = 'phase_5/audio/sfx/SZ_DD_foghorn.ogg'
        self.shipBell = 'phase_6/audio/sfx/SZ_DD_shipbell.ogg'
        self.waterLap = 'phase_6/audio/sfx/SZ_DD_waterlap.ogg'
        self.dockCreak = 'phase_6/audio/sfx/SZ_DD_dockcreak.ogg'
        self.eastWest = 'phase_6/paths/dd-e-w.bam'
        self.westEast = 'phase_6/paths/dd-w-e.bam'
        self.boatPath = '*donalds_boat*'
        self.track = None
        self.state = None

    def __handleOnBoat(self, entry):
        base.localAvatar.b_setParent(CIGlobals.SPDonaldsBoat)
        base.playSfx(self.soundWaterLap, looping=1)

    def __handleOffBoat(self, entry):
        base.localAvatar.b_setParent(CIGlobals.SPRender)
        self.soundWaterLap.stop()

    def __pollBoat(self, task):
        try:
            self.boat = self.cr.playGame.hood.loader.geom.find('**/' +
                                                               self.boatPath)
        except:
            return task.cont
        self.generated()
        return task.done

    def generate(self):
        DistributedObject.generate(self)
        self.soundFogHorn = base.loadSfx(self.fogHorn)
        self.soundShipBell = base.loadSfx(self.shipBell)
        self.soundWaterLap = base.loadSfx(self.waterLap)
        self.soundDockCreak = base.loadSfx(self.dockCreak)
        #try:
        #    self.boat = self.cr.playGame.hood.loader.geom.find('**/' + self.boatPath)
        #except:
        #        base.taskMgr.add(self.__pollBoat, self.uniqueName('__pollBoat'))
        #        return
        self.boat = self.cr.playGame.hood.loader.geom.find('**/' +
                                                           self.boatPath)
        self.generated()

    def generated(self):
        self.eastPier = self.cr.playGame.hood.loader.geom.find(
            '**/' + self.eastPierPath)
        self.westPier = self.cr.playGame.hood.loader.geom.find(
            '**/' + self.westPierPath)
        base.cr.parentMgr.registerParent(CIGlobals.SPDonaldsBoat, self.boat)
        self.accept('enterdonalds_boat_floor', self.__handleOnBoat)
        self.accept('exitdonalds_boat_floor', self.__handleOffBoat)
        self.d_requestCurrentStateAndTimestamp()
        self.fsm.enterInitialState()

    def disable(self):
        base.taskMgr.remove(self.uniqueName('__pollBoat'))
        base.cr.parentMgr.unregisterParent(CIGlobals.SPDonaldsBoat)
        self.ignore('enterdonalds_boat_floor')
        self.ignore('exitdonalds_boat_floor')
        self.fsm.requestFinalState()
        del self.fsm
        del self.soundFogHorn
        del self.soundShipBell
        del self.soundWaterLap
        del self.soundDockCreak
        self.fogHorn = None
        self.shipBell = None
        self.waterLap = None
        self.dockCreak = None
        self.boat = None
        self.track = None
        self.pierDownP = None
        self.pierUpP = None
        self.eastPier = None
        self.eastPierPath = None
        self.westPier = None
        self.westPierPath = None
        self.boatPath = None
        self.westEast = None
        self.eastWest = None
        DistributedObject.disable(self)

    def currentStateAndTimestamp(self, state, timestamp):
        self.setState(state, timestamp)

    def d_requestCurrentStateAndTimestamp(self):
        self.sendUpdate('requestCurrentStateAndTimestamp', [])

    def setState(self, state, timestamp=None):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        self.state = state
        if self.boat:
            self.fsm.request(state, [ts])

    def enterEastToWest(self, ts=0):
        moPath = Mopath.Mopath()
        moPath.loadFile(self.eastWest)
        moIval = MopathInterval(moPath, self.boat)

        self.track = Parallel(
            SoundInterval(self.soundShipBell, node=self.boat),
            SoundInterval(self.soundDockCreak, node=self.eastPier), moIval,
            LerpQuatInterval(self.eastPier,
                             duration=5.0,
                             quat=(90, self.pierDownP, 0),
                             startHpr=(90, self.pierUpP, 0)),
            Sequence(
                Wait(15.0),
                Parallel(
                    LerpQuatInterval(self.westPier,
                                     duration=5.0,
                                     quat=(-90, self.pierUpP, 0),
                                     startHpr=(-90, self.pierDownP, 0)),
                    Sequence(
                        Wait(2.0),
                        SoundInterval(self.soundDockCreak,
                                      node=self.westPier))),
                SoundInterval(self.soundFogHorn, node=self.boat)))
        self.track.start(ts)

    def exitEastToWest(self):
        if self.track:
            self.track.finish()
            self.track = None

    def enterWestToEast(self, ts=0):
        moPath = Mopath.Mopath()
        moPath.loadFile(self.westEast)
        moIval = MopathInterval(moPath, self.boat)

        self.track = Parallel(
            SoundInterval(self.soundShipBell, node=self.boat),
            SoundInterval(self.soundDockCreak, node=self.westPier), moIval,
            LerpQuatInterval(self.westPier,
                             duration=5.0,
                             quat=(-90, self.pierDownP, 0),
                             startHpr=(-90, self.pierUpP, 0)),
            Sequence(
                Wait(15.0),
                Parallel(
                    LerpQuatInterval(self.eastPier,
                                     duration=5.0,
                                     quat=(90, self.pierUpP, 0),
                                     startHpr=(90, self.pierDownP, 0)),
                    Sequence(
                        Wait(2.0),
                        SoundInterval(self.soundDockCreak,
                                      node=self.eastPier))),
                SoundInterval(self.soundFogHorn, node=self.boat)))
        self.track.start(ts)

    def exitWestToEast(self):
        if self.track:
            self.track.finish()
            self.track = None

    def enterOff(self):
        pass

    def exitOff(self):
        pass
예제 #15
0
class AvChooser(StateData):
    notify = directNotify.newCategory('AvChooser')

    def __init__(self, parentFSM):
        StateData.__init__(self, 'avChooseDone')
        self.avChooseFSM = ClassicFSM('avChoose', [State('getToonData', self.enterGetToonData, self.exitGetToonData),
         State('avChoose', self.enterAvChoose, self.exitAvChoose),
         State('waitForToonDelResponse', self.enterWaitForToonDelResponse, self.exitWaitForToonDelResponse),
         State('off', self.enterOff, self.exitOff)], 'off', 'off')
        self.avChooseFSM.enterInitialState()
        self.parentFSM = parentFSM
        self.parentFSM.getStateNamed('avChoose').addChild(self.avChooseFSM)
        self.pickAToon = None
        self.setAvatarsNone()
        return

    def enter(self):
        StateData.enter(self)
        base.transitions.noTransitions()
        self.avChooseFSM.request('getToonData')

    def exit(self):
        StateData.exit(self)
        self.setAvatarsNone()
        self.avChooseFSM.requestFinalState()

    def setAvatarsNone(self):
        self.avChoices = []

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterGetToonData(self):
        self.acceptOnce(base.cr.csm.getSetAvatarsEvent(), self.handleToonData)
        base.cr.csm.d_requestAvatars()

    def handleToonData(self, avatarList):
        for av in avatarList:
            avId = av[0]
            dna = av[1]
            name = av[2]
            slot = av[3]
            choice = AvChoice(dna, name, slot, avId)
            self.avChoices.append(choice)

        self.avChooseFSM.request('avChoose')

    def exitGetToonData(self):
        self.ignore(base.cr.csm.getSetAvatarsEvent())

    def enterAvChoose(self):
        self.pickAToon = CharSelection(self)
        self.pickAToon.load()

    def enterWaitForToonDelResponse(self, avId):
        self.acceptOnce(base.cr.csm.getToonDeletedEvent(), self.handleDeleteToonResp)
        base.cr.csm.sendDeleteToon(avId)

    def exitWaitForToonDelResponse(self):
        self.ignore(base.cr.csm.getToonDeletedEvent())

    def hasToonInSlot(self, slot):
        if self.getAvChoiceBySlot(slot) != None:
            return True
        else:
            return False
            return

    def getNameInSlot(self, slot):
        return self.getAvChoiceBySlot(slot).getName()

    def getAvChoiceBySlot(self, slot):
        for avChoice in self.avChoices:
            if avChoice.getSlot() == slot:
                return avChoice

        return None

    def getHeadInfo(self, slot):
        dna = self.getAvChoiceBySlot(slot).getDNA()
        self.pickAToon.dna.setDNAStrand(dna)
        return [self.pickAToon.dna.getGender(),
         self.pickAToon.dna.getAnimal(),
         self.pickAToon.dna.getHead(),
         self.pickAToon.dna.getHeadColor()]

    def handleDeleteToonResp(self):
        base.cr.loginFSM.request('avChoose')

    def exitAvChoose(self):
        self.pickAToon.unload()
        self.pickAToon = None
        return
class DistributedDisneyChar(DistributedAvatar, DistributedSmoothNode):
    notify = directNotify.newCategory('DistributedDisneyChar')

    def __init__(self, cr):
        DistributedAvatar.__init__(self, cr)
        DistributedSmoothNode.__init__(self, cr)

        self.fsm = ClassicFSM('DDisneyChar', [
            State('off', self.enterOff, self.exitOff),
            State('walking', self.enterWalking, self.exitWalking),
            State('neutral', self.enterNeutral, self.exitNeutral)
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.neutralFSM = ClassicFSM('DDisneyChar-neutral', [
            State('off', self.enterOff, self.exitOff),
            State('turn2target', self.enterTurn2Target, self.exitTurn2Target),
            State('talk2target', self.enterTalk2Target, self.exitTalk2Target)
        ], 'off', 'off')
        self.neutralFSM.enterInitialState()

        self.charId = 0
        self.geoEyes = 0
        self.avatarType = CIGlobals.CChar
        self.isInRange = False
        self.currentPointLetter = "a"
        self.walkIval = None
        self.currentChat = ""
        self.talkEnabled = True
        self.speechSound = None

        self.chatsSinceLastNoise = 0
        self.chatsWithoutNoise = 5

        self.eyes = None
        self.lpupil = None
        self.rpupil = None
        self.eyesOpen = None
        self.eyesClosed = None

    def setCharId(self, charId):
        self.charId = charId

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def doNeutral(self, pointLetter):
        self.fsm.request('neutral', [pointLetter])

    def doWalking(self, pointLetter, startPointLetter, timestamp):
        ts = globalClockDelta.localElapsedTime(timestamp)
        self.fsm.request('walking', [pointLetter, startPointLetter, ts])

    def enterWalking(self, pointLetter, startPointLetter, ts):

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

        self.nametag.clearChatText()

        self.loop('walk')

        point = WALK_POINTS[self.charId][pointLetter][0]
        lastPoint = WALK_POINTS[self.charId][startPointLetter][0]

        seq = Sequence(name=self.uniqueName('DCharWalkIval'))
        if self.charId == PLUTO:
            seq.append(ActorInterval(self, 'stand'))
        elif self.charId == SLEEP_DONALD:
            seq.append(ActorInterval(self, 'neutral2walk'))
        seq.append(Func(self.loop, 'walk'))
        ival = NPCWalkInterval(self, point, startPos=lastPoint, fluid=1)
        seq.append(ival)
        seq.append(Func(self.loop, 'neutral'))
        seq.start(ts)

        self.currentPointLetter = pointLetter

        self.walkIval = ival

    def exitWalking(self):
        if self.walkIval:
            self.walkIval.finish()
            self.walkIval = None

    def enterNeutral(self, pointLetter):
        point = WALK_POINTS[self.charId][pointLetter][0]
        self.setPos(point)
        if self.charId == PLUTO:
            seq = Sequence(ActorInterval(self, 'sit'),
                           Func(self.loop, 'neutral'))
            seq.start()
        elif self.charId == SLEEP_DONALD:
            seq = Sequence(ActorInterval(self, 'walk2neutral'),
                           Func(self.loop, 'neutral'))
            seq.start()
        else:
            self.loop('neutral')

    def talk2Toon(self, chatType, chatIndex, avId):
        toon = self.cr.doId2do.get(avId)
        if not toon:
            return

        if chatType in [SHARED_GREETINGS, SHARED_COMMENTS, SHARED_GOODBYES]:
            self.currentChat = CHATTER[chatType][chatIndex]
        elif chatType in [CHAR_GREETINGS, CHAR_COMMENTS, CHAR_GOODBYES]:
            self.currentChat = CHATTER[chatType][self.charId][chatIndex]

        if '%s' in self.currentChat:
            self.currentChat = self.currentChat % toon.getName()

        self.neutralFSM.request('turn2target', [toon])

    def enterTurn2Target(self, toon):
        self.turnIval = NPCLookInterval(self,
                                        toon,
                                        fluid=1,
                                        name=self.uniqueName('turnIval'))
        if self.turnIval.distance > 30:
            self.loop('walk')
        elif self.turnIval.distance < 10.0:
            self.headsUp(toon)
            self.neutralFSM.request('talk2target')
            return
        self.turnIval.setDoneEvent(self.turnIval.getName())
        self.acceptOnce(self.turnIval.getDoneEvent(), self.__handleTurningDone)
        self.turnIval.start()

    def __handleTurningDone(self):
        self.neutralFSM.request('talk2target')

    def exitTurn2Target(self):
        self.ignore(self.turnIval.getDoneEvent())
        self.turnIval.finish()
        del self.turnIval

    def enterTalk2Target(self):
        self.setChat(self.currentChat)
        if self.getCurrentAnim() != 'neutral':
            if self.charId == SLEEP_DONALD:
                seq = Sequence(ActorInterval(self, 'walk2neutral'),
                               Func(self.loop, 'neutral'))
                seq.start()
            else:
                self.loop('neutral')

    def exitTalk2Target(self):
        pass

    def exitNeutral(self):
        self.neutralFSM.request('off')
        self.stop()

    def setChat(self, chat):
        if self.charId == SLEEP_DONALD:
            chat = "." + chat
        DistributedAvatar.setChat(self, chat)
        if self.chatsSinceLastNoise >= self.chatsWithoutNoise or self.chatsSinceLastNoise == 0:
            base.playSfx(self.speechSound, node=self)
            self.chatsSinceLastNoise = 0
            self.chatsWithoutNoise = random.randint(1, 5)
        self.chatsSinceLastNoise += 1

    def loadChar(self):
        data = CHAR_DATA[self.charId]
        self.loadModel(data[0], 'modelRoot')
        self.loadAnims(data[1], 'modelRoot')
        if self.charId == SLEEP_DONALD:
            self.setPlayRate(0.5, 'neutral')
        self.setHeight(data[2])
        self.setName(data[3])
        self.talkEnabled = data[4]
        if self.talkEnabled:
            self.speechSound = data[5]
            if self.speechSound is not None:
                base.audio3d.attachSoundToObject(self.speechSound, self)
        self.setupNameTag()

        self.ears = []

        if self.charId in [MINNIE, MICKEY]:
            for bundle in self.getPartBundleDict().values():
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                earNull.clearNetTransforms()

            for bundle in self.getPartBundleDict().values():
                charNodepath = bundle['modelRoot'].partBundleNP
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                ears = charNodepath.find('**/sphere3')
                if ears.isEmpty():
                    ears = charNodepath.find('**/*sphere3')
                ears.clearEffect(CharacterJointEffect.getClassType())
                earRoot = charNodepath.attachNewNode('earRoot')
                earPitch = earRoot.attachNewNode('earPitch')
                earPitch.setP(40.0)
                ears.reparentTo(earPitch)
                earNull.addNetTransform(earRoot.node())
                ears.clearMat()
                ears.node().setPreserveTransform(ModelNode.PTNone)
                ears.setP(-40.0)
                ears.flattenMedium()
                self.ears.append(ears)
                ears.setBillboardAxis()

            self.eyesOpen = loader.loadTexture('phase_3/maps/eyes1.jpg',
                                               'phase_3/maps/eyes1_a.rgb')
            self.eyesClosed = loader.loadTexture(
                'phase_3/maps/mickey_eyes_closed.jpg',
                'phase_3/maps/mickey_eyes_closed_a.rgb')
            self.eyes = self.find('**/eyes')
            self.eyes.setBin('transparent', 0)
            self.lpupil = self.find('**/joint_pupilL')
            self.rpupil = self.find('**/joint_pupilR')
            self.drawInFront('joint_pupil?', 'eyes*', -3)
        elif self.charId == PLUTO:
            self.eyesOpen = loader.loadTexture(
                'phase_6/maps/plutoEyesOpen.jpg',
                'phase_6/maps/plutoEyesOpen_a.rgb')
            self.eyesClosed = loader.loadTexture(
                'phase_6/maps/plutoEyesClosed.jpg',
                'phase_6/maps/plutoEyesClosed_a.rgb')
            self.eyes = self.find('**/eyes')
            self.lpupil = self.find('**/joint_pupilL')
            self.rpupil = self.find('**/joint_pupilR')
            self.drawInFront('joint_pupil?', 'eyes*', -3)
        elif self.charId == DAISY:
            self.geoEyes = 1
            self.eyeOpenList = []
            self.eyeCloseList = []
            self.eyeCloseList.append(self.find('**/eyesclose'))
            self.eyeOpenList.append(self.find('**/eyesclose'))
            self.eyeOpenList.append(self.find('**/eyespupil'))
            self.eyeOpenList.append(self.find('**/eyesopen'))
            for part in self.eyeOpenList:
                part.show()

            for part in self.eyeCloseList:
                part.hide()
        elif self.charId == SAILOR_DONALD:
            self.eyes = self.find('**/eyes')
            self.lpupil = self.find('**/joint_pupilL')
            self.rpupil = self.find('**/joint_pupilR')
            self.drawInFront('joint_pupil?', 'eyes*', -3)

        if self.lpupil is not None:
            self.lpupil.adjustAllPriorities(1)
            self.rpupil.adjustAllPriorities(1)
        if self.eyesOpen:
            self.eyesOpen.setMinfilter(Texture.FTLinear)
            self.eyesOpen.setMagfilter(Texture.FTLinear)
        if self.eyesClosed:
            self.eyesClosed.setMinfilter(Texture.FTLinear)
            self.eyesClosed.setMagfilter(Texture.FTLinear)

        if self.charId == MICKEY:
            pupilParent = self.rpupil.getParent()
            pupilOffsetNode = pupilParent.attachNewNode('pupilOffsetNode')
            pupilOffsetNode.setPos(0, 0.025, 0)
            self.rpupil.reparentTo(pupilOffsetNode)

        self.initShadow()
        self.shadow.setScale(0.6)
        self.initializeBodyCollisions(self.avatarType, self.getHeight(), 1.0)
        self.initializeRay(self.avatarType, 1)
        self.disableShadowRay()

        self.__blinkName = 'blink-' + data[3]

    def setupNameTag(self):
        DistributedAvatar.setupNameTag(self)
        self.nametag.setNametagColor(
            NametagGlobals.NametagColors[NametagGlobals.CCNPC])
        self.nametag.setActive(0)
        self.nametag.updateAll()

    def __monitorRange(self, task):
        if base.localAvatar.getDistance(self) <= MAX_RANGE:
            if self.isInRange is False:
                self.sendUpdate('avatarEnter')
                self.isInRange = True
        else:
            if self.isInRange is True:
                self.sendUpdate('avatarExit')
                self.isInRange = False

        return task.cont

    def __blinkOpenEyes(self, task):
        self.openEyes()
        r = random.random()
        if r < 0.1:
            t = 0.2
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self.__blinkCloseEyes, self.__blinkName)
        return task.done

    def __blinkCloseEyes(self, task):
        self.closeEyes()
        taskMgr.doMethodLater(0.125, self.__blinkOpenEyes, self.__blinkName)
        return task.done

    def openEyes(self):
        if self.geoEyes:
            for part in self.eyeOpenList:
                part.show()

            for part in self.eyeCloseList:
                part.hide()

        else:
            if self.eyes:
                self.eyes.setTexture(self.eyesOpen, 1)
            self.lpupil.show()
            self.rpupil.show()

    def closeEyes(self):
        if self.geoEyes:
            for part in self.eyeOpenList:
                part.hide()

            for part in self.eyeCloseList:
                part.show()

        else:
            if self.eyes:
                self.eyes.setTexture(self.eyesClosed, 1)
            self.lpupil.hide()
            self.rpupil.hide()

    def startBlink(self):
        if self.eyesOpen or self.geoEyes:
            taskMgr.remove(self.__blinkName)
            taskMgr.doMethodLater(random.random() * 4 + 1,
                                  self.__blinkCloseEyes, self.__blinkName)

    def stopBlink(self):
        if self.eyesOpen or self.geoEyes:
            taskMgr.remove(self.__blinkName)
            self.openEyes()

    def getNametagJoints(self):
        return []

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

    def announceGenerate(self):
        DistributedAvatar.announceGenerate(self)
        DistributedSmoothNode.announceGenerate(self)
        self.loadChar()
        self.startBlink()
        base.taskMgr.add(self.__monitorRange, self.uniqueName('monitorRange'))
        self.sendUpdate('requestStateData')
        if self.charId == SAILOR_DONALD:
            self.disableRay()
            self.stashBodyCollisions()
            boat = self.cr.playGame.hood.loader.geom.find('**/*donalds_boat*')
            boat.find('**/wheel').hide()
            self.setPos(0, -1, 3.95)
            self.reparentTo(boat)
            self.loop('wheel')
        else:
            self.reparentTo(render)

    def disable(self):
        base.taskMgr.remove(self.uniqueName('monitorRange'))
        self.stopBlink()
        self.fsm.requestFinalState()
        self.fsm = None
        self.neutralFSM.requestFinalState()
        self.neutralFSM = None
        self.charId = None
        self.geoEyes = None
        self.avatarType = None
        self.isInRange = None
        self.currentPointLetter = None
        self.walkIval = None
        self.currentChat = None
        self.talkEnabled = None
        self.speechSound = None
        self.chatsSinceLastNoise = None

        self.eyes = None
        self.lpupil = None
        self.rpupil = None
        self.eyesOpen = None
        self.eyesClosed = None
        DistributedAvatar.disable(self)
        Avatar.disable(self)
        DistributedSmoothNode.disable(self)
class ToonFPS(DirectObject):
    notify = directNotify.newCategory("ToonFPS")

    WeaponName2DamageData = {"pistol": (30.0, 10.0, 150.0, 0.3),
        "shotgun": (40.0, 15.0, 155.0, 0.5), "sniper": (40.0, 15.0, 155.0, 0.5)}

    def __init__(self, mg, weaponName = "pistol"):
        self.mg = mg
        self.weaponName = weaponName
        self.v_model_root = None
        self.v_model = None
        self.weapon = None
        self.track = None
        self.draw = None
        self.shoot = None
        self.reload = None
        self.empty = None
        self.cockBack = None
        self.cockFwd = None
        self.player_node = None

        # blach (02Aug15)
        # Drastically improved the accuracy of bullets... DRASTICALLY
        self.shooterTrav = None
        self.shooterRay = None
        self.shooterRayNode = None
        self.shooterHandler = None

        self.gui = ToonFPSGui(self)
        self.fsm = ClassicFSM('ToonFPS', [State('off', self.enterOff, self.exitOff),
                State('alive', self.enterAlive, self.exitAlive),
                State('dead', self.enterDead, self.exitDead)],
                'off', 'off')
        #self.deadFSM = ClassicFSM('dead', [State('off', self.enterOff, self.exitOff),
        #		State('])
        self.aliveFSM = ClassicFSM('alive', [State('off', self.enterOff, self.exitOff),
                State('draw', self.enterDraw, self.exitDraw, ['idle']),
                State('idle', self.enterIdle, self.exitIdle, ['shoot', 'reload']),
                State('shoot', self.enterShoot, self.exitShoot, ['idle']),
                State('reload', self.enterReload, self.exitReload, ['idle'])],
                'off', 'off')
        self.fsm.getStateNamed('alive').addChild(self.aliveFSM)
        #self.fsm.getStateNamed('dead').addChild(self.deadFSM)
        self.fsm.enterInitialState()
        self.aliveFSM.enterInitialState()
        if self.weaponName == "pistol":
            self.ammo = 14
        elif self.weaponName == "shotgun":
            self.ammo = 7
        self.hp = 125
        self.max_hp = 125
        self.firstPerson = FirstPerson()

    def movementTask(self, task):

        if (inputState.isSet('jump') or
            base.localAvatar.walkControls.isAirborne):

            if base.localAvatar.getAnimState() != "jump":

                base.localAvatar.setAnimState("jump")
                base.localAvatar.playMovementSfx(None)

                self.mg.sendUpdate("jumpingAvatar", [base.localAvatar.doId])

        return Task.cont

    def enterAlive(self):
        if not self.mg.fsm.getCurrentState().getName() in ['gameOver', 'announceGameOver', 'finalScores']:
            self.start()
            self.resetHp()
            self.resetAmmo()
            if self.mg.fsm.getCurrentState().getName() == "play":
                self.reallyStart()

    def exitAlive(self):
        self.end()
        self.v_model.reparentTo(hidden)
        if self.mg.fsm.getCurrentState().getName() != "play":
            self.reallyEnd()

    def updatePoints(self):
        self.points = self.kills - self.deaths

    def enterDead(self, killer):
        base.localAvatar.getGeomNode().show()
        self.gui.end()
        base.localAvatar.attachCamera()
        self.freezeCamSfx = base.loadSfx("phase_4/audio/sfx/freeze_cam.ogg")
        self.freezeCamImage = None
        self.freezeCamImageFile = None
        base.camera.setZ(base.camera.getZ() + 2.0)
        taskMgr.add(
            self.cameraLookAtKillerTask,
            "lookAtKiller",
            extraArgs = [killer],
            appendTask = True
        )
        taskMgr.doMethodLater(
            2.0,
            self.startZoomOnKiller,
            "startFreezeCam",
            extraArgs = [killer],
            appendTask = True
        )

    def startZoomOnKiller(self, killer, task):
        taskMgr.add(
            self.__zoomOnKillerTask,
            "zoomOnKiller",
            extraArgs = [killer],
            appendTask = True
        )
        return task.done

    def __zoomOnKillerTask(self, killer, task):
        if base.camera.getDistance(killer) <= 10.0 and self.freezeCamSfx.status() == self.freezeCamSfx.READY:
            base.playSfx(self.freezeCamSfx)
        if base.camera.getDistance(killer) < 7.0:
            self.doFreezeCam()
            return task.done
        base.camera.setY(base.camera, 60 * globalClock.getDt())
        return task.again

    def doFreezeCam(self):
        taskMgr.remove("lookAtKiller")
        self.frameBuffer = PNMImage()
        base.win.getScreenshot(self.frameBuffer)
        self.freezeCamTex = Texture()
        self.freezeCamTex.load(self.frameBuffer)
        self.freezeCamImage = OnscreenImage(image = self.freezeCamTex, parent=render2d)

    def cameraLookAtKillerTask(self, killer, task):
        try:
            base.camera.lookAt(killer, 0, 0, 3)
        except AssertionError:
            pass
        return task.cont

    def exitDead(self):
        taskMgr.remove("zoomOnKiller")
        taskMgr.remove("lookAtKiller")
        taskMgr.remove("startFreezeCam")
        del self.freezeCamSfx
        if self.freezeCamImage:
            self.freezeCamImage.destroy()
        del self.freezeCamImage
        self.frameBuffer.clear()
        self.freezeCamTex.clear()
        del self.frameBuffer
        del self.freezeCamTex
        base.localAvatar.detachCamera()
        base.localAvatar.getGeomNode().hide()
        self.gui.start()

    def load(self):
        if self.weaponName == "pistol":
            self.draw = base.loadSfx("phase_4/audio/sfx/draw_secondary.ogg")
            self.shoot = base.loadSfx("phase_4/audio/sfx/pistol_shoot.ogg")
            self.reload = base.loadSfx("phase_4/audio/sfx/pistol_worldreload.ogg")
        elif self.weaponName == "sniper":
            self.draw = base.loadSfx("phase_4/audio/sfx/draw_primary.ogg")
            self.shoot = base.loadSfx("phase_4/audio/sfx/shotgun_shoot.ogg")
            self.cockBack = base.loadSfx("phase_4/audio/sfx/shotgun_cock_back.ogg")
            self.cockFwd = base.loadSfx("phase_4/audio/sfx/shotgun_cock_forward.ogg")
        elif self.weaponName == "shotgun":
            self.draw = base.loadSfx("phase_4/audio/sfx/draw_primary.ogg")
            self.shoot = base.loadSfx("phase_4/audio/sfx/shotgun_shoot.ogg")
            self.cockBack = base.loadSfx("phase_4/audio/sfx/shotgun_cock_back.ogg")
            self.cockFwd = base.loadSfx("phase_4/audio/sfx/shotgun_cock_forward.ogg")
        self.empty = base.loadSfx("phase_4/audio/sfx/shotgun_empty.ogg")
        self.v_model_root = base.camera.attachNewNode('v_model_root')
        self.v_model = Actor('phase_4/models/minigames/v_dgm.egg', {'pidle': 'phase_4/models/minigames/v_dgm-pistol-idle.egg',
                        'pshoot': 'phase_4/models/minigames/v_dgm-pistol-shoot.egg',
                        'preload': 'phase_4/models/minigames/v_dgm-pistol-reload.egg',
                        'pdraw': 'phase_4/models/minigames/v_dgm-pistol-draw.egg',
                        'sidle': 'phase_4/models/minigames/v_dgm-shotgun-idle.egg',
                        'sshoot': 'phase_4/models/minigames/v_dgm-shotgun-shoot.egg'})
        if self.weaponName == "pistol":
            self.weapon = loader.loadModel("phase_4/models/props/water-gun.bam")
            self.weapon.reparentTo(self.v_model.exposeJoint(None, "modelRoot", "Bone.011"))
            self.weapon.setX(-0.125)
            self.weapon.setY(0.5)
            self.weapon.setScale(0.65)
        elif self.weaponName == "sniper":
            self.weapon = loader.loadModel("phase_4/models/props/sniper.egg")
            self.weapon.reparentTo(self.v_model.exposeJoint(None, "modelRoot", "Bone.029"))
            self.weapon.setScale(0.75)
            self.weapon.setPos(0.45, -1.03, -1.17)
            self.weapon.setHpr(9.46, 308.19, 75.78)
        elif self.weaponName == "shotgun":
            self.weapon = loader.loadModel("phase_4/models/props/shotgun.egg")
            self.weapon.reparentTo(self.v_model.exposeJoint(None, "modelRoot", "Bone.029"))
            self.weapon.setScale(0.75)
            self.weapon.setPos(0.45, -1.03, -1.17)
            self.weapon.setHpr(9.46, 308.19, 75.78)
        self.gui.load()

    def start(self):
        base.camLens.setNear(0.1)

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

        self.firstPerson.start()
        self.v_model_root.reparentTo(base.camera)
        self.v_model.reparentTo(self.v_model_root)
        if self.weaponName == "pistol":
            self.v_model_root.setZ(-1.8)
            self.v_model_root.setY(0.3)
            self.v_model_root.setX(-0.1)
            self.v_model_root.setH(2)
        elif self.weaponName == "sniper":
            self.v_model_root.setPos(-0.42, -0.81, -1.7)
            self.v_model_root.setHpr(359, 352.87, 0.00)
        elif self.weaponName == "shotgun":
            self.v_model_root.setPos(-0.42, -0.81, -1.7)
            self.v_model_root.setHpr(359, 352.87, 0.00)
        self.gui.start()
        self.firstPerson.disableMouse()
        self.aliveFSM.request('draw')

    def reallyStart(self):
        self.firstPerson.reallyStart()
        base.localAvatar.startTrackAnimToSpeed()
        #taskMgr.add(self.movementTask, "toonBattleMovement")

    def end(self):
        if self.aliveFSM.getCurrentState().getName() != 'off':
            self.aliveFSM.request('off')
        if self.firstPerson:
            self.firstPerson.enableMouse()
            self.firstPerson.end()
        taskMgr.remove("toonBattleMovement")
        if self.mg.fsm.getCurrentState().getName() != "play":
            self.fsm.request('off')

    def reallyEnd(self):
        try:
            self.ToonFPS_reallyEnded
            return
        except:
            self.ToonFPS_reallyEnded = 1
        if self.shooterRayNode:
            self.shooterRayNode.removeNode()
            self.shooterRayNode = None
        self.shooterRay = None
        self.shooterTrav = None
        self.shooterHandler = None
        if self.firstPerson:
            self.firstPerson.reallyEnd()
        if self.v_model_root:
            self.v_model_root.reparentTo(hidden)
        if self.v_model:
            self.v_model.reparentTo(hidden)
            self.v_model.setPosHpr(0, 0, 0, 0, 0, 0)
        if self.gui:
            self.gui.end()
        base.camLens.setNear(1.0)

    def cleanup(self):
        try:
            self.ToonFPS_cleanedUp
            return
        except:
            self.ToonFPS_cleanedUp = 1
        taskMgr.remove("lookAtKiller")
        taskMgr.remove("toonBattleMovement")
        if self.firstPerson:
            self.firstPerson.cleanup()
            self.firstPerson = None
        self.draw = None
        self.shoot = None
        self.reload = None
        self.empty = None
        self.ammo = None
        try:
            self.aliveFSM.requestFinalState()
            self.fsm.requestFinalState()
        except:
            self.notify.warning('Redundant call to enter the final state.')
        self.fsm = None
        self.aliveFSM = None
        self.player_node = None
        self.min_camerap = None
        self.max_camerap = None
        self.hp = None
        self.max_hp = None
        if self.v_model:
            self.v_model.cleanup()
            self.v_model = None
        if self.weapon:
            self.weapon.removeNode()
            self.weapon = None
        if self.weapon:
            self.v_model_root.removeNode()
            self.v_model_root = None
        if self.gui:
            self.gui.cleanup()

    def damageTaken(self, amount, avId):
        if self.hp <= 0.0:
            killer = self.mg.cr.doId2do.get(avId, None)
            self.fsm.request('dead', [killer])
        self.gui.adjustHpMeter()

    def enterDraw(self):
        self.draw.play()
        if self.weaponName == "pistol":
            self.track = ActorInterval(self.v_model, 'pdraw', playRate = 1.6, name = 'drawTrack')
        elif self.weaponName == "shotgun":
            self.v_model.pose('sidle', 15)
            self.track = LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0),
                startHpr = (70, -50, 0), blendType = 'easeOut', name = 'drawTrack')
        elif self.weaponName == "sniper":
            self.v_model.pose('sidle', 15)
            self.track = LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0),
                startHpr = (70, -50, 0), blendType = 'easeOut', name = 'drawTrack')
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.aliveFSM.request, ['idle'])
        self.track.start()

    def exitDraw(self):
        #self.draw.stop()
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None

    def enterIdle(self):
        if self.weaponName == "pistol":
            self.v_model.loop('pidle')
        elif self.weaponName == "shotgun":
            self.track = Sequence(LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 1, 0), startHpr = (0, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 0, 0), startHpr = (0, 1, 0), blendType = 'easeInOut'))
            self.track.loop()
        elif self.weaponName == "sniper":
            self.track = Sequence(LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 1, 0), startHpr = (0, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 0, 0), startHpr = (0, 1, 0), blendType = 'easeInOut'))
            self.track.loop()
        self.accept('mouse1', self.requestShoot)
        if self.ammo <= 0:
            self.gui.notifyNoAmmo()
        if self.ammo < 14:
            self.accept('r', self.aliveFSM.request, ['reload'])

    def requestShoot(self):
        if self.mg.fsm.getCurrentState().getName() != "play":
            return
        if self.ammo > 0:
            self.aliveFSM.request('shoot')
        else:
            self.empty.play()

    def exitIdle(self):
        self.v_model.stop()
        if self.track:
            self.track.finish()
            self.track = None
        self.ignore('mouse1')
        self.ignore('r')

    def enterShoot(self):
        self.shoot.play()
        if self.weaponName == "pistol":
            self.track = ActorInterval(self.v_model, 'pshoot', playRate = 2, name = 'shootTrack')
        elif self.weaponName == "shotgun":
            self.track = Parallel(
                Sequence(
                    LerpQuatInterval(self.v_model, duration = 0.05, quat = (0, 3, 0), startHpr = (0, 0, 0)),
                    LerpQuatInterval(self.v_model, duration = 0.1, quat = (0, 0, 0), startHpr = (0, 3, 0))
                ),
                Sequence(
                    LerpPosInterval(self.v_model, duration = 0.05, pos = (0, -0.3, 0), startPos = (0, 0, 0)),
                    LerpPosInterval(self.v_model, duration = 0.1, pos = (0, 0, 0), startPos = (0, -0.3, 0)),
                    Wait(0.1)
                ),
            )
        elif self.weaponName == "sniper":
            self.track = Parallel(
                Sequence(
                    LerpQuatInterval(self.v_model, duration = 0.05, quat = (0, 3, 0), startHpr = (0, 0, 0)),
                    LerpQuatInterval(self.v_model, duration = 0.1, quat = (0, 0, 0), startHpr = (0, 3, 0))
                ),
                Sequence(
                    LerpPosInterval(self.v_model, duration = 0.05, pos = (0, -0.3, 0), startPos = (0, 0, 0)),
                    LerpPosInterval(self.v_model, duration = 0.1, pos = (0, 0, 0), startPos = (0, -0.3, 0)),
                    Wait(0.1)
                ),
            )
        self.track.setDoneEvent('shootTrack')
        self.acceptOnce(self.track.getDoneEvent(), self.aliveFSM.request, ['idle'])
        self.track.start()
        self.ammo -= 1
        self.gui.adjustAmmoGui()
        self.mg.makeSmokeEffect(self.weapon.find('**/joint_nozzle').getPos(render))
        self.traverse()

    def traverse(self):
        mpos = base.mouseWatcherNode.getMouse()
        self.shooterRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

        self.shooterTrav.traverse(render)

    def calcDamage(self, avatar):
        dmgData = self.WeaponName2DamageData[self.weaponName]
        maxDamage = dmgData[0]
        minDistance = dmgData[1]
        maxDistance = dmgData[2]
        factor = dmgData[3]
        distance = base.localAvatar.getDistance(avatar)
        if distance < minDistance:
            distance = minDistance
        elif distance > maxDistance:
            distance = maxDistance
        damage = maxDamage - ((distance - minDistance) * factor)
        return damage

    def exitShoot(self):
        #self.shoot.stop()
        self.ignore('shootTrack')
        if self.track:
            self.track.finish()
            self.track = None

    def enterReload(self):
        self.gui.deleteNoAmmoLabel()
        if self.weaponName == "pistol":
            self.track = Parallel(Sequence(Wait(0.3), Func(self.reload.play), Func(self.resetAmmo)),
                ActorInterval(self.v_model, 'preload', playRate = 1.5), name = 'reloadTrack')
        elif self.weaponName == "shotgun":
            self.track = Sequence(Func(self.draw.play), LerpQuatInterval(self.v_model, duration = 0.5,
                quat = (70, -50, 0), startHpr = (0, 0, 0), blendType = 'easeIn'),
                SoundInterval(self.cockBack),
                SoundInterval(self.cockFwd),
                Func(self.resetAmmo),
                Func(self.draw.play),
                LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0), startHpr = (70, -50, 0),
                    blendType = 'easeOut'), name = 'reloadTrack')
        elif self.weaponName == "sniper":
            self.track = Sequence(Func(self.draw.play), LerpQuatInterval(self.v_model, duration = 0.5,
                quat = (70, -50, 0), startHpr = (0, 0, 0), blendType = 'easeIn'),
                SoundInterval(self.cockBack),
                SoundInterval(self.cockFwd),
                Func(self.resetAmmo),
                Func(self.draw.play),
                LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0), startHpr = (70, -50, 0),
                    blendType = 'easeOut'), name = 'reloadTrack')
        self.track.setDoneEvent('reloadTrack')
        self.acceptOnce(self.track.getDoneEvent(), self.aliveFSM.request, ['idle'])
        self.track.start()

    def exitReload(self):
        #self.reload.stop()
        self.ignore('reloadTrack')
        if self.track:
            self.track.finish()
            self.track = None

    def resetAmmo(self):
        if self.weaponName == "pistol":
            self.ammo = 14
        elif self.weaponName == "shotgun":
            self.ammo = 7
        elif self.weaponName == "sniper":
            self.ammo = 7
        self.gui.resetAmmo()

    def resetHp(self):
        self.hp = self.max_hp
        self.gui.adjustHpMeter()

    def enterOff(self):
        pass

    def exitOff(self):
        pass
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 Suit(Avatar.Avatar):
    healthColors = (Vec4(0, 1, 0, 1),
        Vec4(1, 1, 0, 1),
        Vec4(1, 0.5, 0, 1),
        Vec4(1, 0, 0, 1),
        Vec4(0.3, 0.3, 0.3, 1))
    healthGlowColors = (Vec4(0.25, 1, 0.25, 0.5),
        Vec4(1, 1, 0.25, 0.5),
        Vec4(1, 0.5, 0.25, 0.5),
        Vec4(1, 0.25, 0.25, 0.5),
        Vec4(0.3, 0.3, 0.3, 0))
    medallionColors = {'c': Vec4(0.863, 0.776, 0.769, 1.0),
        's': Vec4(0.843, 0.745, 0.745, 1.0),
        'l': Vec4(0.749, 0.776, 0.824, 1.0),
        'm': Vec4(0.749, 0.769, 0.749, 1.0)}
    health2DmgMultiplier = 2.5

    def __init__(self):
        try:
            self.Suit_initialized
            return
        except:
            self.Suit_initialized = 1

        Avatar.Avatar.__init__(self)
        self.avatarType = CIGlobals.Suit
        self.name = ''
        self.chat = ''
        self.suit = None
        self.suitHeads = None
        self.suitHead = None
        self.loserSuit = None
        self.healthBarGlow = None
        self.healthBar = None
        self.weapon = None
        self.weapon_sfx = None
        self.anim = None
        self.suit_dial = None
        self.shadow = None
        self.balloon_sfx = None
        self.add_sfx = None
        self.explosion = None
        self.largeExp = None
        self.smallExp = None
        self.death_sfx = None
        self.attack = None
        self.wtrajectory = None
        self.throwObjectId = None
        self.hasSpawned = False
        self.suitTrack = None
        self.headModel = None
        self.condition = 0
        self.type = ""
        self.head = ""
        self.team = ""
        self.isSkele = 0
        self.timestampAnimTrack = 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('attack', self.enterAttack, self.exitAttack),
                                State('flydown', self.enterFlyDown, self.exitFlyDown),
                                State('pie', self.enterPie, self.exitPie),
                                State('win', self.enterWin, self.exitWin),
                                State('flyaway', self.enterFlyAway, self.exitFlyAway),
                                State('rollodex', self.enterRollodex, self.exitRollodex),
                                State('flyNeutral', self.enterFlyNeutral, self.exitFlyNeutral),
                                State('flail', self.enterFlail, self.exitFlail),
                                State('drop', self.enterDrop, self.exitDrop),
                                State('drop-react', self.enterDropReact, self.exitDropReact),
                                State('squirt-large', self.enterLSquirt, self.exitLSquirt),
                                State('squirt-small', self.enterSSquirt, self.exitSSquirt),
                                State('soak', self.enterSoak, self.exitSoak)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

        self.initializeBodyCollisions()

    def delete(self):
        try:
            self.Suit_deleted
        except:
            self.Suit_deleted = 1
            Avatar.Avatar.delete(self)
            self.weapon = None
            self.weapon_sfx = None
            self.suit_dial = None
            del self.shadowPlacer

    def disable(self):
        if self.suitTrack:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.animFSM.requestFinalState()
        self.cleanupSuit()
        self.animFSM = None
        self.avatarType = None
        self.name = None
        self.chat = None
        self.suit = None
        self.state = None
        self.weapon_state = None
        self.suitHeads = None
        self.suitHead = None
        self.loserSuit = None
        self.healthMeterGlow = None
        self.healthMeter = None
        self.weapon = None
        self.weapon_sfx = None
        self.anim = None
        self.suit_dial = None
        self.shadow = None
        self.balloon_sfx = None
        self.add_sfx = None
        self.explosion = None
        self.largeExp = None
        self.smallExp = None
        self.death_sfx = None
        self.attack = None
        self.wtrajectory = None
        self.throwObjectId = None
        self.hasSpawned = None
        self.suitTrack = None
        self.headModel = None
        self.condition = None
        self.type = None
        self.head = None
        self.team = None
        self.isSkele = None
        self.timestampAnimTrack = None
        Avatar.Avatar.disable(self)

    def generateSuit(self, suitType, suitHead, suitTeam, suitHealth, skeleton, hideFirst = True):
        self.type = suitType
        self.head = suitHead
        self.isSkele = skeleton
        self.team = suitTeam
        self.health = suitHealth
        self.maxHealth = suitHealth
        self.cleanupSuit()
        self.generateBody(suitType, suitTeam, suitHead, skeleton)
        self.generateHealthMeter()
        self.generateHead(suitType, suitHead)
        #self.setupNameTag()
        self.parentSuitParts()
        self.rescaleSuit()
        Avatar.Avatar.initShadow(self)
        if hideFirst:
            self.hide()

    def rescaleSuit(self):
        self.setAvatarScale(CIGlobals.SuitScales[self.head] / CIGlobals.SuitScaleFactors[self.type])

    def parentSuitParts(self):
        if not self.isSkele:
            self.headModel.reparentTo(self.find('**/joint_head'))

    def unparentSuitParts(self):
        self.getPart('body').reparentTo(self.getGeomNode())
        if not self.isSkele:
            self.headModel.reparentTo(self.getGeomNode())

    def generateBody(self, suitType, suitTeam, suitHead, skeleton):

        self.team = suitTeam
        self.type = suitType
        self.head = suitHead
        self.isSkele = skeleton

        if suitType == "A":
            if skeleton:
                self.loadModel("phase_5/models/char/cogA_robot-zero.bam", "body")
            else:
                self.loadModel("phase_3.5/models/char/suitA-mod.bam", "body")
            self.loadAnims({"neutral": "phase_4/models/char/suitA-neutral.bam",
                            "walk": "phase_4/models/char/suitA-walk.bam",
                            "pie": "phase_4/models/char/suitA-pie-small.bam",
                            "land": "phase_5/models/char/suitA-landing.bam",
                            "throw-object": "phase_5/models/char/suitA-throw-object.bam",
                            "throw-paper": "phase_5/models/char/suitA-throw-paper.bam",
                            "glower": "phase_5/models/char/suitA-glower.bam",
                            "win": "phase_4/models/char/suitA-victory.bam",
                            "rollodex": "phase_5/models/char/suitA-roll-o-dex.bam",
                            "pickpocket": "phase_5/models/char/suitA-pickpocket.bam",
                            "fountainpen": "phase_7/models/char/suitA-fountain-pen.bam",
                            "phone": "phase_5/models/char/suitA-phone.bam",
                            "flail": "phase_4/models/char/suitA-flailing.bam",
                            "drop" : "phase_5/models/char/suitA-drop.bam",
                            "drop-react" : "phase_5/models/char/suitA-anvil-drop.bam",
                            "squirt-large" : "phase_5/models/char/suitA-squirt-large.bam",
                            "squirt-small" : "phase_4/models/char/suitA-squirt-small.bam",
                            "slip-forward" : "phase_4/models/char/suitA-slip-forward.bam",
                            "slip-backward" : "phase_4/models/char/suitA-slip-backward.bam",
                            "sit": "phase_12/models/char/suitA-sit.bam",
                            "speak": "phase_5/models/char/suitA-speak.bam",
                            "fingerwag": "phase_5/models/char/suitA-fingerwag.bam",
                            "soak" : "phase_5/models/char/suitA-soak.bam"}, "body")
        if suitType == "B":
            if skeleton:
                self.loadModel("phase_5/models/char/cogB_robot-zero.bam", "body")
            else:
                self.loadModel("phase_3.5/models/char/suitB-mod.bam", "body")
            self.loadAnims({"neutral": "phase_4/models/char/suitB-neutral.bam",
                            "walk": "phase_4/models/char/suitB-walk.bam",
                            "pie": "phase_4/models/char/suitB-pie-small.bam",
                            "land": "phase_5/models/char/suitB-landing.bam",
                            "throw-object": "phase_5/models/char/suitB-throw-object.bam",
                            "throw-paper": "phase_5/models/char/suitB-throw-paper.bam",
                            "glower": "phase_5/models/char/suitB-magic1.bam",
                            "win": "phase_4/models/char/suitB-victory.bam",
                            "rollodex": "phase_5/models/char/suitB-roll-o-dex.bam",
                            "pickpocket": "phase_5/models/char/suitB-pickpocket.bam",
                            "fountainpen": "phase_5/models/char/suitB-pen-squirt.bam",
                            "phone": "phase_5/models/char/suitB-phone.bam",
                            "flail": "phase_4/models/char/suitB-flailing.bam",
                            "drop" : "phase_5/models/char/suitB-drop.bam",
                            "drop-react" : "phase_5/models/char/suitB-anvil-drop.bam",
                            "squirt-large" : "phase_5/models/char/suitB-squirt-large.bam",
                            "squirt-small" : "phase_4/models/char/suitB-squirt-small.bam",
                            "slip-forward" : "phase_4/models/char/suitB-slip-forward.bam",
                            "slip-backward" : "phase_4/models/char/suitB-slip-backward.bam",
                            "speak": "phase_5/models/char/suitB-speak.bam",
                            "fingerwag": "phase_5/models/char/suitB-finger-wag.bam",
                            "soak" : "phase_5/models/char/suitB-soak.bam"}, "body")
        if suitType == "C":
            if skeleton:
                self.loadModel("phase_5/models/char/cogC_robot-zero.bam", "body")
            else:
                self.loadModel("phase_3.5/models/char/suitC-mod.bam", "body")
            self.loadAnims({"neutral": "phase_3.5/models/char/suitC-neutral.bam",
                        "walk": "phase_3.5/models/char/suitC-walk.bam",
                        "pie": "phase_3.5/models/char/suitC-pie-small.bam",
                        "land": "phase_5/models/char/suitC-landing.bam",
                        "throw-object": "phase_3.5/models/char/suitC-throw-paper.bam",
                        "throw-paper": "phase_3.5/models/char/suitC-throw-paper.bam",
                        "glower": "phase_5/models/char/suitC-glower.bam",
                        "win": "phase_4/models/char/suitC-victory.bam",
                        "pickpocket": "phase_5/models/char/suitC-pickpocket.bam",
                        "fountainpen": "phase_5/models/char/suitC-fountain-pen.bam",
                        "phone": "phase_3.5/models/char/suitC-phone.bam",
                        "flail": "phase_4/models/char/suitC-flailing.bam",
                        "drop" : "phase_5/models/char/suitC-drop.bam",
                        "drop-react" : "phase_5/models/char/suitC-anvil-drop.bam",
                        "squirt-large" : "phase_5/models/char/suitC-squirt-large.bam",
                        "squirt-small" : "phase_3.5/models/char/suitC-squirt-small.bam",
                        "slip-forward" : "phase_4/models/char/suitC-slip-forward.bam",
                        "slip-backward" : "phase_4/models/char/suitC-slip-backward.bam",
                        "sit": "phase_12/models/char/suitC-sit.bam",
                        "speak": "phase_5/models/char/suitC-speak.bam",
                        "fingerwag": "phase_5/models/char/suitC-finger-wag.bam",
                        "soak" : "phase_5/models/char/suitC-soak.bam"}, "body")
        if skeleton:
            self.setTwoSided(1)

        if skeleton:
            if suitTeam == "s":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_sales.jpg")
            elif suitTeam == "m":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_money.jpg")
            elif suitTeam == "l":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_legal.jpg")
            elif suitTeam == "c":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_boss.jpg")
            self.find('**/tie').setTexture(self.suit_tie, 1)
        else:
            if hasattr(self, 'getBattle') and self.getBattle() != None:
                if self.getBattle().getHoodIndex() == CogBattleGlobals.WaiterHoodIndex:
                    self.suit_blazer = loader.loadTexture("phase_3.5/maps/waiter_m_blazer.jpg")
                    self.suit_leg = loader.loadTexture("phase_3.5/maps/waiter_m_leg.jpg")
                    self.suit_sleeve = loader.loadTexture("phase_3.5/maps/waiter_m_sleeve.jpg")
            if not hasattr(self, 'getBattle') or self.getBattle() == None or self.getBattle().getHoodIndex() != CogBattleGlobals.WaiterHoodIndex:
                self.suit_blazer = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_blazer.jpg")
                self.suit_leg = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_leg.jpg")
                self.suit_sleeve = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_sleeve.jpg")

            self.find('**/legs').setTexture(self.suit_leg, 1)
            self.find('**/arms').setTexture(self.suit_sleeve, 1)
            self.find('**/torso').setTexture(self.suit_blazer, 1)

            if suitHead == "coldcaller":
                self.find('**/hands').setColor(0.55, 0.65, 1.0, 1.0)
            elif suitHead == "corporateraider":
                self.find('**/hands').setColor(0.85, 0.55, 0.55, 1.0)
            elif suitHead == "bigcheese":
                self.find('**/hands').setColor(0.75, 0.95, 0.75, 1.0)
            elif suitHead == "bloodsucker":
                self.find('**/hands').setColor(0.95, 0.95, 1.0, 1.0)
            elif suitHead == "spindoctor":
                self.find('**/hands').setColor(0.5, 0.8, 0.75, 1.0)
            elif suitHead == "legaleagle":
                self.find('**/hands').setColor(0.25, 0.25, 0.5, 1.0)
            elif suitHead == "pennypincher":
                self.find('**/hands').setColor(1.0, 0.5, 0.6, 1.0)
            elif suitHead == "loanshark":
                self.find('**/hands').setColor(0.5, 0.85, 0.75, 1.0)
            else:
                self.find('**/hands').setColor(CIGlobals.SuitHandColors[suitTeam])

    def generateHead(self, suitType, suitHead):

        self.type = suitType
        self.head = suitHead

        if suitHead == "vp":
            self.headModel = Actor("phase_9/models/char/sellbotBoss-head-zero.bam")
            self.headModel.loadAnims({"neutral": "phase_9/models/char/bossCog-head-Ff_neutral.bam"})
            self.headModel.setTwoSided(True)
            self.headModel.loop("neutral")
            self.headModel.setScale(0.35)
            self.headModel.setHpr(270, 0, 270)
            self.headModel.setZ(-0.10)
        else:
            if suitType == "A" or suitType == "B":
                heads = loader.loadModel("phase_4/models/char/suit" + suitType + "-heads.bam")
            else:
                heads = loader.loadModel("phase_3.5/models/char/suit" + suitType + "-heads.bam")
            self.headModel = heads.find('**/' + CIGlobals.SuitHeads[suitHead])
            if suitHead == "flunky":
                glasses = heads.find('**/glasses')
                glasses.reparentTo(self.headModel)
                glasses.setTwoSided(True)
            if suitHead in CIGlobals.SuitSharedHeads:
                if suitHead == "coldcaller":
                    self.headModel.setColor(0.25, 0.35, 1.0, 1.0)
                else:
                    headTexture = loader.loadTexture("phase_3.5/maps/" + suitHead + ".jpg")
                    self.headModel.setTexture(headTexture, 1)

    def cleanupSuit(self):
        self.removeHealthBar()
        if 'body' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['body']
        if self.shadow:
            self.deleteShadow()
        if self.headModel:
            self.headModel.removeNode()
            self.headModel = None
        if self.getPart('body'):
            self.removePart('body')

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

    def setupNameTag(self):
        Avatar.Avatar.setupNameTag(self)
        self.nameTag.setText(self.nameTag.getText() + "\nLevel %d" % self.level)

    def setChat(self, chatString):
        self.chat = chatString
        if self.isSkele:
            if "?" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_question.ogg")
            elif "!" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_grunt.ogg")
            else:
                self.suit_dial = base.audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_statement.ogg")
        elif self.head in ["vp"]:
            if "?" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_question.ogg")
            elif "!" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_grunt.ogg")
            else:
                self.suit_dial = base.audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_statement.ogg")
        else:
            if "?" in chatString:
                self.suit_dial = base.audio3d.loadSfx(
                    random.choice(
                        [
                            "phase_3.5/audio/dial/COG_VO_question.ogg",
                            "phase_3.5/audio/dial/COG_VO_question_2.ogg"
                        ]
                    )
                )
            elif "!" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_grunt.ogg")
            else:
                self.suit_dial = base.audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_statement.ogg")
        if self.isSkele:
            base.audio3d.attachSoundToObject(self.suit_dial, self)
        else:
            base.audio3d.attachSoundToObject(self.suit_dial, self.headModel)
        self.suit_dial.play()
        Avatar.Avatar.setChat(self, chatString)

    def generateHealthMeter(self):
        self.removeHealthBar()
        model = loader.loadModel("phase_3.5/models/gui/matching_game_gui.bam")
        button = model.find('**/minnieCircle')
        button.setScale(3.0)
        button.setH(180)
        button.setColor(self.healthColors[0])
        chestNull = self.find('**/def_joint_attachMeter')
        if chestNull.isEmpty():
            chestNull = self.find('**/joint_attachMeter')
        button.reparentTo(chestNull)
        self.healthBar = button
        glow = loader.loadModel("phase_3.5/models/props/glow.bam")
        glow.reparentTo(self.healthBar)
        glow.setScale(0.28)
        glow.setPos(-0.005, 0.01, 0.015)
        glow.setColor(self.healthGlowColors[0])
        button.flattenLight()
        self.healthBarGlow = glow
        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
        #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(self.healthColors[condition], 1)
                self.healthBarGlow.setColor(self.healthGlowColors[condition], 1)
            self.condition = condition

    def __blinkRed(self, task):
        self.healthBar.setColor(self.healthColors[3], 1)
        self.healthBarGlow.setColor(self.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(self.healthColors[4], 1)
        self.healthBarGlow.setColor(self.healthGlowColors[4], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.0)
        return Task.done

    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 enterOff(self, ts = 0):
        self.anim = None
        return

    def exitOff(self):
        pass

    def exitGeneral(self):
        self.stop()

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

    def exitFlail(self):
        self.stop()

    def enterNeutral(self, ts = 0):
        self.show()
        self.timestampAnimTrack = Sequence(Wait(ts), Func(self.loop, "neutral"))
        self.timestampAnimTrack.start()

    def exitNeutral(self):
        self.timestampAnimTrack.pause()
        self.timestampAnimTrack = None
        self.exitGeneral()

    def enterRollodex(self, ts = 0):
        self.play("rollodex")

    def exitRollodex(self):
        self.exitGeneral()

    def enterWalk(self, ts = 0):
        self.show()
        self.timestampAnimTrack = Sequence(Wait(ts), Func(self.loop, "walk"))
        self.timestampAnimTrack.start()
        self.disableShadowRay()

    def exitWalk(self):
        self.timestampAnimTrack.pause()
        self.timestampAnimTrack = None
        self.exitGeneral()
        self.enableShadowRay()

    def __moveSuitUpForDropAnim(self):
        self.getGeomNode().setZ(0.5)

    def __moveSuitDownToNormal(self):
        self.getGeomNode().setZ(0)

    def enterDrop(self, ts = 0):
        self.suitTrack = Parallel(
            ActorInterval(self, 'drop')#,
            #Sequence(Wait(0.1), Func(self.__moveSuitUpForDropAnim), Wait(2.7), Func(self.__moveSuitDownToNormal))
        )
        self.suitTrack.start()

    def exitDrop(self):
        if self.suitTrack:
            self.suitTrack.finish()
            self.suitTrack = None

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

    def exitDropReact(self):
        self.stop()

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

    def exitSSquirt(self):
        self.stop()

    def enterLSquirt(self, ts = 0):
        self.play('squirt-large')

    def exitLSquirt(self):
        self.stop()

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

    def exitSoak(self):
        self.stop()

    def generateLoserSuit(self):
        if not self.isSkele:
            handColor = self.find('**/hands').getColor()
        self.cleanupSuit()
        if self.type == "A":
            if self.isSkele:
                self.loadModel("phase_5/models/char/cogA_robot-lose-mod.bam", 'body')
            else:
                self.loadModel("phase_4/models/char/suitA-lose-mod.bam", 'body')
            self.loadAnims({"lose": "phase_4/models/char/suitA-lose.bam"}, 'body')
        if self.type == "B":
            if self.isSkele:
                self.loadModel("phase_5/models/char/cogB_robot-lose-mod.bam", 'body')
            else:
                self.loadModel("phase_4/models/char/suitB-lose-mod.bam", 'body')
            self.loadAnims({"lose": "phase_4/models/char/suitB-lose.bam"}, 'body')
        if self.type == "C":
            if self.isSkele:
                self.loadModel("phase_5/models/char/cogC_robot-lose-mod.bam", 'body')
            else:
                self.loadModel("phase_3.5/models/char/suitC-lose-mod.bam", 'body')
            self.loadAnims({"lose": "phase_3.5/models/char/suitC-lose.bam"}, 'body')
        if self.isSkele:
            self.find('**/tie').setTexture(self.suit_tie, 1)
            self.setTwoSided(1)
        else:
            self.find('**/hands').setColor(handColor)
            self.find('**/legs').setTexture(self.suit_leg, 1)
            self.find('**/arms').setTexture(self.suit_sleeve, 1)
            self.find('**/torso').setTexture(self.suit_blazer, 1)
        self.generateHead(self.type, self.head)
        self.rescaleSuit()
        self.parentSuitParts()
        self.deleteNameTag()
        Avatar.Avatar.initShadow(self)

    def enterDie(self, ts = 0):
        self.show()
        self.generateLoserSuit()
        self.clearChat()
        self.state = "dead"
        self.play("lose")
        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.7), Func(self.doSingleGear), Wait(4.3),
                Func(self.suitExplode), Wait(1.0), Func(self.delSuit)), gearTrack, name = trackName)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        Sequence(Wait(0.8), SoundInterval(deathSound)).start()
        self.acceptOnce(self.suitTrack.getName(), self.exitDie)
        if "Distributed" in self.__class__.__name__:
            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()
        self.explosion.setDepthWrite(False)
        if self.isSkele:
            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 delSuit(self):
        self.disableBodyCollisions()

    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
        if self.explosion:
            self.explosion.removeNode()
            self.explosion = None

    def enterFlyNeutral(self, ts = 0):
        self.disableRay()
        self.sfx = base.audio3d.loadSfx("phase_4/audio/sfx/TB_propeller.ogg")
        self.prop = Actor("phase_4/models/props/propeller-mod.bam",
                        {"chan": "phase_4/models/props/propeller-chan.bam"})
        base.audio3d.attachSoundToObject(self.sfx, self.prop)
        self.prop.reparentTo(self.find('**/joint_head'))
        self.sfx.setLoop(True)
        self.sfx.play()
        self.prop.loop('chan', fromFrame = 0, toFrame = 3)
        self.setPlayRate(0.8, 'land')
        self.pingpong('land', fromFrame = 0, toFrame = 10)

    def exitFlyNeutral(self):
        self.prop.cleanup()
        del self.prop
        base.audio3d.detachSound(self.sfx)
        del self.sfx
        self.stop()

    def enterFlyDown(self, ts = 0):
        self.disableRay()
        self.fd_sfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_propeller_in.ogg")
        self.prop = Actor("phase_4/models/props/propeller-mod.bam",
                        {"chan": "phase_4/models/props/propeller-chan.bam"})
        base.audio3d.attachSoundToObject(self.fd_sfx, self.prop)
        self.prop.reparentTo(self.find('**/joint_head'))
        self.fd_sfx.play()
        dur = self.getDuration('land')
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyDown')
        else:
            name = 'enterFlyDown'
        self.suitTrack = Parallel(Sequence(Func(self.pose, 'land', 0), Func(self.prop.loop, 'chan', fromFrame=0, toFrame=3),
                            Wait(1.75),
                            Func(self.prop.play, 'chan', fromFrame=3),
                            Wait(0.15),
                            ActorInterval(self, 'land', duration=dur)), name = name)
        if not self.hasSpawned:
            showSuit = Sequence(Func(self.hideSuit), Wait(0.3), Func(self.showSuit))
            self.hasSpawned = True
            self.suitTrack.append(showSuit)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyAway)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)

    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 initializeLocalCollisions(self, name):
        Avatar.Avatar.initializeLocalCollisions(self, 1, 3, name)

    def initializeBodyCollisions(self):
        Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 6, 2)
        self.initializeRay(self.avatarType, 2)

    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
        if hasattr(self, 'fd_sfx'):
            base.audio3d.detachSound(self.fd_sfx)
        self.exitGeneral()
        if self.prop:
            self.prop.cleanup()
            self.prop = None

    def enterFlyAway(self, ts = 0):
        self.show()
        self.fa_sfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_propeller_out.ogg")
        self.prop = Actor("phase_4/models/props/propeller-mod.bam",
                        {"chan": "phase_4/models/props/propeller-chan.bam"})
        base.audio3d.attachSoundToObject(self.fa_sfx, self.prop)
        self.fa_sfx.play()
        self.prop.reparentTo(self.find('**/joint_head'))
        self.prop.setPlayRate(-1.0, "chan")
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyAway')
        else:
            name = 'enterFlyAway'
        self.suitTrack = Sequence(Func(self.prop.play, 'chan', fromFrame=3),
                            Wait(1.75),
                            Func(self.prop.play, 'chan', fromFrame=0, toFrame=3), name = name)
        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.setPlayRate(-1.0, 'land')
        self.play('land')

    def exitFlyAway(self):
        if self.suitTrack:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'fa_sfx'):
            base.audio3d.detachSound(self.fa_sfx)
        self.exitGeneral()
        if self.prop:
            self.prop.cleanup()
            self.prop = 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.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()

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

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

    def exitPie(self):
        self.exitGeneral()

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

    def exitWin(self):
        self.exitGeneral()
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 DistributedCityCart(DistributedNode):
    notify = directNotify.newCategory('DistributedCityCart')

    def __init__(self, cr):
        DistributedNode.__init__(self, cr)
        self.fsm = ClassicFSM('DistributedCityCart', [State('off', self.enterOff, self.exitOff),
         State('pathFollow', self.enterPathFollow, self.exitPathFollow),
         State('collision', self.enterCollision, self.exitCollision)], 'off', 'off')
        self.fsm.enterInitialState()
        self.suitInCar = None
        self.cart = None
        self.honkSfxPath = 'phase_14/audio/sfx/cogtropolis_citycar_driveby_horn.ogg'
        self.cartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam'
        self.moPaths = ['phase_14/models/paths/ct-citycar-drivepath-1.egg',
            'phase_14/models/paths/ct-citycar-drivepath-2.egg',
            'phase_14/models/paths/ct-citycar-drivepath-3.egg',
            'phase_14/models/paths/ct-citycar-drivepath-4.egg',
            'phase_14/models/paths/ct-citycar-drivepath-5.egg',
            'phase_14/models/paths/ct-citycar-drivepath-6.egg']
        self.moPath = None
        self.soundEngineLoop = None
        self.soundDriveByHorn = None
        self.ivalTDisplace = None
        self.pathIndex = None
        self.wheelSpinTrack = None
        self.collNodePath = None
        self.soundDriveBy = None

    def setIvalTDisplace(self, displace):
        self.ivalTDisplace = displace

    def setPathIndex(self, index):
        self.pathIndex = index

    def setState(self, state, timestamp):
        ts = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
        self.fsm.request(state, [ts])

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterPathFollow(self, ts):
        duration = CityCartGlobals.index2Duration[self.pathIndex]
        self.moPath = NURBSMopath.NURBSMopath(self.moPaths[self.pathIndex], name = self.uniqueName('DCityCart_moPath'))
        startT = 0.0
        if ts > 0.0:
            startT = (ts % duration) * (1.0 / duration)
        self.moPath.play(self, loop = True, duration = duration, startT = startT)
        base.taskMgr.add(self.__drive, self.uniqueName('DCityCart.drive'))
        self.wheelSpinTrack = Parallel()
        for name in ['leftFrontWheel', 'rightBackWheel', 'rightFrontWheel', 'leftBackWheel']:
            wheel = self.find('**/' + name)
            self.wheelSpinTrack.append(LerpHprInterval(wheel, duration = 0.1, hpr = (0, 360, 0), startHpr = (0, 0, 0)))
        self.wheelSpinTrack.loop()
        self.accept('enter' + self.collNodePath.node().getName(), self.__handleRanOver)

    def __handleRanOver(self, entry):
        self.suitInCar.setChat(CityCartGlobals.SuitRanOverTaunt)
        self.sendUpdate('hitByCar')
        self.cr.playGame.getPlace().fsm.request('stop')
        base.localAvatar.b_setAnimState('squish', callback = self.cr.playGame.getPlace().fsm.request, extraArgs = ['walk'])

    def __drive(self, task):
        if base.localAvatar.getDistance(self) < 10.0:
            if self.soundDriveByHorn.status() == self.soundDriveByHorn.READY:
                wantsToHonk = random.randint(0, 3)
                if wantsToHonk == 3:
                    base.playSfx(self.soundDriveByHorn)
                return task.cont
        elif base.localAvatar.getDistance(self) < 20.0:
            if self.soundDriveBy.status() == self.soundDriveBy.READY:
                base.playSfx(self.soundDriveBy)
                return task.cont
        return task.cont

    def exitPathFollow(self):
        self.ignore('enter' + self.collNodePath.node().getName())
        base.taskMgr.remove(self.uniqueName('DCityCart.drive'))
        if self.wheelSpinTrack:
            self.wheelSpinTrack.finish()
            self.wheelSpinTrack = None
        if self.moPath:
            self.moPath.stop()
            self.moPath = None

    def enterCollision(self, ts):
        pass

    def exitCollision(self):
        pass

    def generate(self):
        DistributedNode.generate(self)
        self.cart = loader.loadModel(self.cartModelPath)
        self.cart.reparentTo(self)
        self.cart.setH(180)
        plans = []
        for plan in SuitBank.getSuits():
            if plan.getSuitType() != SuitType.B:
                plans.append(plan)
        plan = random.choice(plans)
        self.suitInCar = Suit()
        self.suitInCar.level = 0
        self.suitInCar.generate(plan, Variant.NORMAL)
        self.suitInCar.loop('sit')
        self.suitInCar.disableRay()
        self.suitInCar.setScale(0.7)
        self.suitInCar.setH(180)
        self.suitInCar.setPos(0, -1, -1.5)
        self.suitInCar.reparentTo(self.cart.find('**/seat1'))
        self.suitInCar.show()
        self.soundEngineLoop = base.audio3d.loadSfx('phase_6/audio/sfx/KART_Engine_loop_0.ogg')
        base.audio3d.attachSoundToObject(self.soundEngineLoop, self)
        base.playSfx(self.soundEngineLoop, looping = 1)
        self.soundDriveByHorn = base.audio3d.loadSfx(self.honkSfxPath)
        base.audio3d.attachSoundToObject(self.soundDriveByHorn, self)
        self.soundDriveBy = base.audio3d.loadSfx('phase_14/audio/sfx/cogtropolis_citycar_driveby.ogg')
        base.audio3d.attachSoundToObject(self.soundDriveBy, self)
        sphere = CollisionSphere(0, 0, 0, 2.5)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('cartSphere'))
        node.setCollideMask(CIGlobals.WallBitmask)
        node.addSolid(sphere)
        self.collNodePath = self.attachNewNode(node)
        self.collNodePath.setZ(1.5)
        self.collNodePath.setSy(2.0)
        self.collNodePath.setSx(1.75)

    def disable(self):
        self.fsm.requestFinalState()
        if self.moPath:
            self.moPath.stop()
            self.moPath = None
        self.moPaths = None
        self.honkSfxPath = None
        self.cartModelPath = None
        self.soundEngineLoop = None
        self.soundDriveBy = None
        if self.suitInCar:
            self.suitInCar.disable()
            self.suitInCar.delete()
            self.suitInCar = None
        if self.cart:
            self.cart.removeNode()
            self.cart = None
        del self.fsm
        DistributedNode.disable(self)
예제 #22
0
class AdminPage(StateData):
    notify = directNotify.newCategory('AdminPage')

    def __init__(self, book, parentFSM):
        self.book = book
        self.parentFSM = parentFSM
        StateData.__init__(self, 'adminPageDone')
        self.fsm = ClassicFSM('AdminPage', [
            State('off', self.enterOff, self.exitOff),
            State('basePage', self.enterBasePage, self.exitBasePage),
            State('kickSection', self.enterKickSection, self.exitKickSection),
            State('sysMsgSection', self.enterSysMsgSection,
                  self.exitSysMsgSection)
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.parentFSM.getStateNamed('adminPage').addChild(self.fsm)

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enter(self):
        StateData.enter(self)
        self.fsm.request('basePage')

    def exit(self):
        self.fsm.requestFinalState()
        StateData.exit(self)

    def unload(self):
        del self.book
        del self.parentFSM
        del self.fsm
        StateData.unload(self)

    def enterSysMsgSection(self):
        self.book.createPageButtons(None, None)
        self.book.setTitle('System Message')
        geom = CIGlobals.getDefaultBtnGeom()
        self.infoLbl = OnscreenText(
            text='Inform all online players about something.', pos=(0, 0.45))
        self.msgEntry = DirectEntry(
            initialText='System Message...',
            scale=0.055,
            width=15,
            numLines=4,
            command=self.sendSystemMessageCommand,
            focusInCommand=base.localAvatar.chatInput.disableKeyboardShortcuts,
            focusOutCommand=base.localAvatar.chatInput.enableKeyboardShortcuts,
            pos=(-0.4, 0, 0))
        self.sendBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text='Send',
                                    pos=(0, 0, -0.35),
                                    text_pos=(0, -0.01),
                                    command=self.sendSystemMessageCommand)
        self.cancelBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text='Cancel',
                                      pos=(-0.45, 0.15, -0.55),
                                      text_pos=(0, -0.01),
                                      command=self.fsm.request,
                                      extraArgs=['basePage'])
        return

    def sendSystemMessageCommand(self, foo=None):
        msg = self.msgEntry.get()
        base.cr.myDistrict.sendUpdate('systemMessageCommand',
                                      [base.localAvatar.getAdminToken(), msg])
        self.fsm.request('basePage')

    def exitSysMsgSection(self):
        self.infoLbl.destroy()
        del self.infoLbl
        self.msgEntry.destroy()
        del self.msgEntry
        self.sendBtn.destroy()
        del self.sendBtn
        self.cancelBtn.destroy()
        del self.cancelBtn
        self.book.clearTitle()
        self.book.deletePageButtons(False, False)

    def enterKickSection(self):
        self.book.createPageButtons(None, None)
        self.book.setTitle('Kick Player')
        geom = CIGlobals.getDefaultBtnGeom()
        self.infoLbl = OnscreenText(
            text='Type the ID of the player you want to boot out.',
            pos=(0, 0.45))
        self.idEntry = DirectEntry(
            width=10,
            scale=0.12,
            pos=(-0.59, 0, 0.15),
            command=self.sendKickMessage,
            focusInCommand=base.localAvatar.chatInput.disableKeyboardShortcuts,
            focusOutCommand=base.localAvatar.chatInput.enableKeyboardShortcuts)
        self.kickBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text='Kick',
                                    pos=(0, 0, -0.15),
                                    text_pos=(0, -0.01),
                                    command=self.sendKickMessage)
        self.banBtn = DirectButton(geom=geom,
                                   text_scale=0.04,
                                   relief=None,
                                   scale=1.0,
                                   text='Ban',
                                   pos=(0, 0, -0.25),
                                   text_pos=(0, -0.01),
                                   command=self.sendKickMessage,
                                   extraArgs=[None, 1])
        self.cancelBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=1.0,
                                      text='Cancel',
                                      pos=(-0.45, 0.15, -0.45),
                                      text_pos=(0, -0.01),
                                      command=self.fsm.request,
                                      extraArgs=['basePage'])
        return

    def sendKickMessage(self, foo=None, andBan=0):
        if self.idEntry.get().isspace() or len(self.idEntry.get()) == 0:
            return
        print 'Sending out kick request for avatar id: ' + str(
            self.idEntry.get())
        base.localAvatar.sendUpdate('requestEject',
                                    [int(self.idEntry.get()), andBan])
        self.fsm.request('basePage')

    def exitKickSection(self):
        self.banBtn.destroy()
        del self.banBtn
        self.infoLbl.destroy()
        del self.infoLbl
        self.cancelBtn.destroy()
        del self.cancelBtn
        self.idEntry.destroy()
        del self.idEntry
        self.kickBtn.destroy()
        del self.kickBtn
        self.book.deletePageButtons(False, False)
        self.book.clearTitle()

    def enterBasePage(self):
        self.book.createPageButtons('mapPage', None)
        self.book.setTitle('Admin Stuff')
        geom = CIGlobals.getDefaultBtnGeom()
        self.suitSpawnerBtn = DirectButton(geom=geom,
                                           text_scale=0.04,
                                           relief=None,
                                           scale=1.0,
                                           text='',
                                           pos=(-0.45, 0.15, 0.5),
                                           text_pos=(0, -0.01),
                                           command=self.sendSuitCommand,
                                           extraArgs=['suitSpawner'])
        self.killCogsBtn = DirectButton(geom=geom,
                                        text_scale=0.04,
                                        relief=None,
                                        scale=1.0,
                                        text='Kill All Cogs',
                                        pos=(-0.45, 0.15, 0.4),
                                        text_pos=(0, -0.01),
                                        command=self.sendSuitCommand,
                                        extraArgs=['killCogs'])
        self.makeTournamentBtn = DirectButton(geom=geom,
                                              text_scale=0.04,
                                              relief=None,
                                              scale=1.0,
                                              text='Make Cog Tournament',
                                              pos=(-0.45, 0.15, 0.3),
                                              text_pos=(0, -0.01),
                                              command=self.sendSuitCommand,
                                              extraArgs=['tournament'])
        self.makeInvasionBtn = DirectButton(geom=geom,
                                            text_scale=0.04,
                                            relief=None,
                                            scale=1.0,
                                            text='Make Cog Invasion',
                                            pos=(-0.45, 0.15, 0.2),
                                            text_pos=(0, -0.01),
                                            command=self.sendSuitCommand,
                                            extraArgs=['invasion'])
        self.makeCogBtn = DirectButton(geom=geom,
                                       text_scale=0.04,
                                       relief=None,
                                       scale=1.0,
                                       text='Make Cog',
                                       pos=(-0.45, 0.15, 0.1),
                                       text_pos=(0, -0.01),
                                       command=self.sendSuitCommand,
                                       extraArgs=['suit'])
        self.ghostBtn = DirectButton(geom=geom,
                                     text_scale=0.04,
                                     relief=None,
                                     scale=1.0,
                                     text='',
                                     pos=(0.45, 0.15, 0.5),
                                     text_pos=(0, -0.01),
                                     command=self.changeGhost)
        self.bgBtn = DirectButton(geom=geom,
                                  text_scale=0.04,
                                  relief=None,
                                  scale=1.0,
                                  text='Toggle Background',
                                  pos=(0.45, 0.15, 0.4),
                                  text_pos=(0, -0.01),
                                  command=self.toggleBackground)
        self.idBtn = DirectButton(geom=geom,
                                  text_scale=0.04,
                                  relief=None,
                                  scale=1.0,
                                  text='Toggle Player Ids',
                                  pos=(0.45, 0.15, 0.3),
                                  text_pos=(0, -0.01),
                                  command=self.togglePlayerIds)
        self.kickBtn = DirectButton(geom=geom,
                                    text_scale=0.04,
                                    relief=None,
                                    scale=1.0,
                                    text='Kick Player',
                                    pos=(0.45, 0.15, 0.2),
                                    text_pos=(0, -0.01),
                                    command=self.openKickPage)
        self.systemMsgBtn = DirectButton(geom=geom,
                                         text_scale=0.04,
                                         relief=None,
                                         scale=1.0,
                                         text='System Message',
                                         pos=(0.45, 0.15, 0.1),
                                         text_pos=(0, -0.01),
                                         command=self.openSysMsgPage)
        if base.localAvatar.getGhost():
            self.ghostBtn['text'] = 'Turn Ghost Off'
        else:
            self.ghostBtn['text'] = 'Turn Ghost On'
        base.cr.playGame.getPlace().maybeUpdateAdminPage()
        del geom
        return

    def togglePlayerIds(self):
        if base.cr.isShowingPlayerIds:
            base.cr.hidePlayerIds()
        else:
            base.cr.showPlayerIds()

    def toggleBackground(self):
        if render.isHidden():
            render.show()
        else:
            render.hide()
        if self.book.book_img.isHidden():
            self.book.book_img.show()
        else:
            self.book.book_img.hide()

    def changeGhost(self):
        if base.localAvatar.getGhost():
            base.localAvatar.b_setGhost(0)
            self.ghostBtn['text'] = 'Turn Ghost On'
        else:
            base.localAvatar.b_setGhost(1)
            self.ghostBtn['text'] = 'Turn Ghost Off'

    def sendSuitCommand(self, commandName):
        if base.cr.playGame.suitManager:
            base.cr.playGame.suitManager.sendUpdate(
                'suitAdminCommand',
                [base.localAvatar.getAdminToken(), commandName])

    def openKickPage(self):
        self.fsm.request('kickSection')

    def openSysMsgPage(self):
        self.fsm.request('sysMsgSection')

    def exitBasePage(self):
        self.systemMsgBtn.destroy()
        del self.systemMsgBtn
        self.idBtn.destroy()
        del self.idBtn
        self.kickBtn.destroy()
        del self.kickBtn
        self.bgBtn.destroy()
        del self.bgBtn
        self.ghostBtn.destroy()
        del self.ghostBtn
        self.suitSpawnerBtn.destroy()
        del self.suitSpawnerBtn
        self.killCogsBtn.destroy()
        del self.killCogsBtn
        self.makeTournamentBtn.destroy()
        del self.makeTournamentBtn
        self.makeInvasionBtn.destroy()
        del self.makeInvasionBtn
        self.makeCogBtn.destroy()
        del self.makeCogBtn
        self.book.clearTitle()
        self.book.deletePageButtons(True, False)
예제 #23
0
class DistributedCityCart(DistributedNode):
    notify = directNotify.newCategory('DistributedCityCart')

    def __init__(self, cr):
        DistributedNode.__init__(self, cr)
        self.fsm = ClassicFSM('DistributedCityCart', [State('off', self.enterOff, self.exitOff), State('pathFollow', self.enterPathFollow, self.exitPathFollow), State('collision', self.enterCollision, self.exitCollision)], 'off', 'off')
        self.fsm.enterInitialState()
        self.suitInCar = None
        self.cart = None
        self.honkSfxPath = 'phase_14/audio/sfx/cogtropolis_citycar_driveby_horn.mp3'
        self.cartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam'
        self.moPaths = ['phase_14/models/paths/ct-citycar-drivepath-1.egg',
         'phase_14/models/paths/ct-citycar-drivepath-2.egg',
         'phase_14/models/paths/ct-citycar-drivepath-3.egg',
         'phase_14/models/paths/ct-citycar-drivepath-4.egg',
         'phase_14/models/paths/ct-citycar-drivepath-5.egg',
         'phase_14/models/paths/ct-citycar-drivepath-6.egg']
        self.moPath = None
        self.soundEngineLoop = None
        self.soundDriveByHorn = None
        self.ivalTDisplace = None
        self.pathIndex = None
        self.wheelSpinTrack = None
        self.collNodePath = None
        self.soundDriveBy = None
        return

    def setIvalTDisplace(self, displace):
        self.ivalTDisplace = displace

    def setPathIndex(self, index):
        self.pathIndex = index

    def setState(self, state, timestamp):
        ts = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
        self.fsm.request(state, [ts])

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterPathFollow(self, ts):
        duration = CityCartGlobals.index2Duration[self.pathIndex]
        self.moPath = NURBSMopath.NURBSMopath(self.moPaths[self.pathIndex], name=self.uniqueName('DCityCart_moPath'))
        startT = 0.0
        if ts > 0.0:
            startT = ts % duration * (1.0 / duration)
        self.moPath.play(self, loop=True, duration=duration, startT=startT)
        base.taskMgr.add(self.__drive, self.uniqueName('DCityCart.drive'))
        self.wheelSpinTrack = Parallel()
        for name in ['leftFrontWheel',
         'rightBackWheel',
         'rightFrontWheel',
         'leftBackWheel']:
            wheel = self.find('**/' + name)
            self.wheelSpinTrack.append(LerpHprInterval(wheel, duration=0.1, hpr=(0, 360, 0), startHpr=(0, 0, 0)))

        self.wheelSpinTrack.loop()
        self.accept('enter' + self.collNodePath.node().getName(), self.__handleRanOver)

    def __handleRanOver(self, entry):
        self.suitInCar.setChat(CityCartGlobals.SuitRanOverTaunt)
        self.sendUpdate('hitByCar')
        self.cr.playGame.getPlace().fsm.request('stop')
        base.localAvatar.b_setAnimState('squish', callback=self.cr.playGame.getPlace().fsm.request, extraArgs=['walk'])

    def __drive(self, task):
        if base.localAvatar.getDistance(self) < 10.0:
            if self.soundDriveByHorn.status() == self.soundDriveByHorn.READY:
                wantsToHonk = random.randint(0, 3)
                if wantsToHonk == 3:
                    base.playSfx(self.soundDriveByHorn)
                return task.cont
        elif base.localAvatar.getDistance(self) < 20.0:
            if self.soundDriveBy.status() == self.soundDriveBy.READY:
                base.playSfx(self.soundDriveBy)
                return task.cont
        return task.cont

    def exitPathFollow(self):
        self.ignore('enter' + self.collNodePath.node().getName())
        if self.wheelSpinTrack:
            self.wheelSpinTrack.finish()
            self.wheelSpinTrack = None
        if self.moPath:
            self.moPath.stop()
            self.moPath = None
        return

    def enterCollision(self, ts):
        pass

    def exitCollision(self):
        pass

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

        head = random.choice(heads)
        suitType = CIGlobals.SuitBodyData[head][0]
        suitDept = CIGlobals.SuitBodyData[head][1]
        self.suitInCar = Suit()
        self.suitInCar.generateSuit(suitType, head, suitDept, 137, 0, False)
        self.suitInCar.loop('sit')
        self.suitInCar.disableRay()
        self.suitInCar.setScale(0.7)
        self.suitInCar.setH(180)
        self.suitInCar.setPos(0, -1, -1.5)
        self.suitInCar.reparentTo(self.cart.find('**/seat1'))
        self.soundEngineLoop = base.audio3d.loadSfx('phase_6/audio/sfx/KART_Engine_loop_0.wav')
        base.audio3d.attachSoundToObject(self.soundEngineLoop, self)
        base.playSfx(self.soundEngineLoop, looping=1)
        self.soundDriveByHorn = base.audio3d.loadSfx(self.honkSfxPath)
        base.audio3d.attachSoundToObject(self.soundDriveByHorn, self)
        self.soundDriveBy = base.audio3d.loadSfx('phase_14/audio/sfx/cogtropolis_citycar_driveby.mp3')
        base.audio3d.attachSoundToObject(self.soundDriveBy, self)
        sphere = CollisionSphere(0, 0, 0, 2.5)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('cartSphere'))
        node.setCollideMask(CIGlobals.WallBitmask)
        node.addSolid(sphere)
        self.collNodePath = self.attachNewNode(node)
        self.collNodePath.setZ(1.5)
        self.collNodePath.setSy(2.0)
        self.collNodePath.setSx(1.75)

    def disable(self):
        self.fsm.requestFinalState()
        if self.moPath:
            self.moPath.stop()
            self.moPath = None
        self.moPaths = None
        self.honkSfxPath = None
        self.cartModelPath = None
        self.soundEngineLoop = None
        self.soundDriveBy = None
        if self.suitInCar:
            self.suitInCar.disable()
            self.suitInCar.delete()
            self.suitInCar = None
        if self.cart:
            self.cart.removeNode()
            self.cart = None
        del self.fsm
        DistributedNode.disable(self)
        return
class BRWater:
    notify = directNotify.newCategory("BRWater")

    def __init__(self, playground):
        self.playground = playground
        self.fsm = ClassicFSM('BRWater', [
            State('off', self.enterOff, self.exitOff),
            State('freezeUp', self.enterFreezeUp, self.exitFreezeUp),
            State('coolDown', self.enterCoolDown, self.exitCoolDown),
            State('frozen', self.enterFrozen, self.exitFrozen)
        ], 'off', 'off')
        self.fsm.enterInitialState()

        #base.localAvatar.audio3d

        self.freezeUpSfx = base.loadSfx('phase_8/audio/sfx/freeze_up.ogg')
        self.frozenSfxArray = [
            base.loadSfx('phase_8/audio/sfx/frozen_1.ogg'),
            base.loadSfx('phase_8/audio/sfx/frozen_2.ogg'),
            base.loadSfx('phase_8/audio/sfx/frozen_3.ogg')
        ]
        self.coolSfxArray = [
            base.loadSfx('phase_8/audio/sfx/cool_down_1.ogg'),
            base.loadSfx('phase_8/audio/sfx/cool_down_2.ogg')
        ]

        self.iceFormSfx = base.loadSfx("phase_4/audio/sfx/ice_cube_form.ogg")
        self.iceBreakSfx = base.loadSfx("phase_4/audio/sfx/ice_cube_break.ogg")

        self.freezeUpSfx.setVolume(12)
        for sfx in self.frozenSfxArray:
            sfx.setVolume(12)
        for sfx in self.coolSfxArray:
            sfx.setVolume(12)
        #for sfx in self.frozenSfxArray:
        #    self.attachSound(sfx)
        #for sfx in self.coolSfxArray:
        #    self.attachSound(sfx)
        #self.attachSound(self.freezeUpSfx)

    def attachSound(self, sound):
        base.localAvatar.audio3d.attachSoundToObject(sound, base.localAvatar)

    def enterOff(self):
        self.playground.startWaterWatch()

    def exitOff(self):
        self.playground.stopWaterWatch()

    def loadIceCube(self):
        self.iceCube = loader.loadModel('phase_8/models/props/icecube.bam')
        for node in self.iceCube.findAllMatches('**/billboard*'):
            node.removeNode()
        for node in self.iceCube.findAllMatches('**/drop_shadow*'):
            node.removeNode()
        for node in self.iceCube.findAllMatches('**/prop_mailboxcollisions*'):
            node.removeNode()
        self.iceCube.reparentTo(base.localAvatar)
        self.iceCube.setScale(1.2, 1.0, base.localAvatar.getHeight() / 1.7)
        self.iceCube.setTransparency(1)
        self.iceCube.setColorScale(0.76, 0.76, 1.0, 0.0)

    def unloadIceCube(self):
        self.iceCube.removeNode()
        del self.iceCube

    def enterFreezeUp(self):
        length = 1.0
        base.playSfx(self.freezeUpSfx)
        self.fucsIval = Sequence(
            LerpColorScaleInterval(
                base.localAvatar.getGeomNode(),
                duration=length,
                colorScale=VBase4(0.5, 0.5, 1.0, 1.0),
                startColorScale=base.localAvatar.getGeomNode().getColorScale(),
                blendType='easeOut'), Func(self.fsm.request, 'frozen'))
        self.fucsIval.start()
        self.playground.startWaterWatch(0)

    def exitFreezeUp(self):
        self.fucsIval.pause()
        del self.fucsIval
        self.playground.stopWaterWatch()

    def enterFrozen(self):
        self.loadIceCube()
        base.playSfx(self.iceFormSfx)
        base.cr.playGame.getPlace().fsm.request('stop', [0])
        base.localAvatar.stop()
        base.playSfx(choice(self.frozenSfxArray))
        self.iccsIval = LerpColorScaleInterval(
            self.iceCube,
            duration=0.5,
            colorScale=VBase4(0.76, 0.76, 1.0, 1.0),
            startColorScale=self.iceCube.getColorScale(),
            blendType='easeInOut')
        self.iccsIval.start()
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)
        self.frame = DirectFrame(pos=(0, 0, 0.7))
        self.powerBar = DirectWaitBar(frameColor=(1, 1, 1, 1),
                                      range=100,
                                      value=0,
                                      scale=(0.4, 0.5, 0.25),
                                      parent=self.frame,
                                      barColor=(0.55, 0.7, 1.0, 1.0))
        self.label = OnscreenText(text="SHAKE MOUSE",
                                  shadow=(0, 0, 0, 1),
                                  fg=(0.55, 0.7, 1.0, 1.0),
                                  pos=(0, -0.1, 0),
                                  parent=self.frame)
        taskMgr.add(self.__watchMouseMovement, 'BRWater-watchMouseMovement')
        taskMgr.add(self.__lowerPowerBar, 'BRWater-lowerPowerBar')
        mw = base.mouseWatcherNode
        if mw.hasMouse():
            self.lastMouseX = mw.getMouseX()

    def __lowerPowerBar(self, task):
        if self.powerBar['value'] <= 0:
            self.powerBar.update(0)
        decrement = 1
        self.powerBar.update(self.powerBar['value'] - decrement)
        task.delayTime = 0.1
        return task.again

    def __watchMouseMovement(self, task):
        if self.powerBar['value'] >= self.powerBar['range']:
            self.fsm.request('coolDown', [1])
            return task.done
        mw = base.mouseWatcherNode
        if mw.hasMouse():
            if not self.lastMouseX or self.lastMouseX != mw.getMouseX():
                value = (abs(self.lastMouseX - mw.getMouseX()) *
                         globalClock.getDt()) / 0.001
                self.lastMouseX = mw.getMouseX()
                self.powerBar.update(self.powerBar['value'] + abs(value))
        return task.cont

    def exitFrozen(self):
        props = WindowProperties()
        props.setCursorHidden(False)
        base.win.requestProperties(props)
        self.iccsIval.pause()
        del self.iccsIval
        self.unloadIceCube()
        taskMgr.remove('BRWater-lowerPowerBar')
        taskMgr.remove('BRWater-watchMouseMovement')
        self.label.destroy()
        del self.label
        self.powerBar.destroy()
        del self.powerBar
        self.frame.destroy()
        del self.frame
        del self.lastMouseX
        base.cr.playGame.getPlace().fsm.request('walk')
        base.localAvatar.b_setAnimState('neutral')

    def enterCoolDown(self, fromFrozen=0):
        if fromFrozen:
            self.loadIceCube()
            base.playSfx(self.iceBreakSfx)
            self.iceCube.setColorScale(0.76, 0.76, 1.0, 1.0)
            self.iccdIval = LerpColorScaleInterval(
                self.iceCube,
                duration=0.5,
                colorScale=VBase4(0.76, 0.76, 1.0, 0.0),
                startColorScale=self.iceCube.getColorScale(),
                blendType='easeInOut')
            self.iccdIval.start()
        length = 1.0
        base.playSfx(choice(self.coolSfxArray))
        self.cdcsIval = Sequence(
            LerpColorScaleInterval(
                base.localAvatar.getGeomNode(),
                duration=length,
                colorScale=VBase4(1.0, 1.0, 1.0, 1.0),
                startColorScale=base.localAvatar.getGeomNode().getColorScale(),
                blendType='easeOut'), Func(self.fsm.request, 'off'))
        self.cdcsIval.start()

    def exitCoolDown(self):
        if hasattr(self, 'iccdIval'):
            self.iccdIval.pause()
            del self.iccdIval
            self.unloadIceCube()
        self.cdcsIval.pause()
        del self.cdcsIval

    def cleanup(self):
        self.fsm.requestFinalState()
        self.playground.stopWaterWatch()
        del self.fsm
        del self.freezeUpSfx
        del self.frozenSfxArray
        del self.coolSfxArray
        del self.playground
예제 #25
0
class TownLoader(StateData):
    notify = directNotify.newCategory('TownLoader')

    def __init__(self, hood, parentFSMState, doneEvent):
        self.hood = hood
        self.parentFSMState = parentFSMState
        StateData.__init__(self, doneEvent)
        self.fsm = ClassicFSM('TownLoader', [
            State('start', self.enterStart, self.exitStart,
                  ['quietZone', 'street']),
            State('street', self.enterStreet, self.exitStreet, ['quietZone']),
            State('toonInterior', self.enterToonInterior,
                  self.exitToonInterior, ['quietZone']),
            State('suitInterior', self.enterSuitInterior,
                  self.exitSuitInterior, ['quietZone']),
            State('quietZone', self.enterQuietZone, self.exitQuietZone,
                  ['street', 'toonInterior', 'suitInterior']),
            State('final', self.enterFinal, self.exitFinal, ['start'])
        ], 'start', 'final')
        self.branchZone = None
        self.canonicalBranchZone = None
        self.placeDoneEvent = 'placeDone'
        self.linkTunnels = []
        self.place = None
        return

    def findAndMakeLinkTunnels(self, requestStatus):
        for tunnel in self.geom.findAllMatches('**/*linktunnel*'):
            dnaRootStr = tunnel.getName()
            zone = LinkTunnel.getZoneFromDNARootStr(dnaRootStr)
            zone = LinkTunnel.maybeFixZone(zone)
            tunnelClass = LinkTunnel.getRecommendedTunnelClassFromZone(zone)
            link = tunnelClass(tunnel, dnaRootStr)
            self.linkTunnels.append(link)

    def load(self, zoneId):
        StateData.load(self)
        self.zoneId = zoneId
        self.branchZone = ZoneUtil.getBranchZone(zoneId)
        self.canonicalBranchZone = ZoneUtil.getCanonicalBranchZone(zoneId)
        self.music = base.loadMusic(self.musicFile)
        self.interiorMusic = base.loadMusic(self.interiorMusicFile)

    def unload(self):
        self.parentFSMState.removeChild(self.fsm)
        del self.parentFSMState
        del self.fsm
        del self.streetClass
        self.landmarkBlocks.removeNode()
        del self.landmarkBlocks
        self.hood.dnaStore.resetSuitPoints()
        self.hood.dnaStore.resetBattleCells()
        del self.hood
        del self.nodeDict
        del self.zoneDict
        del self.fadeInDict
        del self.fadeOutDict
        del self.nodeList
        self.geom.removeNode()
        del self.geom
        del self.music
        del self.interiorMusic
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        StateData.unload(self)

    def enter(self, requestStatus):
        StateData.enter(self)
        self.findAndMakeLinkTunnels(requestStatus)
        self.fsm.enterInitialState()
        self.setState(requestStatus['where'], requestStatus)

    def exit(self):
        self.fsm.requestFinalState()
        self.ignoreAll()
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        StateData.exit(self)

    def setState(self, state, requestStatus):
        self.fsm.request(state, [requestStatus])

    def enterStart(self):
        pass

    def exitStart(self):
        pass

    def enterStreet(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.streetDone)
        self.place = self.streetClass(self, self.fsm, self.placeDoneEvent)
        self.place.load()

    def exitStreet(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def streetDone(self):
        self.requestStatus = self.place.doneStatus
        status = self.place.doneStatus
        if status['loader'] == 'townLoader' and ZoneUtil.getBranchZone(
                status['zoneId']
        ) == self.branchZone and status['shardId'] == None or status[
                'how'] == 'doorOut' or status['where'] == 'suitInterior':
            self.fsm.request('quietZone', [status])
        else:
            self.doneStatus = status
            messenger.send(self.doneEvent)
        return

    def enterToonInterior(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone)
        self.place = ToonInterior.ToonInterior(self, self.fsm,
                                               self.placeDoneEvent)
        self.place.load()

    def exitToonInterior(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def enterSuitInterior(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.handleSuitInteriorDone)
        self.place = CogOfficeInterior.CogOfficeInterior(
            self, self.fsm, self.placeDoneEvent)
        self.place.load()

    def exitSuitInterior(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def enterThePlace(self, requestStatus):
        base.cr.playGame.setPlace(self.place)
        if self.place is not None:
            self.place.enter(requestStatus)
        return

    def handleToonInteriorDone(self):
        status = self.place.doneStatus
        if status['loader'] == 'townLoader' and ZoneUtil.getBranchZone(
                status['zoneId']) == self.branchZone and status[
                    'shardId'] == None or status['how'] == 'doorOut':
            self.fsm.request('quietZone', [status])
        else:
            self.doneStatus = status
            messenger.send(self.doneEvent)
        return

    def handleSuitInteriorDone(self):
        self.handleToonInteriorDone()

    def enterQuietZone(self, requestStatus):
        self.fsm.request(requestStatus['where'], [requestStatus],
                         exitCurrent=0)
        self.quietZoneDoneEvent = uniqueName('quietZoneDone')
        self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone)
        self.quietZoneStateData = QuietZoneState(self.quietZoneDoneEvent)
        self.quietZoneStateData.load()
        self.quietZoneStateData.enter(requestStatus)

    def exitQuietZone(self):
        self.ignore(self.quietZoneDoneEvent)
        del self.quietZoneDoneEvent
        self.quietZoneStateData.exit()
        self.quietZoneStateData.unload()
        self.quietZoneStateData = None
        return

    def handleQuietZoneDone(self):
        status = self.quietZoneStateData.getRequestStatus()
        self.exitQuietZone()
        self.enterThePlace(status)

    def enterFinal(self):
        pass

    def exitFinal(self):
        pass

    def createHood(self, dnaFile, loadStorage=1):
        if loadStorage:
            loader.loadDNAFile(self.hood.dnaStore,
                               'phase_5/dna/storage_town.pdna')
            loader.loadDNAFile(self.hood.dnaStore, self.townStorageDNAFile)
        node = loader.loadDNAFile(self.hood.dnaStore, dnaFile)
        if node.getNumParents() == 1:
            self.geom = NodePath(node.getParent(0))
            self.geom.reparentTo(hidden)
        else:
            self.geom = hidden.attachNewNode(node)
        self.makeDictionaries(self.hood.dnaStore)
        self.reparentLandmarkBlockNodes()
        self.renameFloorPolys(self.nodeList)
        gsg = base.win.getGsg()
        if gsg:
            self.geom.prepareScene(gsg)
        self.geom.flattenLight()
        self.geom.setName('town_top_level')

    def reparentLandmarkBlockNodes(self):
        bucket = self.landmarkBlocks = hidden.attachNewNode('landmarkBlocks')
        npc = self.geom.findAllMatches('**/sb*:*_landmark_*_DNARoot')
        for i in xrange(npc.getNumPaths()):
            nodePath = npc.getPath(i)
            nodePath.wrtReparentTo(bucket)

        npc = self.geom.findAllMatches('**/sb*:*animated_building*_DNARoot')
        for i in xrange(npc.getNumPaths()):
            nodePath = npc.getPath(i)
            nodePath.wrtReparentTo(bucket)

    def makeDictionaries(self, dnaStore):
        self.nodeDict = {}
        self.zoneDict = {}
        self.zoneVisDict = {}
        self.nodeList = []
        self.fadeInDict = {}
        self.fadeOutDict = {}
        a1 = Vec4(1, 1, 1, 1)
        a0 = Vec4(1, 1, 1, 0)
        numVisGroups = dnaStore.getNumDNAVisGroupsAI()
        for i in xrange(numVisGroups):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            visGroup = dnaStore.getDNAVisGroupAI(i)
            groupName = base.cr.hoodMgr.extractGroupName(groupFullName)
            zoneId = int(groupName)
            zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
            groupNode = self.geom.find('**/' + groupFullName)
            if groupNode.isEmpty():
                continue
            else:
                if ':' in groupName:
                    groupName = '%s%s' % (zoneId,
                                          groupName[groupName.index(':'):])
                else:
                    groupName = '%s' % zoneId
                groupNode.setName(groupName)
            groupNode.flattenMedium()
            self.nodeDict[zoneId] = []
            self.nodeList.append(groupNode)
            self.zoneDict[zoneId] = groupNode
            visibles = []
            for i in xrange(visGroup.getNumVisibles()):
                visibles.append(int(visGroup.get_visible(i)))

            visibles.append(ZoneUtil.getBranchZone(zoneId))
            self.zoneVisDict[zoneId] = visibles
            fadeDuration = 0.5
            self.fadeOutDict[groupNode] = Sequence(
                Func(groupNode.setTransparency, 1),
                LerpColorScaleInterval(groupNode,
                                       fadeDuration,
                                       a0,
                                       startColorScale=a1),
                Func(groupNode.clearColorScale),
                Func(groupNode.clearTransparency),
                Func(groupNode.stash),
                name='fadeZone-' + str(zoneId),
                autoPause=1)
            self.fadeInDict[groupNode] = Sequence(
                Func(groupNode.unstash),
                Func(groupNode.setTransparency, 1),
                LerpColorScaleInterval(groupNode,
                                       fadeDuration,
                                       a1,
                                       startColorScale=a0),
                Func(groupNode.clearColorScale),
                Func(groupNode.clearTransparency),
                name='fadeZone-' + str(zoneId),
                autoPause=1)

        for i in xrange(numVisGroups):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            zoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName))
            zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
            for j in xrange(dnaStore.getNumVisiblesInDNAVisGroup(i)):
                visName = dnaStore.getVisibleName(i, j)
                groupName = base.cr.hoodMgr.extractGroupName(visName)
                nextZoneId = int(groupName)
                nextZoneId = ZoneUtil.getTrueZoneId(nextZoneId, self.zoneId)
                visNode = self.zoneDict[nextZoneId]
                self.nodeDict[zoneId].append(visNode)

        self.hood.dnaStore.resetPlaceNodes()
        self.hood.dnaStore.resetDNAGroups()
        self.hood.dnaStore.resetDNAVisGroups()
        self.hood.dnaStore.resetDNAVisGroupsAI()

    def renameFloorPolys(self, nodeList):
        for i in nodeList:
            collNodePaths = i.findAllMatches('**/+CollisionNode')
            numCollNodePaths = collNodePaths.getNumPaths()
            visGroupName = i.node().getName()
            for j in xrange(numCollNodePaths):
                collNodePath = collNodePaths.getPath(j)
                bitMask = collNodePath.node().getIntoCollideMask()
                if bitMask.getBit(1):
                    collNodePath.node().setName(visGroupName)
class DistributedBoat(DistributedObject):
    notify = directNotify.newCategory("DistributedBoat")

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM('DistributedBoat', [
            State('off', self.enterOff, self.exitOff),
            State('eastToWest', self.enterEastToWest, self.exitEastToWest),
            State('westToEast', self.enterWestToEast, self.exitWestToEast)
        ], 'off', 'off')
        self.boat = None
        self.eastPier = None
        self.eastPierPath = 'east_pier'
        self.westPier = None
        self.westPierPath = 'west_pier'
        self.pierUpP = 0.0
        self.pierDownP = -45.0
        self.fogHorn = 'phase_5/audio/sfx/SZ_DD_foghorn.ogg'
        self.shipBell = 'phase_6/audio/sfx/SZ_DD_shipbell.ogg'
        self.waterLap = 'phase_6/audio/sfx/SZ_DD_waterlap.ogg'
        self.dockCreak = 'phase_6/audio/sfx/SZ_DD_dockcreak.ogg'
        self.eastWest = 'phase_6/paths/dd-e-w.bam'
        self.westEast = 'phase_6/paths/dd-w-e.bam'
        self.track = None
        self.state = None
        self.animBoat1Track = None
        self.animBoatTrack = None

        # Variables that handle the winter collision node.
        self.crashColl = None
        self.crashCollNP = None

    def __handleOnBoat(self, entry):
        base.localAvatar.b_setParent(CIGlobals.SPDonaldsBoat)

    def __handleOffBoat(self, entry):
        base.localAvatar.b_setParent(CIGlobals.SPRender)
        base.localAvatar.setR(0)
        base.localAvatar.setP(0)

    def generate(self):
        DistributedObject.generate(self)
        self.soundFogHorn = base.audio3d.loadSfx(self.fogHorn)
        self.soundShipBell = base.audio3d.loadSfx(self.shipBell)
        self.soundWaterLap = base.audio3d.loadSfx(self.waterLap)
        self.soundDockCreak = base.audio3d.loadSfx(self.dockCreak)

        geom = self.cr.playGame.hood.loader.geom
        self.boatMdl = geom.find('**/*donalds_boat*')
        self.boat = geom.find("**/ddBoatRoot")
        self.boatMdl1 = geom.find("**/ddBoatMdl1")

        base.audio3d.attachSoundToObject(self.soundFogHorn, self.boat)
        base.audio3d.attachSoundToObject(self.soundShipBell, self.boat)
        base.audio3d.attachSoundToObject(self.soundWaterLap, self.boat)
        base.audio3d.attachSoundToObject(self.soundDockCreak, self.boat)

        self.soundWaterLap.setLoop(True)
        self.soundWaterLap.play()

        self.generated()

    def generated(self):
        self.eastPier = self.cr.playGame.hood.loader.geom.find(
            '**/' + self.eastPierPath)
        self.westPier = self.cr.playGame.hood.loader.geom.find(
            '**/' + self.westPierPath)
        base.cr.parentMgr.registerParent(CIGlobals.SPDonaldsBoat, self.boatMdl)
        self.accept('enterdonalds_boat_floor', self.__handleOnBoat)
        self.accept('exitdonalds_boat_floor', self.__handleOffBoat)
        self.d_requestCurrentStateAndTimestamp()
        self.fsm.enterInitialState()

        speedFactor = 2

        self.animBoatTrack = Sequence(
            LerpHprInterval(self.boatMdl,
                            duration=1 * speedFactor,
                            hpr=(0, 0, -1),
                            startHpr=(0, 0, 1),
                            blendType='easeInOut'),
            LerpHprInterval(self.boatMdl,
                            duration=1 * speedFactor,
                            hpr=(0, 0, 1),
                            startHpr=(0, 0, -1),
                            blendType='easeInOut'))

        import math

        self.animBoat1Track = Sequence(
            LerpHprInterval(self.boatMdl1,
                            duration=math.pi * speedFactor,
                            hpr=(0, 1, 0),
                            startHpr=(0, -1, 0),
                            blendType='easeInOut'),
            LerpHprInterval(self.boatMdl1,
                            duration=math.pi * speedFactor,
                            hpr=(0, -1, 0),
                            startHpr=(0, 1, 0),
                            blendType='easeInOut'))
        self.animBoat1Track.loop()
        self.animBoatTrack.loop()

        if base.cr.holidayManager.getHoliday() == HolidayType.CHRISTMAS:
            self.boat.setPosHpr(12.73, -1.6, -4.7, 341.57, 350.0, 26.5)
            self.fsm.request('off')

            self.crashColl = CollisionSphere(0, 0, 0, 15)
            self.crashCollNP = self.boat.attachNewNode(
                CollisionNode('crashed_boat_collision'))
            self.crashCollNP.node().addSolid(self.crashColl)
            self.crashCollNP.node().setCollideMask(CIGlobals.WallBitmask)
            self.crashCollNP.setSz(2)
            self.crashCollNP.setSx(0.75)
            self.crashCollNP.setSy(1.25)
            self.crashCollNP.setPosHpr(2.05, 3.21, 1.66, 8.44, 6.93, 332.61)

    def disable(self):
        base.taskMgr.remove(self.uniqueName('__pollBoat'))
        base.cr.parentMgr.unregisterParent(CIGlobals.SPDonaldsBoat)
        self.ignore('enterdonalds_boat_floor')
        self.ignore('exitdonalds_boat_floor')
        self.fsm.requestFinalState()

        if self.animBoat1Track:
            self.animBoat1Track.finish()
            self.animBoat1Track = None
        if self.animBoatTrack:
            self.animBoatTrack.finish()
            self.animBoatTrack = None

        if self.crashCollNP:
            self.crashCollNP.removeNode()
            del self.crashCollNP
            del self.crashColl

        del self.fsm
        del self.soundFogHorn
        del self.soundShipBell
        self.soundWaterLap.stop()
        del self.soundWaterLap
        del self.soundDockCreak
        self.fogHorn = None
        self.shipBell = None
        self.waterLap = None
        self.dockCreak = None
        self.boat = None
        self.boatMdl = None
        self.boatMdl1 = None
        self.track = None
        self.pierDownP = None
        self.pierUpP = None
        self.eastPier = None
        self.eastPierPath = None
        self.westPier = None
        self.westPierPath = None
        self.boatPath = None
        self.westEast = None
        self.eastWest = None
        DistributedObject.disable(self)

    def currentStateAndTimestamp(self, state, timestamp):
        self.setState(state, timestamp)

    def d_requestCurrentStateAndTimestamp(self):
        self.sendUpdate('requestCurrentStateAndTimestamp', [])

    def setState(self, state, timestamp=None):
        if timestamp is None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        self.state = state
        if self.boat and base.cr.holidayManager.getHoliday(
        ) != HolidayType.CHRISTMAS:
            self.fsm.request(state, [ts])

    def enterEastToWest(self, ts=0):
        moPath = Mopath.Mopath()
        moPath.loadFile(self.eastWest)
        moIval = MopathInterval(moPath, self.boat, blendType='easeInOut')

        self.track = Parallel(
            SoundInterval(self.soundShipBell, node=self.boat),
            SoundInterval(self.soundDockCreak, node=self.eastPier), moIval,
            LerpQuatInterval(self.eastPier,
                             duration=5.0,
                             quat=(90, self.pierDownP, 0),
                             startHpr=(90, self.pierUpP, 0),
                             blendType='easeInOut'),
            Sequence(
                Wait(15.0),
                Parallel(
                    LerpQuatInterval(self.westPier,
                                     duration=5.0,
                                     quat=(-90, self.pierUpP, 0),
                                     startHpr=(-90, self.pierDownP, 0),
                                     blendType='easeInOut'),
                    Sequence(
                        Wait(2.0),
                        SoundInterval(self.soundDockCreak,
                                      node=self.westPier))),
                SoundInterval(self.soundFogHorn, node=self.boat)))
        self.track.start(ts)

    def exitEastToWest(self):
        if self.track:
            self.track.finish()
            self.track = None

    def enterWestToEast(self, ts=0):
        moPath = Mopath.Mopath()
        moPath.loadFile(self.westEast)
        moIval = MopathInterval(moPath, self.boat, blendType='easeInOut')

        self.track = Parallel(
            SoundInterval(self.soundShipBell, node=self.boat),
            SoundInterval(self.soundDockCreak, node=self.westPier), moIval,
            LerpQuatInterval(self.westPier,
                             duration=5.0,
                             quat=(-90, self.pierDownP, 0),
                             startHpr=(-90, self.pierUpP, 0),
                             blendType='easeInOut'),
            Sequence(
                Wait(15.0),
                Parallel(
                    LerpQuatInterval(self.eastPier,
                                     duration=5.0,
                                     quat=(90, self.pierUpP, 0),
                                     startHpr=(90, self.pierDownP, 0),
                                     blendType='easeInOut'),
                    Sequence(
                        Wait(2.0),
                        SoundInterval(self.soundDockCreak,
                                      node=self.eastPier))),
                SoundInterval(self.soundFogHorn, node=self.boat)))
        self.track.start(ts)

    def exitWestToEast(self):
        if self.track:
            self.track.finish()
            self.track = None

    def enterOff(self):
        pass

    def exitOff(self):
        pass
예제 #27
0
class AdminPage(StateData):
    notify = directNotify.newCategory('AdminPage')

    def __init__(self, book, parentFSM):
        self.book = book
        self.parentFSM = parentFSM
        StateData.__init__(self, 'adminPageDone')
        self.fsm = ClassicFSM('AdminPage', [State('off', self.enterOff, self.exitOff),
         State('basePage', self.enterBasePage, self.exitBasePage),
         State('kickSection', self.enterKickSection, self.exitKickSection),
         State('sysMsgSection', self.enterSysMsgSection, self.exitSysMsgSection)], 'off', 'off')
        self.fsm.enterInitialState()
        self.parentFSM.getStateNamed('adminPage').addChild(self.fsm)

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enter(self):
        StateData.enter(self)
        self.fsm.request('basePage')

    def exit(self):
        self.fsm.requestFinalState()
        StateData.exit(self)

    def unload(self):
        del self.book
        del self.parentFSM
        del self.fsm
        StateData.unload(self)

    def enterSysMsgSection(self):
        self.book.createPageButtons(None, None)
        self.book.setTitle('System Message')
        geom = CIGlobals.getDefaultBtnGeom()
        self.infoLbl = OnscreenText(text='Inform all online players about something.', pos=(0, 0.45))
        self.msgEntry = DirectEntry(initialText='System Message...', scale=0.055, width=15, numLines=4, command=self.sendSystemMessageCommand, focusInCommand=base.localAvatar.chatInput.disableKeyboardShortcuts, focusOutCommand=base.localAvatar.chatInput.enableKeyboardShortcuts, pos=(-0.4, 0, 0))
        self.sendBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Send', pos=(0, 0, -0.35), text_pos=(0, -0.01), command=self.sendSystemMessageCommand)
        self.cancelBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Cancel', pos=(-0.45, 0.15, -0.55), text_pos=(0, -0.01), command=self.fsm.request, extraArgs=['basePage'])
        return

    def sendSystemMessageCommand(self, foo = None):
        msg = self.msgEntry.get()
        base.cr.myDistrict.sendUpdate('systemMessageCommand', [base.localAvatar.getAdminToken(), msg])
        self.fsm.request('basePage')

    def exitSysMsgSection(self):
        self.infoLbl.destroy()
        del self.infoLbl
        self.msgEntry.destroy()
        del self.msgEntry
        self.sendBtn.destroy()
        del self.sendBtn
        self.cancelBtn.destroy()
        del self.cancelBtn
        self.book.clearTitle()
        self.book.deletePageButtons(False, False)

    def enterKickSection(self):
        self.book.createPageButtons(None, None)
        self.book.setTitle('Kick Player')
        geom = CIGlobals.getDefaultBtnGeom()
        self.infoLbl = OnscreenText(text='Type the ID of the player you want to boot out.', pos=(0, 0.45))
        self.idEntry = DirectEntry(width=10, scale=0.12, pos=(-0.59, 0, 0.15), command=self.sendKickMessage, focusInCommand=base.localAvatar.chatInput.disableKeyboardShortcuts, focusOutCommand=base.localAvatar.chatInput.enableKeyboardShortcuts)
        self.kickBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Kick', pos=(0, 0, -0.15), text_pos=(0, -0.01), command=self.sendKickMessage)
        self.banBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Ban', pos=(0, 0, -0.25), text_pos=(0, -0.01), command=self.sendKickMessage, extraArgs=[None, 1])
        self.cancelBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Cancel', pos=(-0.45, 0.15, -0.45), text_pos=(0, -0.01), command=self.fsm.request, extraArgs=['basePage'])
        return

    def sendKickMessage(self, foo = None, andBan = 0):
        if self.idEntry.get().isspace() or len(self.idEntry.get()) == 0:
            return
        print 'Sending out kick request for avatar id: ' + str(self.idEntry.get())
        base.localAvatar.sendUpdate('requestEject', [int(self.idEntry.get()), andBan])
        self.fsm.request('basePage')

    def exitKickSection(self):
        self.banBtn.destroy()
        del self.banBtn
        self.infoLbl.destroy()
        del self.infoLbl
        self.cancelBtn.destroy()
        del self.cancelBtn
        self.idEntry.destroy()
        del self.idEntry
        self.kickBtn.destroy()
        del self.kickBtn
        self.book.deletePageButtons(False, False)
        self.book.clearTitle()

    def enterBasePage(self):
        self.book.createPageButtons('releaseNotesPage', None)
        self.book.setTitle('Admin Stuff')
        geom = CIGlobals.getDefaultBtnGeom()
        self.suitSpawnerBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='', pos=(-0.45, 0.15, 0.5), text_pos=(0, -0.01), command=self.sendSuitCommand, extraArgs=['suitSpawner'])
        self.killCogsBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Kill All Cogs', pos=(-0.45, 0.15, 0.4), text_pos=(0, -0.01), command=self.sendSuitCommand, extraArgs=['killCogs'])
        self.makeTournamentBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Make Cog Tournament', pos=(-0.45, 0.15, 0.3), text_pos=(0, -0.01), command=self.sendSuitCommand, extraArgs=['tournament'])
        self.makeInvasionBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Make Cog Invasion', pos=(-0.45, 0.15, 0.2), text_pos=(0, -0.01), command=self.sendSuitCommand, extraArgs=['invasion'])
        self.makeCogBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Make Cog', pos=(-0.45, 0.15, 0.1), text_pos=(0, -0.01), command=self.sendSuitCommand, extraArgs=['suit'])
        self.ghostBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='', pos=(0.45, 0.15, 0.5), text_pos=(0, -0.01), command=self.changeGhost)
        self.bgBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Toggle Background', pos=(0.45, 0.15, 0.4), text_pos=(0, -0.01), command=self.toggleBackground)
        self.idBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Toggle Player Ids', pos=(0.45, 0.15, 0.3), text_pos=(0, -0.01), command=self.togglePlayerIds)
        self.kickBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='Kick Player', pos=(0.45, 0.15, 0.2), text_pos=(0, -0.01), command=self.openKickPage)
        self.systemMsgBtn = DirectButton(geom=geom, text_scale=0.04, relief=None, scale=1.0, text='System Message', pos=(0.45, 0.15, 0.1), text_pos=(0, -0.01), command=self.openSysMsgPage)
        if base.localAvatar.getGhost():
            self.ghostBtn['text'] = 'Turn Ghost Off'
        else:
            self.ghostBtn['text'] = 'Turn Ghost On'
        base.cr.playGame.getPlace().maybeUpdateAdminPage()
        del geom
        return

    def togglePlayerIds(self):
        if base.cr.isShowingPlayerIds:
            base.cr.hidePlayerIds()
        else:
            base.cr.showPlayerIds()

    def toggleBackground(self):
        if render.isHidden():
            render.show()
        else:
            render.hide()
        if self.book.book_img.isHidden():
            self.book.book_img.show()
        else:
            self.book.book_img.hide()

    def changeGhost(self):
        if base.localAvatar.getGhost():
            base.localAvatar.b_setGhost(0)
            self.ghostBtn['text'] = 'Turn Ghost On'
        else:
            base.localAvatar.b_setGhost(1)
            self.ghostBtn['text'] = 'Turn Ghost Off'

    def sendSuitCommand(self, commandName):
        if base.cr.playGame.suitManager:
            base.cr.playGame.suitManager.sendUpdate('suitAdminCommand', [base.localAvatar.getAdminToken(), commandName])

    def openKickPage(self):
        self.fsm.request('kickSection')

    def openSysMsgPage(self):
        self.fsm.request('sysMsgSection')

    def exitBasePage(self):
        self.systemMsgBtn.destroy()
        del self.systemMsgBtn
        self.idBtn.destroy()
        del self.idBtn
        self.kickBtn.destroy()
        del self.kickBtn
        self.bgBtn.destroy()
        del self.bgBtn
        self.ghostBtn.destroy()
        del self.ghostBtn
        self.suitSpawnerBtn.destroy()
        del self.suitSpawnerBtn
        self.killCogsBtn.destroy()
        del self.killCogsBtn
        self.makeTournamentBtn.destroy()
        del self.makeTournamentBtn
        self.makeInvasionBtn.destroy()
        del self.makeInvasionBtn
        self.makeCogBtn.destroy()
        del self.makeCogBtn
        self.book.clearTitle()
        self.book.deletePageButtons(True, False)
예제 #28
0
class DistributedButterflyAI(DistributedNodeAI):
    notify = directNotify.newCategory('DistributedButterflyAI')

    def __init__(self, air, hood, wingType):
        DistributedNodeAI.__init__(self, air)
        self.hood = hood
        self.wingType = wingType
        self.state = 0
        self.stateChangeTimestamp = 0.0
        self.fromLoc = 0
        self.toLoc = 0

        self.fsm = ClassicFSM('DBFAI', [State('off', self.enterOff, self.exitOff),
                                        State('sit', self.enterSit, self.exitSit),
                                        State('fly', self.enterFly, self.exitFly)],
                              'off', 'off')
        self.fsm.enterInitialState()

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterSit(self, fromLoc, toLoc):
        taskMgr.doMethodLater(random.uniform(*ButterflyGlobals.SitTime), self.__flySomewhere, self.uniqueName('stopSittingTask'))

    def __flySomewhere(self, task):
        toLoc = ButterflyGlobals.Spots[self.hood].index(random.choice(ButterflyGlobals.Spots[self.hood]))
        fromLoc = self.toLoc

        self.b_setState(2, fromLoc, toLoc)
        return task.done

    def exitSit(self):
        taskMgr.remove(self.uniqueName('stopSittingTask'))

    def enterFly(self, fromLoc, toLoc):
        distance = (ButterflyGlobals.Spots[self.hood][toLoc] - ButterflyGlobals.Spots[self.hood][fromLoc]).length()
        time = distance / ButterflyGlobals.Speed
        taskMgr.doMethodLater(time, self.__land, self.uniqueName('landTask'))

    def __land(self, task):
        self.b_setState(1, self.toLoc, self.toLoc)
        return task.done

    def exitFly(self):
        taskMgr.remove(self.uniqueName('landTask'))

    def setState(self, state, fromLoc, toLoc):
        self.state = state
        self.fromLoc = fromLoc
        self.toLoc = toLoc
        self.fsm.request(ButterflyGlobals.StateIdx2State[state], [fromLoc, toLoc])

    def b_setState(self, state, fromLoc, toLoc):
        self.setState(state, fromLoc, toLoc)
        self.d_setState(state, fromLoc, toLoc)

    def d_setState(self, state, fromLoc, toLoc):
        self.stateChangeTimestamp = globalClockDelta.getRealNetworkTime()
        self.sendUpdate('setState', [state, fromLoc, toLoc, self.stateChangeTimestamp])

    def getState(self):
        return [self.state, self.fromLoc, self.toLoc, self.stateChangeTimestamp]

    def getWingType(self):
        return self.wingType

    def getHood(self):
        return self.hood

    def announceGenerate(self):
        DistributedNodeAI.announceGenerate(self)
        loc = ButterflyGlobals.Spots[self.hood].index(random.choice(ButterflyGlobals.Spots[self.hood]))
        self.b_setState(1, loc, loc)

    def delete(self):
        self.fsm.requestFinalState()
        self.hood = None
        self.state = None
        self.toLoc = None
        self.fromLoc = None
        self.stateChangeTimestamp = None
        self.wingType = None
        DistributedNodeAI.delete(self)
예제 #29
0
class DistributedBoat(DistributedObject):
    notify = directNotify.newCategory('DistributedBoat')

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM('DistributedBoat', [State('off', self.enterOff, self.exitOff), State('eastToWest', self.enterEastToWest, self.exitEastToWest), State('westToEast', self.enterWestToEast, self.exitWestToEast)], 'off', 'off')
        self.boat = None
        self.eastPier = None
        self.eastPierPath = 'east_pier'
        self.westPier = None
        self.westPierPath = 'west_pier'
        self.pierUpP = 0.0
        self.pierDownP = -45.0
        self.fogHorn = 'phase_5/audio/sfx/SZ_DD_foghorn.mp3'
        self.shipBell = 'phase_6/audio/sfx/SZ_DD_shipbell.mp3'
        self.waterLap = 'phase_6/audio/sfx/SZ_DD_waterlap.mp3'
        self.dockCreak = 'phase_6/audio/sfx/SZ_DD_dockcreak.mp3'
        self.eastWest = 'phase_6/paths/dd-e-w.bam'
        self.westEast = 'phase_6/paths/dd-w-e.bam'
        self.boatPath = '*donalds_boat*'
        self.track = None
        self.state = None
        return

    def __handleOnBoat(self, entry):
        base.localAvatar.b_setParent(CIGlobals.SPDonaldsBoat)
        base.playSfx(self.soundWaterLap, looping=1)

    def __handleOffBoat(self, entry):
        base.localAvatar.b_setParent(CIGlobals.SPRender)
        self.soundWaterLap.stop()

    def __pollBoat(self, task):
        try:
            self.boat = self.cr.playGame.hood.loader.geom.find('**/' + self.boatPath)
        except:
            return task.cont

        self.generated()
        return task.done

    def generate(self):
        DistributedObject.generate(self)
        self.soundFogHorn = base.loadSfx(self.fogHorn)
        self.soundShipBell = base.loadSfx(self.shipBell)
        self.soundWaterLap = base.loadSfx(self.waterLap)
        self.soundDockCreak = base.loadSfx(self.dockCreak)
        self.boat = self.cr.playGame.hood.loader.geom.find('**/' + self.boatPath)
        self.generated()

    def generated(self):
        self.eastPier = self.cr.playGame.hood.loader.geom.find('**/' + self.eastPierPath)
        self.westPier = self.cr.playGame.hood.loader.geom.find('**/' + self.westPierPath)
        base.cr.parentMgr.registerParent(CIGlobals.SPDonaldsBoat, self.boat)
        self.accept('enterdonalds_boat_floor', self.__handleOnBoat)
        self.accept('exitdonalds_boat_floor', self.__handleOffBoat)
        self.d_requestCurrentStateAndTimestamp()
        self.fsm.enterInitialState()

    def disable(self):
        base.taskMgr.remove(self.uniqueName('__pollBoat'))
        base.cr.parentMgr.unregisterParent(CIGlobals.SPDonaldsBoat)
        self.ignore('enterdonalds_boat_floor')
        self.ignore('exitdonalds_boat_floor')
        self.fsm.requestFinalState()
        del self.fsm
        del self.soundFogHorn
        del self.soundShipBell
        del self.soundWaterLap
        del self.soundDockCreak
        self.fogHorn = None
        self.shipBell = None
        self.waterLap = None
        self.dockCreak = None
        self.boat = None
        self.track = None
        self.pierDownP = None
        self.pierUpP = None
        self.eastPier = None
        self.eastPierPath = None
        self.westPier = None
        self.westPierPath = None
        self.boatPath = None
        self.westEast = None
        self.eastWest = None
        DistributedObject.disable(self)
        return

    def currentStateAndTimestamp(self, state, timestamp):
        self.setState(state, timestamp)

    def d_requestCurrentStateAndTimestamp(self):
        self.sendUpdate('requestCurrentStateAndTimestamp', [])

    def setState(self, state, timestamp = None):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        self.state = state
        if self.boat:
            self.fsm.request(state, [ts])
        return

    def enterEastToWest(self, ts = 0):
        moPath = Mopath.Mopath()
        moPath.loadFile(self.eastWest)
        moIval = MopathInterval(moPath, self.boat)
        self.track = Parallel(SoundInterval(self.soundShipBell, node=self.boat), SoundInterval(self.soundDockCreak, node=self.eastPier), moIval, LerpQuatInterval(self.eastPier, duration=5.0, quat=(90, self.pierDownP, 0), startHpr=(90, self.pierUpP, 0)), Sequence(Wait(15.0), Parallel(LerpQuatInterval(self.westPier, duration=5.0, quat=(-90, self.pierUpP, 0), startHpr=(-90, self.pierDownP, 0)), Sequence(Wait(2.0), SoundInterval(self.soundDockCreak, node=self.westPier))), SoundInterval(self.soundFogHorn, node=self.boat)))
        self.track.start(ts)

    def exitEastToWest(self):
        if self.track:
            self.track.finish()
            self.track = None
        return

    def enterWestToEast(self, ts = 0):
        moPath = Mopath.Mopath()
        moPath.loadFile(self.westEast)
        moIval = MopathInterval(moPath, self.boat)
        self.track = Parallel(SoundInterval(self.soundShipBell, node=self.boat), SoundInterval(self.soundDockCreak, node=self.westPier), moIval, LerpQuatInterval(self.westPier, duration=5.0, quat=(-90, self.pierDownP, 0), startHpr=(-90, self.pierUpP, 0)), Sequence(Wait(15.0), Parallel(LerpQuatInterval(self.eastPier, duration=5.0, quat=(90, self.pierUpP, 0), startHpr=(90, self.pierDownP, 0)), Sequence(Wait(2.0), SoundInterval(self.soundDockCreak, node=self.eastPier))), SoundInterval(self.soundFogHorn, node=self.boat)))
        self.track.start(ts)

    def exitWestToEast(self):
        if self.track:
            self.track.finish()
            self.track = None
        return

    def enterOff(self):
        pass

    def exitOff(self):
        pass
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 TownLoader(StateData):
    notify = directNotify.newCategory("TownLoader")

    def __init__(self, hood, parentFSMState, doneEvent):
        self.hood = hood
        self.parentFSMState = parentFSMState
        StateData.__init__(self, doneEvent)
        self.fsm = ClassicFSM('TownLoader', [
            State('start', self.enterStart, self.exitStart,
                  ['quietZone', 'street', 'toonInterior']),
            State('street', self.enterStreet, self.exitStreet, ['quietZone']),
            State('toonInterior', self.enterToonInterior,
                  self.exitToonInterior, ['quietZone']),
            State('suitInterior', self.enterSuitInterior,
                  self.exitSuitInterior, ['quietZone']),
            State('quietZone', self.enterQuietZone, self.exitQuietZone,
                  ['street', 'toonInterior', 'suitInterior']),
            State('final', self.enterFinal, self.exitFinal, ['start'])
        ], 'start', 'final')
        self.branchZone = None
        self.canonicalBranchZone = None
        self.placeDoneEvent = 'placeDone'
        self.streetSong = ''
        self.interiorSong = ''
        self.linkTunnels = []
        self.place = None
        return

    def findAndMakeLinkTunnels(self, requestStatus):
        for tunnel in self.geom.findAllMatches('**/*linktunnel*'):
            dnaRootStr = tunnel.getName()
            zone = LinkTunnel.getZoneFromDNARootStr(dnaRootStr)
            zone = LinkTunnel.maybeFixZone(zone)
            tunnelClass = LinkTunnel.getRecommendedTunnelClassFromZone(zone)
            link = tunnelClass(tunnel, dnaRootStr)
            self.linkTunnels.append(link)

    def load(self, zoneId):
        StateData.load(self)
        self.zoneId = zoneId
        self.branchZone = ZoneUtil.getBranchZone(zoneId)
        self.canonicalBranchZone = ZoneUtil.getCanonicalBranchZone(zoneId)

    def unload(self):
        self.parentFSMState.removeChild(self.fsm)
        del self.parentFSMState
        del self.fsm
        del self.streetClass
        base.disablePhysicsNodes(self.landmarkBlocks)
        self.landmarkBlocks.removeNode()
        del self.landmarkBlocks
        self.hood.dnaStore.resetSuitPoints()
        self.hood.dnaStore.resetBattleCells()
        del self.hood
        del self.nodeDict
        del self.zoneDict
        del self.fadeInDict
        del self.fadeOutDict
        del self.nodeList
        del self.zoneVisDict

        base.disablePhysicsNodes(self.geom)
        self.geom.removeNode()
        del self.geom
        del self.streetSong
        del self.interiorSong

        #CIGlobals.doSceneCleanup()

        StateData.unload(self)

    def enter(self, requestStatus):
        StateData.enter(self)
        self.findAndMakeLinkTunnels(requestStatus)
        self.fsm.enterInitialState()
        self.setState(requestStatus['where'], requestStatus)

    def exit(self):
        self.fsm.requestFinalState()
        self.ignoreAll()
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        StateData.exit(self)

    def setState(self, state, requestStatus):
        self.fsm.request(state, [requestStatus])

    def enterStart(self):
        pass

    def exitStart(self):
        pass

    def enterStreet(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.streetDone)
        self.place = self.streetClass(self, self.fsm, self.placeDoneEvent)
        self.place.load()

    def exitStreet(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def streetDone(self):
        self.requestStatus = self.place.doneStatus
        status = self.place.doneStatus
        if (status['loader'] == 'townLoader'
                and ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone
                and status['shardId'] is None or status['how'] == 'doorOut'
                or status['where'] == 'suitInterior'):
            self.fsm.request('quietZone', [status])
        else:
            self.doneStatus = status
            messenger.send(self.doneEvent)

    def enterToonInterior(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone)
        self.place = ToonInterior.ToonInterior(self, self.fsm,
                                               self.placeDoneEvent)
        self.place.load()

    def exitToonInterior(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def enterSuitInterior(self, requestStatus):
        self.acceptOnce(self.placeDoneEvent, self.handleSuitInteriorDone)
        self.place = CogOfficeInterior.CogOfficeInterior(
            self, self.fsm, self.placeDoneEvent)
        self.place.load()

    def exitSuitInterior(self):
        self.ignore(self.placeDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)

    def enterThePlace(self, requestStatus):
        base.cr.playGame.setPlace(self.place)
        if self.place is not None:
            self.place.enter(requestStatus)

    def handleToonInteriorDone(self):
        status = self.place.doneStatus
        if (status['loader'] == 'townLoader'
                and ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone
                and status['shardId'] is None or status['how'] == 'doorOut'):
            self.fsm.request('quietZone', [status])
        else:
            self.doneStatus = status
            messenger.send(self.doneEvent)
        return

    def handleSuitInteriorDone(self):
        self.handleToonInteriorDone()

    def enterQuietZone(self, requestStatus):
        self.fsm.request(requestStatus['where'], [requestStatus],
                         exitCurrent=0)

        self.quietZoneDoneEvent = uniqueName('quietZoneDone')
        self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone)
        self.quietZoneStateData = QuietZoneState(self.quietZoneDoneEvent)
        self.quietZoneStateData.load()
        self.quietZoneStateData.enter(requestStatus)

    def exitQuietZone(self):
        self.ignore(self.quietZoneDoneEvent)
        del self.quietZoneDoneEvent
        self.quietZoneStateData.exit()
        self.quietZoneStateData.unload()
        self.quietZoneStateData = None
        return

    def handleQuietZoneDone(self):
        status = self.quietZoneStateData.getRequestStatus()
        self.exitQuietZone()
        self.enterThePlace(status)

    def enterFinal(self):
        pass

    def exitFinal(self):
        pass

    def createHood(self, dnaFile, loadStorage=1, flattenNow=True):
        if loadStorage:
            loader.loadDNAFile(self.hood.dnaStore,
                               'phase_5/dna/storage_town.pdna')
            loader.loadDNAFile(self.hood.dnaStore, self.townStorageDNAFile)
        node = loader.loadDNAFile(self.hood.dnaStore, dnaFile)
        if node.getNumParents() == 1:
            self.geom = NodePath(node.getParent(0))
            self.geom.reparentTo(hidden)
        else:
            self.geom = hidden.attachNewNode(node)
        if flattenNow:
            self.doFlatten()
        self.geom.setName('town_top_level')

    def doFlatten(self):
        self.makeDictionaries(self.hood.dnaStore)
        self.reparentLandmarkBlockNodes()
        base.createPhysicsNodes(self.geom)
        self.renameFloorPolys(self.nodeList)
        self.geom.flattenLight()

        CIGlobals.preRenderScene(self.geom)

    def reparentLandmarkBlockNodes(self):
        bucket = self.landmarkBlocks = hidden.attachNewNode('landmarkBlocks')
        npc = self.geom.findAllMatches('**/sb*:*_landmark_*_DNARoot')
        for i in xrange(npc.getNumPaths()):
            nodePath = npc.getPath(i)
            nodePath.wrtReparentTo(bucket)

        npc = self.geom.findAllMatches('**/sb*:*animated_building*_DNARoot')
        for i in xrange(npc.getNumPaths()):
            nodePath = npc.getPath(i)
            nodePath.wrtReparentTo(bucket)

    def makeDictionaries(self, dnaStore):
        self.nodeDict = {}
        self.zoneDict = {}
        self.zoneVisDict = {}
        self.nodeList = []
        self.fadeInDict = {}
        self.fadeOutDict = {}
        a1 = Vec4(1, 1, 1, 1)
        a0 = Vec4(1, 1, 1, 0)
        numVisGroups = dnaStore.getNumDNAVisGroupsAI()
        for i in xrange(numVisGroups):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            visGroup = dnaStore.getDNAVisGroupAI(i)
            groupName = base.cr.hoodMgr.extractGroupName(groupFullName)
            zoneId = int(groupName)
            zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
            groupNode = self.geom.find('**/' + groupFullName)
            if groupNode.isEmpty():
                continue
            else:
                if ':' in groupName:
                    groupName = '%s%s' % (zoneId,
                                          groupName[groupName.index(':'):])
                else:
                    groupName = '%s' % zoneId
                groupNode.setName(groupName)

            CIGlobals.replaceDecalEffectsWithDepthOffsetAttrib(groupNode)

            #group all the flat walls

            block2flatwall = {}
            flatwalls = groupNode.findAllMatches("**/tb*:*_DNARoot;+s")
            for flatwall in flatwalls:
                if "toon_landmark" in flatwall.getName():
                    print "Skipping", flatwall.getName()
                    continue
                if flatwall.hasTag("DNACode") and flatwall.hasMat():
                    continue
                block = int(flatwall.getName().split(":")[0][2:])
                if not block2flatwall.has_key(block):
                    block2flatwall[block] = groupNode.attachNewNode(
                        ModelNode('toonBuildingsBlock' + str(block)))
                flatwall.wrtReparentTo(block2flatwall[block])

            for node in block2flatwall.values():
                for child in node.findAllMatches("**"):
                    child.clearEffect(DecalEffect.getClassType())
                    child.clearTag("DNACode")
                    child.clearTag("cam")
                CIGlobals.clearModelNodesBelow(node)
                node.flattenStrong()

            flattenGroup = groupNode.attachNewNode('optim')
            flattens = ['street*_DNARoot']
            removes = ['interactive_prop*_DNARoot']
            for remove in removes:
                for np in groupNode.findAllMatches("**/" + remove):
                    np.removeNode()
            for flatten in flattens:
                for np in groupNode.findAllMatches("**/" + flatten):
                    if np.hasTag("DNACode") and np.hasMat():
                        continue
                    for child in np.findAllMatches("**"):
                        child.clearEffect(DecalEffect.getClassType())
                        child.clearTag("DNACode")
                        child.clearTag("cam")
                    np.wrtReparentTo(flattenGroup)
            flattenGroup.clearModelNodes()
            flattenGroup.flattenStrong()

            CIGlobals.flattenModelNodes(groupNode)
            groupNode.flattenStrong()
            #groupNode.ls()

            self.nodeDict[zoneId] = []
            self.nodeList.append(groupNode)
            self.zoneDict[zoneId] = groupNode
            visibles = []
            for i in xrange(visGroup.getNumVisibles()):
                visibles.append(int(visGroup.get_visible(i)))
            visibles.append(ZoneUtil.getBranchZone(zoneId))
            self.zoneVisDict[zoneId] = visibles
            fadeDuration = 0.5
            self.fadeOutDict[groupNode] = Sequence(
                Func(groupNode.setTransparency, 1),
                LerpColorScaleInterval(groupNode,
                                       fadeDuration,
                                       a0,
                                       startColorScale=a1),
                Func(groupNode.clearColorScale),
                Func(groupNode.clearTransparency),
                Func(groupNode.stash),
                Func(base.disablePhysicsNodes, groupNode),
                name='fadeZone-' + str(zoneId),
                autoPause=1)
            self.fadeInDict[groupNode] = Sequence(
                Func(base.enablePhysicsNodes, groupNode),
                Func(groupNode.unstash),
                Func(groupNode.setTransparency, 1),
                LerpColorScaleInterval(groupNode,
                                       fadeDuration,
                                       a1,
                                       startColorScale=a0),
                Func(groupNode.clearColorScale),
                Func(groupNode.clearTransparency),
                name='fadeZone-' + str(zoneId),
                autoPause=1)

        for i in xrange(numVisGroups):
            groupFullName = dnaStore.getDNAVisGroupName(i)
            zoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName))
            zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId)
            for j in xrange(dnaStore.getNumVisiblesInDNAVisGroup(i)):
                visName = dnaStore.getVisibleName(i, j)
                groupName = base.cr.hoodMgr.extractGroupName(visName)
                nextZoneId = int(groupName)
                nextZoneId = ZoneUtil.getTrueZoneId(nextZoneId, self.zoneId)
                visNode = self.zoneDict[nextZoneId]
                self.nodeDict[zoneId].append(visNode)

        self.hood.dnaStore.resetPlaceNodes()
        self.hood.dnaStore.resetDNAGroups()
        self.hood.dnaStore.resetDNAVisGroups()
        self.hood.dnaStore.resetDNAVisGroupsAI()

    def renameFloorPolys(self, nodeList):
        for i in nodeList:
            collNodePaths = i.findAllMatches('**/+BulletRigidBodyNode')
            numCollNodePaths = collNodePaths.getNumPaths()
            visGroupName = i.node().getName()
            for j in xrange(numCollNodePaths):
                collNodePath = collNodePaths.getPath(j)
                bitMask = collNodePath.node().getIntoCollideMask()
                if bitMask == CIGlobals.FloorGroup:
                    collNodePath.node().setName(visGroupName)
                    collNodePath.setCollideMask(CIGlobals.StreetVisGroup)
예제 #32
0
class NamePage(StateData):
    notify = directNotify.newCategory('NamePage')

    def __init__(self, book, parentFSM):
        self.book = book
        self.parentFSM = parentFSM
        StateData.__init__(self, 'namePageDone')
        self.fsm = ClassicFSM('NamePage', [
            State('off', self.enterOff, self.exitOff),
            State('basePage', self.enterBasePage, self.exitBasePage)
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.parentFSM.getStateNamed('namePage').addChild(self.fsm)
        self.nameServ = base.cr.nameServicesManager
        self.baseRequestIndex = 0
        self.requestsPerCluster = 5
        self.requestsContainer = {}
        self.loadingLabel = None
        self.selectedName = None
        self.nameButtons = []
        self.avId2NameData = {}
        geom = CIGlobals.getDefaultBtnGeom()
        self.acceptBtn = DirectButton(geom=geom,
                                      text_scale=0.04,
                                      relief=None,
                                      scale=0.5,
                                      text='Accept',
                                      pos=(0.5, posY, 0),
                                      text_pos=(0, -0.01),
                                      command=self.acceptName)
        self.acceptBtn.hide()
        self.declineBtn = DirectButton(geom=geom,
                                       text_scale=0.04,
                                       relief=None,
                                       scale=0.5,
                                       text='Decline',
                                       pos=(0.75, posY, 0),
                                       text_pos=(0, -0.01),
                                       command=self.declineName)
        self.declineBtn.hide()
        self.avIdLbl = OnscreenText(text='',
                                    scale=0.08,
                                    pos=(0.3, 0, 0.5),
                                    align=TextNode.ACenter)
        self.avIdLbl.hide()
        self.accIdLbl = OnscreenText(text='',
                                     scale=0.08,
                                     pos=(0.3, 0, 0.3),
                                     align=TextNode.ACenter)
        self.accIdLbl.hide()
        return

    def handleRequests(self):
        gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui.bam')
        self.nameList = DirectScrolledList(
            relief=None,
            pos=(-0.54, 0, 0.08),
            incButton_image=(gui.find('**/FndsLst_ScrollUp'),
                             gui.find('**/FndsLst_ScrollDN'),
                             gui.find('**/FndsLst_ScrollUp_Rllvr'),
                             gui.find('**/FndsLst_ScrollUp')),
            incButton_relief=None,
            incButton_scale=(arrowButtonScale, arrowButtonScale,
                             -arrowButtonScale),
            incButton_pos=(buttonXstart, 0, itemFrameZorigin - 0.999),
            incButton_image3_color=Vec4(1, 1, 1, 0.2),
            incButton_command=self.__moveItems,
            incButton_extraArgs=[1],
            decButton_image=(gui.find('**/FndsLst_ScrollUp'),
                             gui.find('**/FndsLst_ScrollDN'),
                             gui.find('**/FndsLst_ScrollUp_Rllvr'),
                             gui.find('**/FndsLst_ScrollUp')),
            decButton_relief=None,
            decButton_scale=(arrowButtonScale, arrowButtonScale,
                             arrowButtonScale),
            decButton_pos=(buttonXstart, 0, itemFrameZorigin + 0.125),
            decButton_image3_color=Vec4(1, 1, 1, 0.2),
            decButton_command=self.__moveItems,
            decButton_extraArgs=[0],
            itemFrame_pos=(itemFrameXorigin, 0, itemFrameZorigin),
            itemFrame_scale=1.0,
            itemFrame_relief=DGG.SUNKEN,
            itemFrame_frameSize=(listXorigin, listXorigin + listFrameSizeX,
                                 listZorigin, listZorigin + listFrameSizeZ),
            itemFrame_frameColor=(0.85, 0.95, 1, 1),
            itemFrame_borderWidth=(0.01, 0.01),
            numItemsVisible=5,
            forceHeight=0.075,
            items=self.nameButtons)
        self.__buildItems()
        return

    def __moveItems(self, direction):
        if direction == 0:
            self.baseRequestIndex += 1
        else:
            if direction == 1:
                self.baseRequestIndex -= 1
        self.clearItems()
        self.__buildItems()

    def clearItems(self):
        for btn in self.nameButtons:
            btn.destroy()

        self.nameButtons = []
        self.nameList.removeAndDestroyAllItems()

    def __buildItems(self):
        for i in xrange(self.requestsPerCluster):
            request = self.nameServ.getNameRequests()[self.baseRequestIndex +
                                                      i]
            date = request['date']
            date = date.replace(' ', '-')
            data = NameData(request['name'], date, request['avId'],
                            request['accId'])
            self.avId2NameData[data.avId] = data
            btn = DirectButton(relief=None,
                               text=data.name,
                               text_scale=0.07,
                               text_align=TextNode.ALeft,
                               text1_bg=textDownColor,
                               text2_bg=textRolloverColor,
                               text3_fg=textDisabledColor,
                               textMayChange=0,
                               command=self.__handleNameButton,
                               extraArgs=[data],
                               text_pos=(0, 0, 0.0))
            data.btn = btn
            self.nameButtons.append(btn)

        self.loadingLabel.hide()
        return

    def __handleNameButton(self, data):
        self.selectedName = data
        data.btn['state'] = DGG.DISABLED
        self.avIdLbl.setText('Avatar ID:\n' + str(data.avId))
        self.avIdLbl.show()
        self.accIdLbl.setText('Account ID:\n' + str(data.accId))
        self.accIdLbl.show()
        self.acceptBtn.show()
        self.declineBtn.show()

    def acceptName(self):
        pass

    def load(self):
        StateData.load(self)
        self.loadingLabel = OnscreenText(text='Loading...',
                                         font=CIGlobals.getToonFont(),
                                         pos=(0, 0.1, 0),
                                         scale=0.08,
                                         parent=aspect2d)

    def unload(self):
        StateData.unload(self)
        self.loadingLabel.destroy()
        self.loadingLabel = None
        for request in self.requestsContainer.values():
            for element in request:
                element.destroy()

        self.requestsContainer = {}
        return

    def enter(self):
        StateData.enter(self)
        self.fsm.request('basePage')
        base.acceptOnce(self.nameServ.getRequestCompleteName(),
                        self.handleRequests)
        self.nameServ.d_requestNameData()

    def exit(self):
        self.fsm.requestFinalState()
        StateData.exit(self)

    def enterBasePage(self):
        self.book.createPageButtons('adminPage', None)
        self.book.setTitle('Name Approval')
        return

    def exitBasePage(self):
        self.book.deletePageButtons(True, False)
        self.book.clearTitle()

    def enterOff(self):
        pass

    def exitOff(self):
        pass
class DistributedTakeOverSuit(DistributedSuit):
    notify = directNotify.newCategory("DistributedTakeOverSuit")

    StartPosFromDoor = Point3(1.6, -10, -0.5)
    AtDoorPos = Point3(1.6, -1, 0)

    def __init__(self, cr):
        DistributedSuit.__init__(self, cr)
        self.showNametagInMargins = True
        self.doorDoId = None
        self.door = None
        self.takeOverTrack = None
        self.fsm = ClassicFSM('DTOS-fsm', [
            State('off', self.enterOff, self.exitOff),
            State('takeOver', self.enterTakeOver, self.exitTakeOver)
        ], 'off', 'off')
        self.fsm.enterInitialState()

    def interruptTakeOver(self):
        if self.takeOverTrack:
            self.takeOverTrack.pause()
            self.takeOverTrack = None

    def setState(self, state, timestamp):
        ts = globalClockDelta.localElapsedTime(timestamp)
        self.fsm.request(state, [ts])

    def disable(self):
        taskMgr.remove('posTask')
        self.fsm.requestFinalState()
        self.fsm = None
        self.doorDoId = None
        self.door = None
        if self.takeOverTrack:
            self.takeOverTrack.finish()
            self.takeOverTrack = None

        DistributedSuit.disable(self)

    def setDoorDoId(self, doId):
        self.doorDoId = doId
        self.door = self.cr.doId2do.get(doId)
        if self.door is None:
            taskMgr.add(self.__pollDoor, self.uniqueName('pollDoor'))

    def stateAndTimestamp(self, state, timestamp):
        self.setState(state, timestamp)

    def __pollDoor(self, task):
        self.door = self.cr.doId2do.get(self.doorDoId)
        if self.door:
            self.sendUpdate('requestStateAndTimestamp')
            return task.done
        return task.cont

    def getDoorDoId(self):
        return self.doorDoId

    def enterOff(self, ts=0):
        pass

    def exitOff(self):
        pass

    def enterTakeOver(self, ts=0):
        if not self.door:
            return
        self.stopSmooth()
        self.hasSpawned = False
        self.doingActivity = True
        self.reparentTo(self.door.doorNode)
        self.setHpr(0, 0, 0)
        self.takeOverTrack = Parallel(
            Sequence(
                Func(self.animFSM.request, 'flyDown', [ts]), Wait(6.834),
                Func(self.loop, 'neutral'), Wait(0.5), Func(self.loop, 'walk'),
                LerpPosInterval(
                    self,
                    duration=2.0,
                    pos=render.getRelativePoint(self.door.doorNode,
                                                self.AtDoorPos),
                    startPos=render.getRelativePoint(self.door.doorNode,
                                                     self.StartPosFromDoor)),
                Func(self.loop, 'neutral'), Wait(0.3), Func(self.loop, 'walk'),
                LerpPosInterval(self,
                                duration=0.5,
                                pos=self.door.enterWalkBackPos,
                                startPos=self.AtDoorPos),
                Func(self.loop, 'neutral'), Wait(1.0), Func(self.loop, 'walk'),
                LerpPosInterval(self,
                                duration=1.0,
                                pos=self.door.enterWalkInPos,
                                startPos=self.door.enterWalkBackPos)),
            LerpPosInterval(self,
                            duration=4.375,
                            pos=render.getRelativePoint(
                                self.door.doorNode, self.StartPosFromDoor),
                            startPos=render.getRelativePoint(
                                self.door.doorNode, self.StartPosFromDoor) +
                            (0, 0, 6.5 * 4.8)))
        self.takeOverTrack.start(ts)

    def exitTakeOver(self):
        self.doingActivity = False
        if self.takeOverTrack:
            self.takeOverTrack.pause()
            self.takeOverTrack = None
class DistributedRaceGame(DistributedMinigame.DistributedMinigame):
    def __init__(self, cr):
        try:
            self.DistributedRaceGame_initialized
            return
        except:
            self.DistributedRaceGame_initialized = 1
        DistributedMinigame.DistributedMinigame.__init__(self, cr)
        self.movement = RaceGameMovement.RaceGameMovement(base.localAvatar)
        self.skyUtil = SkyUtil()
        self.raceFSM = ClassicFSM('DistributedRaceGame', [
            State('race', self.enterRace, self.exitRace),
            State('raceTransition', self.enterRaceTransition,
                  self.exitRaceTransition),
            State('off', self.enterRaceOff, self.exitRaceOff)
        ], 'off', 'off')
        self.raceFSM.enterInitialState()
        self.cr = cr
        self.track = None
        self.sky = None
        self.countSfx = base.loadSfx("phase_5/audio/sfx/firehydrant_popup.ogg")
        self.goSfx = base.loadSfx("phase_4/audio/sfx/AA_sound_whistle.ogg")
        self.game = CIGlobals.RaceGame
        self.trackPath = "phase_4/models/minigames/sprint_track.egg"
        self.skyPath = "phase_3.5/models/props/TT_sky.bam"
        self.lanePos = [(-22.00, -205.00, 0.00), (-11.66, -205.00, 0.00),
                        (0.00, -205.00, 0.00), (-33.66, -205.00, 0.00)]
        self.initialCamPos = {
            "pos": (41.10, -145.00, 25.88),
            "hpr": (135.00, 345.96, 0.0)
        }
        self.raceCamPos = (-24.52, -37.22, 25.00)
        self.lane = 0
        return

    def load(self):
        self.deleteWorld()
        self.track = loader.loadModel(self.trackPath)
        self.track.reparentTo(render)
        self.sky = loader.loadModel(self.skyPath)
        self.sky.reparentTo(self.track)
        self.skyUtil.startSky(self.sky)
        self.setMinigameMusic("phase_4/audio/bgm/MG_toontag.ogg")
        self.setDescription("Tap the left and right arrow keys repeatedly, in turns, as fast as " + \
         "you can to win the race! Every time your power bar hits the top, the boost bar starts" + \
         " to fill. When the boost bar is full, press CTRL to boost for a few seconds.")
        self.setWinnerPrize(100)
        self.setLoserPrize(5)
        self.d_requestToonLane()
        camera.reparentTo(render)
        camera.setPos(self.initialCamPos["pos"])
        camera.setHpr(self.initialCamPos["hpr"])
        DistributedMinigame.DistributedMinigame.load(self)

    def enterPlay(self):
        DistributedMinigame.DistributedMinigame.enterPlay(self)
        self.raceFSM.request('raceTransition')

    def exitPlay(self):
        DistributedMinigame.DistributedMinigame.exitPlay(self)
        self.raceFSM.request('off')

    def enterRace(self):
        self.startMovement()

    def exitRace(self):
        self.stopMovement()

    def enterRaceOff(self):
        pass

    def exitRaceOff(self):
        pass

    def enterRaceTransition(self):
        self.raceTrans = Sequence(Wait(0.5), Func(self.moveCameraToToon),
                                  Wait(4.5), Func(self.moveCameraToTop),
                                  Wait(4.5), Func(self.startCountdown))
        self.raceTrans.start()

    def exitRaceTransition(self):
        self.raceTrans.pause()
        del self.raceTrans

    def startMovement(self):
        self.movement.createGui()
        self.movement.fsm.request('run')

    def enterGameOver(self, winner=0, winnerDoId=0, allPrize=0):
        self.raceFSM.request('off')
        DistributedMinigame.DistributedMinigame.enterGameOver(
            self, winner, winnerDoId, allPrize)

    def stopMovement(self):
        self.movement.cleanup()
        self.movement.deleteGui()

    def startCountdown(self):
        """ Start the countdown to the start of the race. """
        self.countdownLbl = DirectLabel(text="",
                                        text_scale=0.3,
                                        text_font=CIGlobals.getMickeyFont(),
                                        text_fg=(1, 1, 0, 1),
                                        pos=(0, 0, 0.5))
        Sequence(Func(self.setCountdownText, "3"), Wait(1.0),
                 Func(self.setCountdownText, "2"), Wait(1.0),
                 Func(self.setCountdownText, "1"), Wait(1.0),
                 Func(self.setCountdownText, "GO!"), Wait(1.5),
                 Func(self.deleteCountdownLabel)).start()

    def setCountdownText(self, number):
        self.countdownLbl['text'] = number
        if number == "GO!":
            self.countdownLbl['text_fg'] = (0, 1, 0, 1)
            self.goSfx.play()
            self.raceFSM.request('race')
        else:
            self.countSfx.play()

    def deleteCountdownLabel(self):
        self.countdownLbl.destroy()
        del self.countdownLbl

    def moveCameraToToon(self):
        camPInt = LerpPosInterval(camera,
                                  duration=3.0,
                                  pos=self.localAv.getPos(render) + (0, 15, 3),
                                  startPos=(camera.getPos(render)),
                                  blendType="easeInOut")
        camQInt = camera.quatInterval(3.0,
                                      hpr=Vec3(180, 0, 0),
                                      blendType="easeInOut")
        camPInt.start()
        camQInt.start()

    def moveCameraToTop(self):
        camera.setPos(camera.getPos(self.localAv))
        camera.reparentTo(self.localAv)
        oldPos = camera.getPos()
        camera.setPos(self.raceCamPos)
        oldHpr = camera.getHpr()
        camera.lookAt(self.localAv.getPart('head'))
        newHpr = camera.getHpr()
        camera.setHpr(oldHpr)
        camera.setPos(oldPos)
        camPInt = LerpPosInterval(camera,
                                  duration=3.0,
                                  pos=self.raceCamPos,
                                  startPos=oldPos,
                                  blendType="easeInOut")
        camQInt = camera.quatInterval(3.0, hpr=newHpr, blendType="easeInOut")
        camPInt.start()
        camQInt.start()

    def deleteWorld(self):
        if self.track:
            self.track.removeNode()
            self.track = None
        if self.sky:
            self.skyUtil.stopSky()
            self.sky.removeNode()
            self.sky = None

    def setToonLane(self, lane):
        self.lane = lane
        base.localAvatar.setPos(self.lanePos[lane])
        base.localAvatar.setHpr(0, 0, 0)

    def getToonLane(self):
        return self.lane

    def d_requestToonLane(self):
        self.sendUpdate('requestToonLane', [])

    def announceGenerate(self):
        DistributedMinigame.DistributedMinigame.announceGenerate(self)
        self.load()

    def disable(self):
        DistributedMinigame.DistributedMinigame.disable(self)
        self.deleteWorld()
        self.raceFSM.requestFinalState()
        del self.raceFSM
        self.countSfx = None
        self.goSfx = None
예제 #35
0
class CameraShyFirstPerson(FirstPerson):
    toonInFocusColor = VBase4(0.25, 1.0, 0.25, 1.0)
    toonOutOfFocusColor = VBase4(1.0, 1.0, 1.0, 1.0)
    fullyChargedState = 5

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

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

    def enterOff(self):
        pass

    def exitOff(self):
        pass

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

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

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

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

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

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

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

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

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

        return

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

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

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

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

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

    def cleanup(self):
        self.camFSM.requestFinalState()
        self.camFSM = None
        FirstPerson.cleanup(self)
        return