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 FactorySneakGuardSuit(Suit, FSM):
    notify = directNotify.newCategory('FactorySneakGuardSuit')
    SUIT = 'mrhollywood'
    VIEW_DISTANCE_TASK_NAME = 'ViewDistanceTask'
    MAX_VIEW_DISTANCE = 100.0
    GUARD_DIED_DELAY = 6.0
    MAX_HP = 200
    IN_VIEW = 'somethingInSight'
    HEARD = 'heard'
    TRY_TO_CONFIRM_TIME = 5.0

    def __init__(self, world, guardKey):
        Suit.__init__(self)
        FSM.__init__(self, 'FactorySneakGuardSuit')
        self.gameWorld = world
        self.guardKey = guardKey
        self.viewDistanceTaskName = self.VIEW_DISTANCE_TASK_NAME + '-' + str(id(self))
        self.diedTaskName = 'GuardDied-' + str(id(self))
        self.health = 0
        self.maxHealth = self.MAX_HP
        self.eyeLight = None
        self.eyeLens = None
        self.eyeNode = None
        self.moveTrack = None
        return

    def enterGuard(self):
        self.loop('neutral')
        pos, hpr = CGG.FactoryGuardPoints[self.guardKey]
        self.setHpr(hpr - (180, 0, 0))
        self.setPos(pos)
        base.taskMgr.add(self.__guard, self.taskName('guard'))

    def __guard(self, task):
        if self.eyeNode.node().isInView(base.localAvatar.getPos(self.eyeNode)):
            self.request('SeekTarget', self.IN_VIEW)
            return task.done
        return task.cont

    def exitGuard(self):
        base.taskMgr.remove(self.taskName('guard'))

    def enterTurnToGuardSpot(self):
        self.loop('walk')
        _, hpr = CGG.FactoryGuardPoints[self.guardKey]
        self.moveTrack = LerpHprInterval(self, duration=1.0, hpr=hpr, startHpr=self.getHpr())
        self.moveTrack.setDoneEvent(self.uniqueName('TurnedToGuardSpot'))
        self.acceptOnce(self.moveTrack.getDoneEvent(), self.request, ['Guard'])
        self.moveTrack.start()

    def exitTurnToGuardSpot(self):
        if self.moveTrack:
            self.ignore(self.moveTrack.getDoneEvent())
            self.moveTrack.finish()
            self.moveTrack = None
        return

    def enterSeekTarget(self, event):
        dialogue = random.choice(CGG.GuardDialog[event])
        self.setChat(dialogue)
        self.loop('walk')
        self.moveTrack = NPCLookInterval(self, base.localAvatar)
        self.moveTrack.setDoneEvent(self.uniqueName('SeekLocalAvatar'))
        self.acceptOnce(self.moveTrack.getDoneEvent(), self.request, ['TryToConfirmTarget'])
        self.moveTrack.start()

    def exitSeekTarget(self):
        if self.moveTrack:
            self.ignore(self.moveTrack.getDoneEvent())
            self.moveTrack.finish()
            self.moveTrack = None
        return

    def enterTryToConfirmTarget(self):
        self.loop('neutral')
        base.taskMgr.add(self.__tryToConfirmTarget, self.uniqueName('TryToConfirmTarget'))

    def __tryToConfirmTarget(self, task):
        if task.time >= self.TRY_TO_CONFIRM_TIME:
            chat = random.choice(CGG.GuardDialog['disregard'])
            self.setChat(chat)
            self.request('TurnToGuardSpot')
            return task.done
        if self.eyeNode.node().isInView(base.localAvatar.getPos(self.eyeNode)):
            chat = random.choice(CGG.GuardDialog['spot'])
            self.setChat(chat)
            return task.done
        return task.cont

    def exitTryToConfirmTarget(self):
        base.taskMgr.remove(self.uniqueName('TryToConfirmTarget'))

    def uniqueName(self, name):
        return self.taskName(name)

    def taskName(self, name):
        return name + '-' + str(id(self))

    def setHealth(self, hp):
        self.health = hp

    def getHealth(self):
        return self.health

    def shot(self):
        dialogue = random.choice(CGG.GuardDialog['shot'])
        self.setChat(dialogue)

    def dead(self):
        self.animFSM.request('die')
        base.taskMgr.doMethodLater(self.GUARD_DIED_DELAY, self.__diedDone, self.diedTaskName)

    def __diedDone(self, task):
        self.gameWorld.deleteGuard(self)
        return task.done

    def generate(self):
        data = CIGlobals.SuitBodyData[self.SUIT]
        type = data[0]
        team = data[1]
        self.generateSuit(type, self.SUIT, team, self.MAX_HP, 0, False)
        base.taskMgr.add(self.__viewDistance, self.viewDistanceTaskName)
        self.setPythonTag('guard', self)
        self.eyeLight = Spotlight('eyes')
        self.eyeLens = PerspectiveLens()
        self.eyeLens.setMinFov(90.0 / (4.0 / 3.0))
        self.eyeLight.setLens(self.eyeLens)
        self.eyeNode = self.headModel.attachNewNode(self.eyeLight)
        self.eyeNode.setZ(-5)
        self.eyeNode.setY(-4.5)
        self.request('Guard')

    def __viewDistance(self, task):
        if self.getDistance(base.localAvatar) > self.MAX_VIEW_DISTANCE:
            if not self.isHidden():
                self.hide()
        elif self.isHidden():
            self.show()
        task.delayTime = 1.0
        return task.again

    def disable(self):
        base.taskMgr.remove(self.diedTaskName)
        base.taskMgr.remove(self.viewDistanceTaskName)
        if self.eyeNode:
            self.eyeNode.removeNode()
            self.eyeNode = None
            self.eyeLens = None
            self.eyeLight = None
        self.viewDistanceTaskName = None
        self.guardKey = None
        self.gameWorld = None
        Suit.disable(self)
        return
Exemple #3
0
class FactorySneakGuardSuit(Suit, FSM):
    notify = directNotify.newCategory('FactorySneakGuardSuit')
    SUIT = 'mrhollywood'
    VIEW_DISTANCE_TASK_NAME = 'ViewDistanceTask'
    MAX_VIEW_DISTANCE = 100.0
    GUARD_DIED_DELAY = 6.0
    MAX_HP = 200
    PROWLER_DISTANCE = 40.0
    IN_VIEW = 'somethingInSight'
    HEARD = 'heard'
    TRY_TO_CONFIRM_TIME = 5.0

    def __init__(self, world, guardKey):
        Suit.__init__(self)
        FSM.__init__(self, 'FactorySneakGuardSuit')
        self.gameWorld = world
        self.guardKey = guardKey
        self.viewDistanceTaskName = self.VIEW_DISTANCE_TASK_NAME + '-' + str(
            id(self))
        self.diedTaskName = 'GuardDied-' + str(id(self))
        self.health = 0
        self.maxHealth = self.MAX_HP
        self.eyeLight = None
        self.eyeLens = None
        self.eyeNode = None
        self.moveTrack = None
        self.trav = None
        self.rayNP = None
        self.queue = None
        self.currentKey = self.guardKey
        self.firstPoint = CGG.GuardPointData[self.guardKey]
        self.walkTrack = None
        self.pathQueue = []
        self.currentPathIndex = 0
        return

    def enterGuard(self):
        self.loop('neutral')
        pos, hpr = CGG.FactoryGuardPoints[self.guardKey]
        self.setHpr(hpr - (180, 0, 0))
        self.setPos(pos)
        base.taskMgr.add(self.__guard, self.taskName('guard'))

    def __checkToon(self):
        self.rayNP.lookAt(base.localAvatar)
        self.trav.traverse(render)
        if self.queue.getNumEntries() > 0:
            self.queue.sortEntries()
            hitObj = self.queue.getEntry(0).getIntoNodePath()
            print hitObj
            isLocalAvatar = hitObj.getParent().getPythonTag('localAvatar')
            if isLocalAvatar == 1:
                return 1
        return 0

    def __guard(self, task):
        if self.eyeNode.node().isInView(base.localAvatar.getPos(
                self.eyeNode)) and self.getDistance(
                    base.localAvatar) <= self.PROWLER_DISTANCE:
            if self.__checkToon():
                self.request('SeekTarget', self.IN_VIEW)
                return task.done
        return task.cont

    def exitGuard(self):
        base.taskMgr.remove(self.taskName('guard'))

    def enterTurnToGuardSpot(self):
        self.loop('walk')
        _, hpr = CGG.FactoryGuardPoints[self.guardKey]
        self.moveTrack = LerpHprInterval(self,
                                         duration=1.0,
                                         hpr=hpr,
                                         startHpr=self.getHpr())
        self.moveTrack.setDoneEvent(self.uniqueName('TurnedToGuardSpot'))
        self.acceptOnce(self.moveTrack.getDoneEvent(), self.request, ['Guard'])
        self.moveTrack.start()

    def exitTurnToGuardSpot(self):
        if self.moveTrack:
            self.ignore(self.moveTrack.getDoneEvent())
            self.moveTrack.finish()
            self.moveTrack = None
        return

    def enterSeekTarget(self, event):
        dialogue = random.choice(CGG.GuardDialog[event])
        self.setChat(dialogue)
        self.loop('walk')
        self.moveTrack = NPCLookInterval(self, base.localAvatar)
        self.moveTrack.setDoneEvent(self.uniqueName('SeekLocalAvatar'))
        self.acceptOnce(self.moveTrack.getDoneEvent(), self.request,
                        ['TryToConfirmTarget'])
        self.moveTrack.start()

    def exitSeekTarget(self):
        if self.moveTrack:
            self.ignore(self.moveTrack.getDoneEvent())
            self.moveTrack.finish()
            self.moveTrack = None
        return

    def enterTryToConfirmTarget(self):
        self.loop('neutral')
        base.taskMgr.add(self.__tryToConfirmTarget,
                         self.uniqueName('TryToConfirmTarget'))

    def __tryToConfirmTarget(self, task):
        if task.time >= self.TRY_TO_CONFIRM_TIME:
            chat = random.choice(CGG.GuardDialog['disregard'])
            self.setChat(chat)
            self.request('TurnToGuardSpot')
            return task.done
        if self.eyeNode.node().isInView(base.localAvatar.getPos(
                self.eyeNode)) and self.getDistance(
                    base.localAvatar) <= self.PROWLER_DISTANCE:
            if self.__checkToon():
                chat = random.choice(CGG.GuardDialog['spot'])
                self.setChat(chat)
                self.request('Pursue')
                return task.done
        return task.cont

    def exitTryToConfirmTarget(self):
        base.taskMgr.remove(self.uniqueName('TryToConfirmTarget'))

    def enterGoBackToGuardSpot(self):
        self.walkBackToGuardSpot()

    def walkBackToGuardSpot(self):
        self.currentPathIndex = 0
        self.pathQueue = SuitPathFinder.find_path(CGG.FactoryWalkPoints,
                                                  CGG.FactoryWayPointData,
                                                  self.currentKey,
                                                  self.guardKey)
        self.currentKey = self.guardKey
        self.walk(0.2)
        self.loop('walk')

    def exitGoBackToGuardSpot(self):
        pass

    def enterPursue(self):
        self.numTries = 0
        self.maxTries = 3
        self.runToClosestPoint()
        self.setPlayRate(1.5, 'walk')
        self.loop('walk')
        messenger.send('guardPursue')

    def getClosestPoint(self):
        closestPoint = None
        pointKey2range = {}
        for key, point in CGG.FactoryWalkPoints.items():
            dummyNode = render.attachNewNode('dummyNode')
            dummyNode.setPos(point)
            pointKey2range[key] = base.localAvatar.getDistance(dummyNode)
            dummyNode.removeNode()

        ranges = []
        for distance in pointKey2range.values():
            ranges.append(distance)

        ranges.sort()
        for key in pointKey2range.keys():
            distance = pointKey2range[key]
            if distance == ranges[0]:
                closestPoint = key
                break

        return closestPoint

    def runToClosestPoint(self):
        self.numTries += 1
        closestPoint = self.getClosestPoint()
        self.currentPathIndex = 0
        startKey = None
        if self.currentKey == self.guardKey and self.firstPoint in CGG.GuardPointData:
            startKey = CGG.GuardPointData[self.firstPoint]
        else:
            startKey = self.currentKey
        self.pathQueue = SuitPathFinder.find_path(CGG.FactoryWalkPoints,
                                                  CGG.FactoryWayPointData,
                                                  startKey, closestPoint)
        if self.currentKey == self.guardKey:
            self.pathQueue.insert(0, 1)
        else:
            self.pathQueue.insert(0, 0)
        self.currentKey = closestPoint
        self.walk(0.1)
        return

    def walk(self, speed=0.2):
        self.currentPathIndex += 1
        if len(self.pathQueue) <= self.currentPathIndex:
            if self.getCurrentOrNextState() == 'Pursue':
                if self.getClosestPoint() != self.currentKey:
                    if self.numTries >= self.maxTries:
                        self.request('GoBackToGuardSpot')
                    else:
                        self.runToClosestPoint()
            else:
                if self.getCurrentOrNextState() == 'GoBackToGuardSpot':
                    self.request('Guard')
            return
        print self.pathQueue[self.currentPathIndex]
        if self.currentPathIndex == 1 and self.pathQueue[0] == 1:
            startPoint = self.getPos(render)
            endPoint = CGG.FactoryWalkPoints[self.firstPoint]
        else:
            if self.pathQueue[0] == 0:
                self.pathQueue.remove(self.pathQueue[0])
            key = self.pathQueue[self.currentPathIndex]
            endPoint = CGG.FactoryWalkPoints[key]
            oldKey = self.pathQueue[self.currentPathIndex - 1]
            startPoint = CGG.FactoryWalkPoints[oldKey]
        self.walkTrack = NPCWalkInterval(self, endPoint, speed, startPoint)
        self.walkTrack.setDoneEvent(self.uniqueName('guardWalkDone'))
        self.acceptOnce(self.uniqueName('guardWalkDone'), self.walk)
        self.walkTrack.start()

    def exitPursue(self):
        self.setPlayRate(1.0, 'walk')
        del self.numTries
        if self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.pause()
            self.walkTrack = None
        messenger.send('guardStopPursue')
        return

    def uniqueName(self, name):
        return self.taskName(name)

    def taskName(self, name):
        return name + '-' + str(id(self))

    def setHealth(self, hp):
        self.health = hp

    def getHealth(self):
        return self.health

    def shot(self):
        dialogue = random.choice(CGG.GuardDialog['shot'])
        self.setChat(dialogue)

    def dead(self):
        self.request('Off')
        self.animFSM.request('die')
        base.taskMgr.doMethodLater(self.GUARD_DIED_DELAY, self.__diedDone,
                                   self.diedTaskName)

    def __diedDone(self, task):
        self.gameWorld.deleteGuard(self)
        return task.done

    def generate(self):
        data = CIGlobals.SuitBodyData[self.SUIT]
        type = data[0]
        team = data[1]
        self.team = team
        self.level = 12
        self.suit = type
        Suit.generate(self, SuitBank.MrHollywood, 0, hideFirst=False)
        self.suit = type
        base.taskMgr.add(self.__viewDistance, self.viewDistanceTaskName)
        self.setPythonTag('guard', self)
        self.eyeLight = Spotlight('eyes')
        self.eyeLens = PerspectiveLens()
        self.eyeLens.setMinFov(90.0 / (4.0 / 3.0))
        self.eyeLight.setLens(self.eyeLens)
        self.eyeNode = self.headModel.attachNewNode(self.eyeLight)
        self.eyeNode.setZ(-5)
        self.eyeNode.setY(-4.5)
        self.trav = CollisionTraverser(self.uniqueName('eyeTrav'))
        ray = CollisionRay(0, 0, 0, 0, 1, 0)
        rayNode = CollisionNode('ToonFPS.rayNode')
        rayNode.addSolid(ray)
        rayNode.setFromCollideMask(CGG.GuardBitmask | CIGlobals.WallBitmask)
        rayNode.setIntoCollideMask(BitMask32.allOff())
        self.rayNP = base.camera.attachNewNode(rayNode)
        self.rayNP.setZ(3)
        self.queue = CollisionHandlerQueue()
        self.trav.addCollider(self.rayNP, self.queue)
        self.trav.addCollider(self.gameWorld.mg.avatarBody, self.queue)
        self.request('Guard')

    def __viewDistance(self, task):
        if self.getDistance(base.localAvatar) > self.MAX_VIEW_DISTANCE:
            if not self.isHidden():
                self.hide()
        else:
            if self.isHidden():
                self.show()
        task.delayTime = 1.0
        return task.again

    def disable(self):
        self.request('Off')
        base.taskMgr.remove(self.taskName('guard'))
        base.taskMgr.remove(self.diedTaskName)
        base.taskMgr.remove(self.viewDistanceTaskName)
        self.trav = None
        if self.rayNP:
            self.rayNP.removeNode()
            self.rayNP = None
        self.queue = None
        self.currentPathIndex = None
        if self.eyeNode:
            self.eyeNode.removeNode()
            self.eyeNode = None
            self.eyeLens = None
            self.eyeLight = None
        self.viewDistanceTaskName = None
        self.guardKey = None
        self.gameWorld = None
        self.pathQueue = None
        if self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.finish()
            self.walkTrack = None
        Suit.disable(self)
        return
class FactorySneakGuardSuit(Suit, FSM):
    notify = directNotify.newCategory("FactorySneakGuardSuit")

    SUIT = "mrhollywood"
    VIEW_DISTANCE_TASK_NAME = "ViewDistanceTask"
    MAX_VIEW_DISTANCE = 100.0
    GUARD_DIED_DELAY = 6.0
    MAX_HP = 200
    PROWLER_DISTANCE = 40.0

    IN_VIEW = "somethingInSight"
    HEARD = "heard"
    TRY_TO_CONFIRM_TIME = 5.0

    def __init__(self, world, guardKey):
        Suit.__init__(self)
        FSM.__init__(self, 'FactorySneakGuardSuit')
        self.gameWorld = world
        self.guardKey = guardKey
        self.viewDistanceTaskName = self.VIEW_DISTANCE_TASK_NAME + "-" + str(
            id(self))
        self.diedTaskName = "GuardDied-" + str(id(self))
        self.health = 0
        self.maxHealth = self.MAX_HP
        self.eyeLight = None
        self.eyeLens = None
        self.eyeNode = None
        self.moveTrack = None
        self.trav = None
        self.rayNP = None
        self.queue = None
        self.currentKey = self.guardKey
        self.firstPoint = CGG.GuardPointData[self.guardKey]
        self.walkTrack = None
        self.pathQueue = []
        self.currentPathIndex = 0

    def enterGuard(self):
        self.loop('neutral')
        pos, hpr = CGG.FactoryGuardPoints[self.guardKey]
        self.setHpr(hpr - (180, 0, 0))
        self.setPos(pos)

        base.taskMgr.add(self.__guard, self.taskName("guard"))

    def __checkToon(self):
        self.rayNP.lookAt(base.localAvatar)
        self.trav.traverse(render)
        if self.queue.getNumEntries() > 0:
            self.queue.sortEntries()
            hitObj = self.queue.getEntry(0).getIntoNodePath()
            print hitObj
            isLocalAvatar = hitObj.getParent().getPythonTag('localAvatar')
            if isLocalAvatar == 1:
                # Yes! We see the prowler!
                return 1
        return 0

    def __guard(self, task):
        # Let me check if the target is my frustrum, and if it's a close enough distance from me.
        if (self.eyeNode.node().isInView(base.localAvatar.getPos(self.eyeNode))
                and
                self.getDistance(base.localAvatar) <= self.PROWLER_DISTANCE):
            # Now, let me check if the toon is standing right in front of me; not occluded.
            if self.__checkToon():
                # Yes! We see some one!
                self.request('SeekTarget', self.IN_VIEW)
                return task.done
        return task.cont

    def exitGuard(self):
        base.taskMgr.remove(self.taskName("guard"))

    def enterTurnToGuardSpot(self):
        self.loop('walk')
        _, hpr = CGG.FactoryGuardPoints[self.guardKey]
        self.moveTrack = LerpHprInterval(self,
                                         duration=1.0,
                                         hpr=hpr,
                                         startHpr=self.getHpr())
        self.moveTrack.setDoneEvent(self.uniqueName('TurnedToGuardSpot'))
        self.acceptOnce(self.moveTrack.getDoneEvent(), self.request, ['Guard'])
        self.moveTrack.start()

    def exitTurnToGuardSpot(self):
        if self.moveTrack:
            self.ignore(self.moveTrack.getDoneEvent())
            self.moveTrack.finish()
            self.moveTrack = None

    def enterSeekTarget(self, event):
        dialogue = random.choice(CGG.GuardDialog[event])
        self.setChat(dialogue)

        self.loop('walk')
        self.moveTrack = NPCLookInterval(self, base.localAvatar)
        self.moveTrack.setDoneEvent(self.uniqueName("SeekLocalAvatar"))
        self.acceptOnce(self.moveTrack.getDoneEvent(), self.request,
                        ['TryToConfirmTarget'])
        self.moveTrack.start()

    def exitSeekTarget(self):
        if self.moveTrack:
            self.ignore(self.moveTrack.getDoneEvent())
            self.moveTrack.finish()
            self.moveTrack = None

    def enterTryToConfirmTarget(self):
        self.loop('neutral')
        base.taskMgr.add(self.__tryToConfirmTarget,
                         self.uniqueName('TryToConfirmTarget'))

    def __tryToConfirmTarget(self, task):
        if task.time >= self.TRY_TO_CONFIRM_TIME:
            # Hmm, I guess it was nothing.
            chat = random.choice(CGG.GuardDialog['disregard'])
            self.setChat(chat)
            self.request('TurnToGuardSpot')
            return task.done
        # Let me see the target again, so I know it's actually something.
        if (self.eyeNode.node().isInView(base.localAvatar.getPos(self.eyeNode))
                and
                self.getDistance(base.localAvatar) <= self.PROWLER_DISTANCE):
            # Now, let me check if the toon is standing right in front of me; not occluded.
            if self.__checkToon():
                # There he is!
                chat = random.choice(CGG.GuardDialog['spot'])
                self.setChat(chat)
                self.request('Pursue')
                return task.done
        return task.cont

    def exitTryToConfirmTarget(self):
        base.taskMgr.remove(self.uniqueName('TryToConfirmTarget'))

    def enterGoBackToGuardSpot(self):
        self.walkBackToGuardSpot()

    def walkBackToGuardSpot(self):
        self.currentPathIndex = 0
        self.pathQueue = SuitPathFinder.find_path(CGG.FactoryWalkPoints,
                                                  CGG.FactoryWayPointData,
                                                  self.currentKey,
                                                  self.guardKey)
        self.currentKey = self.guardKey
        self.walk(0.2)
        self.loop('walk')

    def exitGoBackToGuardSpot(self):
        pass

    def enterPursue(self):
        self.numTries = 0
        self.maxTries = 3
        self.runToClosestPoint()
        self.setPlayRate(1.5, 'walk')
        self.loop('walk')
        messenger.send('guardPursue')

    def getClosestPoint(self):
        # Return the key of the closest point to the localAvatar.
        closestPoint = None
        pointKey2range = {}
        for key, point in CGG.FactoryWalkPoints.items():
            dummyNode = render.attachNewNode('dummyNode')
            dummyNode.setPos(point)
            pointKey2range[key] = base.localAvatar.getDistance(dummyNode)
            dummyNode.removeNode()
        ranges = []
        for distance in pointKey2range.values():
            ranges.append(distance)
        ranges.sort()
        for key in pointKey2range.keys():
            distance = pointKey2range[key]
            if distance == ranges[0]:
                closestPoint = key
                break
        return closestPoint

    def runToClosestPoint(self):
        self.numTries += 1
        closestPoint = self.getClosestPoint()
        self.currentPathIndex = 0
        startKey = None
        if self.currentKey == self.guardKey:
            startKey = CGG.GuardPointData[self.firstPoint]
        else:
            startKey = self.currentKey
        self.pathQueue = SuitPathFinder.find_path(CGG.FactoryWalkPoints,
                                                  CGG.FactoryWayPointData,
                                                  startKey, closestPoint)
        if self.currentKey == self.guardKey:
            self.pathQueue.insert(0, 1)
        else:
            self.pathQueue.insert(0, 0)
        self.currentKey = closestPoint
        self.walk(0.1)

    def walk(self, speed=0.2):
        self.currentPathIndex += 1
        if len(self.pathQueue) <= self.currentPathIndex:
            if self.getCurrentOrNextState() == 'Pursue':
                if self.getClosestPoint() != self.currentKey:
                    # Wow, the player ran off somewhere else! Go there!
                    if self.numTries >= self.maxTries:
                        # Dang it, give up, we can't get to them!
                        self.request('GoBackToGuardSpot')
                    else:
                        self.runToClosestPoint()
            elif self.getCurrentOrNextState() == 'GoBackToGuardSpot':
                self.request('Guard')
            return
        print self.pathQueue[self.currentPathIndex]
        if self.currentPathIndex == 1 and self.pathQueue[0] == 1:
            # We need to walk from our guard point to the first waypoint in our path
            startPoint = self.getPos(render)
            endPoint = CGG.FactoryWalkPoints[self.firstPoint]
        else:
            if self.pathQueue[0] == 0:
                self.pathQueue.remove(self.pathQueue[0])
            key = self.pathQueue[self.currentPathIndex]
            endPoint = CGG.FactoryWalkPoints[key]
            oldKey = self.pathQueue[self.currentPathIndex - 1]
            startPoint = CGG.FactoryWalkPoints[oldKey]
        self.walkTrack = NPCWalkInterval(self, endPoint, speed, startPoint)
        self.walkTrack.setDoneEvent(self.uniqueName('guardWalkDone'))
        self.acceptOnce(self.uniqueName('guardWalkDone'), self.walk)
        self.walkTrack.start()

    def exitPursue(self):
        self.setPlayRate(1.0, 'walk')
        del self.numTries
        if self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.pause()
            self.walkTrack = None
        messenger.send('guardStopPursue')

    def uniqueName(self, name):
        return self.taskName(name)

    def taskName(self, name):
        return name + "-" + str(id(self))

    def setHealth(self, hp):
        self.health = hp

    def getHealth(self):
        return self.health

    def shot(self):
        dialogue = random.choice(CGG.GuardDialog['shot'])
        self.setChat(dialogue)

    def dead(self):
        self.request('Off')
        self.animFSM.request('die')
        base.taskMgr.doMethodLater(self.GUARD_DIED_DELAY, self.__diedDone,
                                   self.diedTaskName)

    def __diedDone(self, task):
        self.gameWorld.deleteGuard(self)
        return task.done

    def generate(self):
        data = CIGlobals.SuitBodyData[self.SUIT]
        type = data[0]
        team = data[1]
        self.generateSuit(type, self.SUIT, team, self.MAX_HP, 0, False)
        base.taskMgr.add(self.__viewDistance, self.viewDistanceTaskName)
        self.setPythonTag('guard', self)
        self.eyeLight = Spotlight('eyes')
        self.eyeLens = PerspectiveLens()
        self.eyeLens.setMinFov(90.0 / (4. / 3.))
        self.eyeLight.setLens(self.eyeLens)
        self.eyeNode = self.headModel.attachNewNode(self.eyeLight)
        self.eyeNode.setZ(-5)
        self.eyeNode.setY(-4.5)
        self.trav = CollisionTraverser(self.uniqueName('eyeTrav'))
        ray = CollisionRay(0, 0, 0, 0, 1, 0)
        rayNode = CollisionNode('ToonFPS.rayNode')
        rayNode.addSolid(ray)
        rayNode.setFromCollideMask(CGG.GuardBitmask | CIGlobals.WallBitmask)
        rayNode.setIntoCollideMask(BitMask32.allOff())
        self.rayNP = base.camera.attachNewNode(rayNode)
        self.rayNP.setZ(3)
        self.queue = CollisionHandlerQueue()
        self.trav.addCollider(self.rayNP, self.queue)
        self.trav.addCollider(self.gameWorld.mg.avatarBody, self.queue)
        self.request('Guard')

    def __viewDistance(self, task):
        # All the guards in the warehouse eat up a lot of frames.  This task will
        # hide the guard geometry if it's too far away.

        if self.getDistance(base.localAvatar) > self.MAX_VIEW_DISTANCE:
            if not self.isHidden():
                self.hide()
        else:
            if self.isHidden():
                self.show()

        task.delayTime = 1.0
        return task.again

    def disable(self):
        self.request('Off')
        base.taskMgr.remove(self.taskName("guard"))
        base.taskMgr.remove(self.diedTaskName)
        base.taskMgr.remove(self.viewDistanceTaskName)
        self.trav = None
        if self.rayNP:
            self.rayNP.removeNode()
            self.rayNP = None
        self.queue = None
        self.currentPathIndex = None
        if self.eyeNode:
            self.eyeNode.removeNode()
            self.eyeNode = None
            self.eyeLens = None
            self.eyeLight = None
        self.viewDistanceTaskName = None
        self.guardKey = None
        self.gameWorld = None
        self.pathQueue = None
        if self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.finish()
            self.walkTrack = None
        Suit.disable(self)