class WayPointTest:
    def __init__(self, point, number, wayPointsToTest):
        self.point = point
        self.number = number
        self.wayPointsToTest = wayPointsToTest
        del self.wayPointsToTest[number]
        self.finalList = []
        self.currentWayPointTestKey = None
        self.numberOfTests = -1
        self.movementIval = None
        self.allTestsDone = False
        print "Testing waypoint: " + str(number)
        self.testNextWayPoint()

    def allTestsCompleted(self):
        print "Done testing waypoint: " + str(self.number)
        self.allTestsDone = True

    def testNextWayPoint(self):
        self.numberOfTests += 1
        if self.movementIval:
            self.movementIval.pause()
            self.movementIval = None
        if self.numberOfTests > len(self.wayPointsToTest.keys()) - 1:
            self.allTestsCompleted()
            return
        print "Test number " + str(self.numberOfTests) + " on waypoint " + str(
            self.number)
        self.currentWayPointTestKey = self.wayPointsToTest.keys()[
            self.numberOfTests]
        self.movementIval = NPCWalkInterval(
            node,
            Point3(*self.wayPointsToTest[self.currentWayPointTestKey]['pos']),
            0.005,
            startPos=self.point,
            fluid=1)
        self.movementIval.setDoneEvent('testWayPointDone')
        base.acceptOnce(self.movementIval.getDoneEvent(),
                        self.currentTestSucceeded)
        base.acceptOnce("sensors-into", self.handleBadTest)
        self.movementIval.start()

    def handleBadTest(self, entry):
        print "Failed"
        base.ignore("sensors-into")
        base.ignore("sensors-out")
        base.ignore(self.movementIval.getDoneEvent())
        self.movementIval.pause()
        self.movementIval = None
        self.testNextWayPoint()

    def currentTestSucceeded(self):
        print "Passed"
        base.ignore("sensors-into")
        base.ignore("sensors-out")
        base.ignore(self.movementIval.getDoneEvent())
        self.finalList.append(self.currentWayPointTestKey)
        print self.finalList
        self.testNextWayPoint()
Esempio n. 2
0
 def enterStationSlot(self, slot):
     self.cr.playGame.getPlace().fsm.request('station')
     camera.reparentTo(self)
     numSlots = len(self.circles)
     camera.setPos(self.numPlayers2CamPos[numSlots])
     camera.setPos(camera.getPos(render))
     camera.reparentTo(render)
     camera.lookAt(self)
     base.localAvatar.headsUp(slot)
     base.localAvatar.setAnimState('run')
     runTrack = NPCWalkInterval(base.localAvatar, slot.getPos(render), 0.1, startPos=base.localAvatar.getPos(render))
     runTrack.setDoneEvent('SlotEnterDone')
     runTrack.start()
     base.acceptOnce('SlotEnterDone', self.__handleSlotEntrance)
def doPath():
    global path
    if not len(path):
        suit.loop('neutral')
        return
    endX, endY = path[0]
    endPoint = Point3(endX, endY, 0)
    startPoint = suit.getPos(render)
    path.remove(path[0])
    ival = NPCWalkInterval(suit, endPoint, 0.2, startPoint)
    ival.setDoneEvent(suit.uniqueName('guardWalkDone'))
    base.acceptOnce(suit.uniqueName('guardWalkDone'), doPath)
    ival.start()
    suit.loop('walk')
Esempio n. 4
0
 def enterStationSlot(self, slot):
     self.cr.playGame.getPlace().fsm.request('station')
     camera.reparentTo(self)
     numSlots = len(self.circles)
     camera.setPos(self.numPlayers2CamPos[numSlots])
     camera.setPos(camera.getPos(render))
     camera.reparentTo(render)
     camera.lookAt(self)
     base.localAvatar.headsUp(slot)
     base.localAvatar.b_setAnimState('run')
     runTrack = NPCWalkInterval(base.localAvatar, slot.getPos(render), 0.1, startPos=base.localAvatar.getPos(render))
     runTrack.setDoneEvent('SlotEnterDone')
     runTrack.start()
     base.acceptOnce('SlotEnterDone', self.__handleSlotEntrance)
Esempio n. 5
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 DistributedDeliveryGameSuitAI(DistributedSuitAI):
    notify = directNotify.newCategory('DistributedDeliveryGameSuitAI')

    def __init__(self, air, mg):
        DistributedSuitAI.__init__(self, air)
        self.mg = mg
        self.truck = random.choice(self.mg.trucks)
        self.truckIndex = self.mg.trucks.index(self.truck)
        self.spawnPoint = None
        return

    def walkToTruck(self):
        index = DGG.WalkToTruckIndex
        pos = DGG.TruckSuitPointsByIndex[self.truckIndex]
        startPos = self.getPos(render)
        self.b_setSuitState(1, -1, index)
        durationFactor = 0.2
        pathName = self.uniqueName('WalkToTruck')
        self.walkTrack = NPCWalkInterval(self, pos, startPos=startPos, name=pathName, durationFactor=durationFactor, fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.acceptOnce(self.walkTrack.getDoneEvent(), self.__walkedToTruck)
        self.walkTrack.start()
        self.b_setAnimState(SuitGlobals.getAnimId(SuitGlobals.getAnimByName('walk')))

    def __walkedToTruck(self):
        self.truck.suitPickUpBarrel(self.doId)
        self.walkBackToSpawnPointWithBarrel()

    def walkBackToSpawnPointWithBarrel(self):
        pos = DGG.SpawnPoints[self.spawnPoint]
        startPos = self.getPos(render)
        self.b_setSuitState(1, -1, self.spawnPoint)
        durationFactor = 0.2
        pathName = self.uniqueName('WalkBackToSpawn')
        self.walkTrack = NPCWalkInterval(self, pos, startPos=startPos, name=pathName, durationFactor=durationFactor, fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.acceptOnce(self.walkTrack.getDoneEvent(), self.__walkedBack2Spawn)
        self.walkTrack.start()
        self.b_setAnimState(SuitGlobals.getAnimId(SuitGlobals.getAnimByName('tray-walk')))

    def __walkedBack2Spawn(self):
        self.b_setSuitState(3, self.spawnPoint, self.spawnPoint)
        base.taskMgr.doMethodLater(10, self.__finished, self.uniqueName('finishSuit'))

    def __finished(self, task):
        self.mg.suits.remove(self)
        self.truck.barrelDroppedOff()
        self.requestDelete()
        return task.done

    def spawn(self):
        pos = random.choice(DGG.SpawnPoints)
        index = DGG.SpawnPoints.index(pos)
        self.spawnPoint = index
        self.b_setSuitState(2, index, index)
        flyTrack = self.posInterval(3, pos, startPos=pos + (0, 0, 50))
        flyTrack.start()
        self.track = Sequence()
        self.track.append(Wait(5.4))
        self.track.append(Func(self.b_setAnimState, 'neutral'))
        self.track.append(Wait(1.0))
        self.track.append(Func(self.walkToTruck))
        self.track.start()
        self.b_setParent(CIGlobals.SPRender)

    def delete(self):
        base.taskMgr.remove(self.uniqueName('finishSuit'))
        if hasattr(self, 'walkTrack') and self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.finish()
            self.walkTrack = None
        self.mg = None
        self.truck = None
        self.truckIndex = None
        self.spawnPoint = None
        DistributedSuitAI.delete(self)
        return
Esempio n. 7
0
class DistributedEagleSuitAI(DistributedSuitAI):
    notify = directNotify.newCategory('DistributedEagleSuitAI')

    def __init__(self, air):
        DistributedSuitAI.__init__(self, air)
        self.mg = None
        self.flyTrack = None
        self.currentFlyPoint = None
        self.flySpeed = 0.0
        return

    def setMinigame(self, mg):
        self.mg = mg

    def getMinigame(self):
        return self.mg

    def handleGotHit(self):
        self.b_setAnimState('flail')
        if self.flyTrack:
            self.ignore(self.flyTrack.getDoneEvent())
            self.flyTrack.pause()
            self.flyTrack = None
        self.sendUpdate('fallAndExplode', [])
        self.b_setSuitState(6, -1, -1)
        self.flyTrack = Sequence(LerpPosInterval(self, duration=4.0, pos=self.getPos(render) - (0, 0, 75), startPos=self.getPos(render), blendType='easeIn'), Wait(1.5), Func(self.killMe))
        self.flyTrack.start()
        return

    def killMe(self):
        self.disable()
        self.requestDelete()

    def setFlySpeed(self, value):
        self.flySpeed = value

    def b_setFlySpeed(self, value):
        self.sendUpdate('setFlySpeed', [value])
        self.setFlySpeed(value)

    def getFlySpeed(self):
        return self.flySpeed

    def spawn(self):
        if not self.getMinigame():
            self.notify.error('Tried to spawn before self.mg was set!')
        self.b_setAnimState('flyNeutral')
        point = random.choice(EGG.EAGLE_FLY_POINTS)
        self.setPos(point)
        self.d_setPos(*point)
        self.b_setFlySpeed(EGG.ROUND_2_EAGLE_SPEED[self.getMinigame().getRound()])
        self.createFlyPath()
        self.b_setParent(CIGlobals.SPRender)

    def createFlyPath(self):
        self.b_setAnimState('flyNeutral')
        if self.flyTrack:
            self.ignore(self.flyTrack.getDoneEvent())
            self.flyTrack.pause()
            self.flyTrack = None
        point = random.choice(EGG.EAGLE_FLY_POINTS)
        if self.currentFlyPoint == point:
            self.createFlyPath()
            return
        else:
            if self.currentFlyPoint == None:
                point_list = list(EGG.EAGLE_FLY_POINTS)
                point_list.remove(point)
                startIndex = point_list.index(random.choice(point_list))
            else:
                startIndex = -1
            self.b_setSuitState(5, startIndex, EGG.EAGLE_FLY_POINTS.index(point))
            mgRound = self.getMinigame().getRound()
            if mgRound:
                self.flyTrack = NPCWalkInterval(self, point, durationFactor=EGG.ROUND_2_EAGLE_SPEED[mgRound], startPos=self.getPos(render), fluid=1, name=self.uniqueName('DEagleSuitAI-flyTrack'))
                self.flyTrack.setDoneEvent(self.flyTrack.getName())
                self.acceptOnce(self.flyTrack.getDoneEvent(), self.handleFlyDone)
                self.flyTrack.start()
                self.currentFlyPoint = point
            else:
                return
            return

    def handleFlyDone(self):
        self.createFlyPath()

    def delete(self):
        del self.currentFlyPoint
        del self.mg
        if self.flyTrack:
            self.ignore(self.flyTrack.getDoneEvent())
            self.flyTrack.pause()
            self.flyTrack = None
        DistributedSuitAI.delete(self)
        return
Esempio n. 8
0
class DistributedSuitAI(DistributedAvatarAI, DistributedSmoothNodeAI):
    notify = directNotify.newCategory("DistributedSuitAI")

    def __init__(self, air):
        try:
            self.DistributedSuitAI_initialized
            return
        except:
            self.DistributedSuitAI_initialized = 1
        DistributedAvatarAI.__init__(self, air)
        DistributedSmoothNodeAI.__init__(self, air)
        self.itemDropper = SuitItemDropper(self)
        self.avatarType = CIGlobals.Suit
        self.aiChar = None
        self.aiBehaviors = None
        self.walkTrack = None
        self.name = ""
        self.anim = "neutral"
        self.state = "alive"
        self.damage = 0
        self.health = 132
        self.type = "A"
        self.team = "c"
        self.head = "bigcheese"
        self.name = "The Big Cheese"
        self.skeleton = 0
        self.dmg_lbl = None
        self.lbl_int = None
        self.bean = None
        self.boss = None
        self.brain = None
        self.startPoint = -1
        self.endPoint = -1
        self.suitState = 0
        self.walkPaused = 0
        self.attacking = False
        self.suitHealTrack = None
        self.continuePathId = 0
        self.attackId = 0
        self.mgr = None
        self.backup = 0
        self.difficulty = None
        self.track = None
        self.lateX = 0
        self.lateY = 0
        self.stateTimestamp = 0
        self.animState2animId = {
            'off': 13,
            'neutral': 10,
            'walk': 9,
            'die': 5,
            'attack': 7,
            'flydown': 1,
            'pie': 4,
            'win': 12,
            'flyaway': 14,
            'rollodex': 3,
            'flyNeutral': 15,
            'flail': 0,
            'drop': 6,
            'drop-react': 16,
            'squirt-large': 8,
            'squirt-small': 11,
            'soak': 2,
        }
        self.animId2animState = {
            v: k
            for k, v in self.animState2animId.items()
        }
        self.level = 0
        self.currentPathQueue = []
        return

    def resetPathQueue(self):
        self.currentPathQueue = []

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

    def d_setLevel(self, level):
        self.sendUpdate('setLevel', [level])

    def b_setLevel(self, level):
        self.d_setLevel(level)
        self.setLevel(level)

    def getLevel(self):
        return self.level

    def setLatePos(self, x, y):
        self.lateX = x
        self.lateY = y

    def getLatePos(self):
        return [self.lateX, self.lateY]

    def setSuitState(self, index, startPoint, endPoint):
        if index == 0:
            self.setLatePos(self.getX(render), self.getY(render))
        self.suitState = index
        self.startPoint = startPoint
        self.endPoint = endPoint

    def d_setSuitState(self, index, startPoint, endPoint):
        self.stateTimestamp = globalClockDelta.getFrameNetworkTime()
        self.sendUpdate('setSuitState',
                        [index, startPoint, endPoint, self.stateTimestamp])

    def b_setSuitState(self, index, startPoint, endPoint):
        self.d_setSuitState(index, startPoint, endPoint)
        self.setSuitState(index, startPoint, endPoint)

    def getSuitState(self):
        return [
            self.suitState, self.startPoint, self.endPoint, self.stateTimestamp
        ]

    def setDifficulty(self, difficulty):
        self.difficulty = difficulty

    def getDifficulty(self):
        return self.difficulty

    def setBackup(self, backup):
        self.backup = backup

    def isBackup(self):
        return self.backup

    def setManager(self, mgr):
        self.mgr = mgr
        self.hood = CogBattleGlobals.HoodIndex2HoodName[
            self.getManager().getBattle().getHoodIndex()]

    def getManager(self):
        return self.mgr

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

    def spawn(self):
        self.brain = CogBrainAI.CogBrain(self)
        landspot = random.choice(CIGlobals.SuitSpawnPoints[self.hood].keys())
        path = CIGlobals.SuitSpawnPoints[self.hood][landspot]
        index = CIGlobals.SuitSpawnPoints[self.hood].keys().index(landspot)
        self.b_setSuitState(2, index, index)
        self.currentPath = landspot
        track = self.posInterval(3, path, startPos=path + (0, 0, 50))
        track.start()
        yaw = random.uniform(0.0, 360.0)
        self.setH(yaw)
        if self.track:
            self.track.pause()
            self.track = None
        self.track = Sequence(Wait(5.4), Func(self.b_setAnimState, 'neutral'),
                              Wait(1.0), Func(self.startRoaming))
        self.track.start()
        self.b_setParent(CIGlobals.SPRender)

    def startRoaming(self):
        if self.head == "vp" or self.isBackup():
            # If this is a vp or a backup cog, do the random attacks.
            self.startAttacks()
        taskMgr.add(self.monitorHealth, self.uniqueName('monitorHealth'))
        if self.head == "vp":
            self.boss = SuitBoss.SuitBoss(self)
            self.boss.startBoss()
        else:
            self.brain.start()

    def startAttacks(self):
        if self.head != "vp":
            attackTime = random.randint(8, 20)
        else:
            attackTime = random.randint(8, 12)
        taskMgr.doMethodLater(attackTime, self.attackTask,
                              self.uniqueName('attackTask'))

    def attackTask(self, task):
        if self.brain.fsm.getCurrentState().getName() == "runAway":
            # Attack while running away... ain't nobody got time for that!
            delay = random.randint(6, 12)
            task.delayTime = delay
            return task.again
        if self.head == "vp":
            # We can't attack while we're flying
            if not self.boss.getFlying():
                self.chooseVictim()
        else:
            self.chooseVictim()
        if self.head != "vp":
            delay = random.randint(6, 15)
        else:
            delay = random.randint(6, 12)
        task.delayTime = delay
        return task.again

    def enableMovement(self):
        self.brain.start()
        if self.head != "vp":
            attackTime = random.randint(8, 20)
        else:
            attackTime = random.randint(8, 12)
        taskMgr.doMethodLater(attackTime, self.attackTask,
                              self.uniqueName('attackTask'))

    def disableMovement(self):
        taskMgr.remove(self.uniqueName('attackTask'))
        taskMgr.remove(self.uniqueName('continueSuitRoam'))
        if self.suitHealTrack:
            self.suitHealTrack.pause()
            self.suitHealTrack = None
        self.brain.end()
        self.b_setSuitState(3, -1, -1)
        if self.head != "vp":
            if self.walkTrack:
                self.ignore(self.walkTrack.getName())
                self.walkTrack.clearToInitial()
                self.walkTrack = None
        self.d_interruptAttack()

    def chooseVictim(self):
        toons = []
        for key in self.air.doId2do.keys():
            val = self.air.doId2do[key]
            if val.__class__.__name__ == "DistributedToonAI" or val.__class__.__name__ == "DistributedSuitAI" or val.__class__.__name__ == "DistributedPieTurretAI":
                if val.zoneId == self.zoneId:
                    if val.__class__.__name__ == "DistributedSuitAI" and val.head == "vp" \
                    and val.doId != self.doId or val.__class__.__name__ == "DistributedToonAI" or val.__class__.__name__ == "DistributedPieTurretAI":
                        # We can be a medic and heal the fellow VP...
                        if not val.isDead():
                            if self.getDistance(val) <= 40:
                                if val.__class__.__name__ == "DistributedToonAI":
                                    if not val.getGhost():
                                        toons.append(val)
                                else:
                                    toons.append(val)
        if toons == []:
            return
        toon = random.randint(0, len(toons) - 1)
        self.disableMovement()
        self.headsUp(toons[toon])
        self.attackToon(toons[toon])
        self.setAttacking(True)

    def attackToon(self, av):
        if av.__class__.__name__ in [
                "DistributedSuitAI", "DistributedPieTurretAI"
        ]:
            # Why would I pick pocket my boss?
            attack = random.randint(0, 6)
            attackName = SuitAttacks.SuitAttackLengths.keys()[attack]
        else:
            if self.head in ['vp']:
                attack = random.randint(0, 6)
                attackName = SuitAttacks.SuitAttackLengths.keys()[attack]
            else:
                attackName = random.choice(
                    SuitAttacks.SuitAttackLengths.keys())
                attack = SuitAttacks.SuitAttackLengths.keys().index(attackName)
        attackTaunt = random.randint(
            0,
            len(CIGlobals.SuitAttackTaunts[attackName]) - 1)
        timestamp = globalClockDelta.getFrameNetworkTime()
        self.sendUpdate('doAttack', [attack, av.doId, timestamp])
        if av.__class__.__name__ in [
                "DistributedSuitAI", "DistributedPieTurretAI"
        ]:
            distance = self.getDistance(av)
            speed = 50.0
            if attackName == "glowerpower":
                speed = 100.0
            timeUntilHeal = distance / speed
            if av.__class__.__name__ == "DistributedSuitAI":
                self.d_setChat(CIGlobals.SuitHealTaunt)
            else:
                self.d_setChat(
                    CIGlobals.SuitAttackTaunts[attackName][attackTaunt])
            if attackName != "glowerpower":
                if self.type == "C":
                    timeUntilRelease = 2.2
                else:
                    timeUntilRelease = 3.0
            else:
                timeUntilRelease = 1.0
            currentBossPos = av.getPos(render)
            hp = int(self.maxHealth /
                     SuitAttacks.SuitAttackDamageFactors[attackName])
            self.suitHealTrack = Sequence(
                Wait(timeUntilRelease + timeUntilHeal),
                Func(self.attemptToHealBoss, av, currentBossPos, hp))
            self.suitHealTrack.start()
        else:
            self.d_setChat(CIGlobals.SuitAttackTaunts[attackName][attackTaunt])
        time = SuitAttacks.SuitAttackLengths[attackName]
        if self.track:
            self.track.pause()
            self.track = None
        taskMgr.doMethodLater(SuitAttacks.SuitAttackLengths[attackName],
                              self.continuePathTask,
                              self.uniqueName('continueSuitRoam'))

    def attemptToHealBoss(self, boss, currBossPos, hp):
        if not boss.isEmpty():
            if (boss.getPos(render) - currBossPos).length() <= 1:
                if not boss.isDead():
                    if boss.__class__.__name__ == "DistributedSuitAI":
                        boss.b_setHealth(boss.getHealth() + hp)
                        boss.d_announceHealth(1, hp)
                    else:
                        # Turret
                        boss.b_setHealth(boss.getHealth() - hp)
                        boss.d_announceHealth(0, hp)
                    self.d_handleWeaponTouch()

    def continuePathTask(self, task):
        self.setAttacking(False)
        if self.head != "vp":
            if self.brain.fsm.getCurrentState().getName() == "followBoss":
                # If we're protecting the boss, don't walk away from him!
                return task.done
            else:
                self.brain.neutral_startLookingForToons()
                self.brain.start()
                return task.done
        self.continuePath()
        return task.done

    def d_handleWeaponTouch(self):
        self.sendUpdate("handleWeaponTouch", [])

    def continuePath(self):
        # Create a new path for the Suit if they are stuck...
        if self.head != "vp":
            if self.walkTrack:
                self.ignore(self.walkTrack.getName())
                self.walkTrack.clearToInitial()
                self.walkTrack = None
            self.brain.end()
            self.brain.start()
        else:
            self.b_setAnimState("neutral")

    def setAttacking(self, value):
        self.attacking = value

    def getAttacking(self):
        return self.attacking

    def monitorHealth(self, task):
        if self.health <= 0:
            taskMgr.remove(self.uniqueName('attackTask'))
            taskMgr.remove(self.uniqueName('continueSuitRoam'))
            if self.suitHealTrack:
                self.suitHealTrack.pause()
                self.suitHealTrack = None
            self.b_setSuitState(3, -1, -1)
            if self.walkTrack:
                self.ignore(self.walkTrack.getName())
                self.walkTrack.clearToInitial()
                self.walkTrack = None
            self.d_interruptAttack()
            self.brain.end()
            if self.head == "vp":
                self.boss.stopBoss()
            if self.track:
                self.track.pause()
                self.track = None

            anim2WaitTime = {
                'pie': 2.0,
                'drop': 6.0,
                'drop-react': 3.5,
                'squirt-small': 4.0,
                'squirt-large': 4.9,
                'soak': 6.5,
                'neutral': 0.0,
                'walk': 0.0
            }
            self.track = Sequence(Wait(anim2WaitTime[self.getAnimStateStr()]),
                                  Func(self.killSuit))
            self.track.start()

            return task.done
        return task.cont

    def isWalking(self):
        if self.walkTrack:
            return self.walkTrack.isPlaying()
        else:
            return False

    def killSuit(self):
        self.b_setAnimState('die')
        if self.track:
            self.track.pause()
            self.track = None
        self.track = Sequence(Wait(6.0), Func(self.closeSuit))
        self.track.start()

    def closeSuit(self):
        # Drop the jellybeans I stole before I die!
        self.itemDropper.drop()
        self.getManager().deadSuit(self.doId)
        self.disable()
        self.requestDelete()

    def createPath(self, path_key=None, durationFactor=0.2, fromCurPos=False):
        if path_key == None and not len(self.currentPathQueue):
            path_key_list = CIGlobals.SuitPathData[self.hood][self.currentPath]
            path_key = random.choice(path_key_list)
        elif len(self.currentPathQueue):
            path_key = self.currentPathQueue[0]
            self.currentPathQueue.remove(path_key)
        endIndex = CIGlobals.SuitSpawnPoints[self.hood].keys().index(path_key)
        path = CIGlobals.SuitSpawnPoints[self.hood][path_key]
        if self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.clearToInitial()
            self.walkTrack = None
        if not self.currentPath or fromCurPos:
            startIndex = -1
        else:
            oldPath = self.currentPath
            startIndex = CIGlobals.SuitSpawnPoints[self.hood].keys().index(
                oldPath)
        self.currentPath = path_key
        pathName = self.uniqueName('suitPath')
        self.walkTrack = NPCWalkInterval(self,
                                         path,
                                         startPos=self.getPos(render),
                                         name=pathName,
                                         durationFactor=durationFactor,
                                         fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.startFollow()
        self.b_setSuitState(1, startIndex, endIndex)

    def startFollow(self):
        #self.b_setAnimState('walk')
        if self.walkTrack:
            self.acceptOnce(self.walkTrack.getName(), self.walkDone)
            self.walkTrack.start()

    def walkDone(self):
        if self.walkTrack:
            self.walkTrack.finish()
            self.walkTrack = None
        self.b_setAnimState('neutral')
        self.createPath()

    def toonHitByWeapon(self, weaponId, avId):
        sender = self.air.getMsgSender()
        weapon = SuitAttacks.SuitAttackLengths.keys()[weaponId]
        if not weapon in [
                "pickpocket", "fountainpen", "hangup", "buzzword",
                "razzledazzle", "jargon", "mumbojumbo", 'doubletalk',
                'schmooze', 'fingerwag', 'filibuster'
        ]:
            self.d_handleWeaponTouch()
        dmg = int(self.maxHealth / SuitAttacks.SuitAttackDamageFactors[weapon])
        toon = self.air.doId2do.get(avId, None)
        if toon:
            hp = toon.getHealth() - dmg
            if hp < 0:
                hp = 0
            toon.b_setHealth(hp)
            toon.d_announceHealth(0, dmg)
            if toon.isDead():
                self.b_setAnimState('win')
                taskMgr.remove(self.uniqueName('continueSuitRoam'))
                taskMgr.doMethodLater(6.0, self.continuePathTask,
                                      self.uniqueName('continueSuitRoam'))

    def turretHitByWeapon(weaponId, avId):
        weapon = SuitAttacks.SuitAttackLengths.keys()[weaponId]
        if not weapon in ["pickpocket", "fountainpen", "hangup"]:
            self.d_handleWeaponTouch()
        dmg = int(self.maxHealth / CIGlobals.SuitAttackDamageFactors[weapon])
        turret = self.air.doId2do.get(avId, None)
        if turret:
            turret.b_setHealth(turret.getHealth() - 1)
            turret.d_announceHealth(0, dmg)

    def setSuit(self, suitType, head, team, skeleton):
        self.type = suitType
        self.head = head
        self.team = team
        self.skeleton = skeleton
        self.health = CIGlobals.getSuitHP(self.level)
        self.maxHealth = self.health
        self.itemDropper.calculate()

    def b_setSuit(self, suitType, head, team, skeleton):
        self.d_setSuit(suitType, head, team, skeleton)
        self.setSuit(suitType, head, team, skeleton)

    def d_setSuit(self, suitType, head, team, skeleton):
        self.sendUpdate("setSuit", [suitType, head, team, skeleton])

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

    def setAnimState(self, anim):
        self.anim = anim

    def b_setAnimState(self, anim):
        if type(anim) == types.StringType:
            anim = self.animState2animId[anim]
        self.d_setAnimState(anim)
        self.setAnimState(anim)

    def d_setAnimState(self, anim):
        timestamp = globalClockDelta.getFrameNetworkTime()
        self.sendUpdate("setAnimState", [anim, timestamp])

    def getAnimState(self):
        return self.anim

    def getAnimStateStr(self):
        return self.animId2animState[self.getAnimState()]

    def d_interruptAttack(self):
        self.sendUpdate("interruptAttack", [])

    def d_setAttack(self, attack):
        self.sendUpdate("setAttack", [attack])

    def announceGenerate(self):
        DistributedAvatarAI.announceGenerate(self)
        if self.track:
            self.track.pause()
            self.track = None
        Sequence(Wait(0.1), Func(self.spawn)).start()

    def generate(self):
        DistributedAvatarAI.generate(self)
        DistributedSmoothNodeAI.generate(self)

    def disable(self):
        try:
            self.DistributedSuitAI_disabled
        except:
            self.DistributedSuitAI_disabled = 1
            if self.track:
                self.track.pause()
                self.track = None
            taskMgr.remove(self.uniqueName('monitorHealth'))
            taskMgr.remove(self.uniqueName('attackTask'))
            taskMgr.remove(self.uniqueName('continueSuitRoam'))
            if self.suitHealTrack:
                self.suitHealTrack.pause()
                self.suitHealTrack = None
            if self.walkTrack:
                self.ignore(self.walkTrack.getName())
                self.walkTrack.clearToInitial()
                self.walkTrack = None
            if self.boss:
                self.boss.stopBoss()
                self.boss = None
            if self.brain:
                self.brain.end()
                self.brain = None
            self.itemDropper.cleanup()
            self.itemDropper = None
            self.aiChar = None
            self.aiBehaviors = None
            self.continuePathId = None
            self.attackId = None
            self.name = None
            self.anim = None
            self.state = None
            self.damage = None
            self.health = None
            self.backup = None
            self.type = None
            self.team = None
            self.head = None
            self.skeleton = 0
            self.dmg_lbl = None
            self.currentPath = None
            self.lbl_int = None
            self.bean = None
            self.avatarType = None
            self.lateX = None
            self.lateY = None
            self.currentPathQueue = None
            DistributedAvatarAI.disable(self)
        return

    def delete(self):
        try:
            self.DistributedSuitAI_deleted
        except:
            self.DistributedSuitAI_deleted = 1
            del self.aiChar
            del self.brain
            del self.aiBehaviors
            del self.boss
            del self.continuePathId
            del self.attackId
            del self.name
            del self.anim
            del self.state
            del self.damage
            del self.health
            del self.type
            del self.team
            del self.head
            del self.skeleton
            del self.dmg_lbl
            del self.lbl_int
            del self.bean
            del self.currentPath
            del self.avatarType
            del self.walkTrack
            del self.suitHealTrack
            del self.backup
            del self.lateX
            del self.lateY
            del self.currentPathQueue
            DistributedAvatarAI.delete(self)
            DistributedSmoothNodeAI.delete(self)
        return
class DistributedEagleSuitAI(DistributedSuitAI):
    notify = directNotify.newCategory("DistributedEagleSuitAI")

    def __init__(self, air):
        DistributedSuitAI.__init__(self, air)
        self.mg = None
        self.flyTrack = None
        self.currentFlyPoint = None
        self.flySpeed = 0.0

    def setMinigame(self, mg):
        self.mg = mg

    def getMinigame(self):
        return self.mg

    def handleGotHit(self):
        self.b_setAnimState('flail')
        if self.flyTrack:
            self.ignore(self.flyTrack.getDoneEvent())
            self.flyTrack.pause()
            self.flyTrack = None
        self.sendUpdate('fallAndExplode', [])
        self.b_setSuitState(6, -1, -1)
        self.flyTrack = Sequence(
            LerpPosInterval(
                self,
                duration = 4.0,
                pos = self.getPos(render) - (0, 0, 75),
                startPos = self.getPos(render),
                blendType = 'easeIn'
            ),
            Wait(1.5),
            Func(self.killMe)
        )
        self.flyTrack.start()

    def killMe(self):
        self.disable()
        self.requestDelete()

    def setFlySpeed(self, value):
        self.flySpeed = value

    def b_setFlySpeed(self, value):
        self.sendUpdate('setFlySpeed', [value])
        self.setFlySpeed(value)

    def getFlySpeed(self):
        return self.flySpeed

    def spawn(self):
        # spawn() also exists in DistributedSuitAI, but we're not doing
        # anything that a normal suit would do here, so don't even call
        # DistributedSuitAI.spawn.

        if not self.getMinigame():
            self.notify.error("Tried to spawn before self.mg was set!")
        
        self.b_setAnimState('flyNeutral')
        point = random.choice(EGG.EAGLE_FLY_POINTS)
        self.setPos(point)
        self.d_setPos(*point)

        self.b_setFlySpeed(EGG.ROUND_2_EAGLE_SPEED[self.getMinigame().getRound()])

        self.createFlyPath()
        self.b_setParent(CIGlobals.SPRender)

    def createFlyPath(self):
        self.b_setAnimState('flyNeutral')
        if self.flyTrack:
            self.ignore(self.flyTrack.getDoneEvent())
            self.flyTrack.pause()
            self.flyTrack = None
        point = random.choice(EGG.EAGLE_FLY_POINTS)
        if self.currentFlyPoint == point:
            self.createFlyPath()
            return
        if self.currentFlyPoint == None:
            point_list = list(EGG.EAGLE_FLY_POINTS)
            point_list.remove(point)
            startIndex = point_list.index(random.choice(point_list))
        else:
            startIndex = -1
        self.b_setSuitState(5, startIndex, EGG.EAGLE_FLY_POINTS.index(point))
        mgRound = self.getMinigame().getRound()
        if mgRound:
            self.flyTrack = NPCWalkInterval(self, point,
                durationFactor = EGG.ROUND_2_EAGLE_SPEED[mgRound],
                startPos = self.getPos(render), fluid = 1, name = self.uniqueName('DEagleSuitAI-flyTrack'))
            self.flyTrack.setDoneEvent(self.flyTrack.getName())
            self.acceptOnce(self.flyTrack.getDoneEvent(), self.handleFlyDone)
            self.flyTrack.start()
            self.currentFlyPoint = point
        else: return

    def handleFlyDone(self):
        self.createFlyPath()

    def delete(self):
        del self.currentFlyPoint
        del self.mg
        if self.flyTrack:
            self.ignore(self.flyTrack.getDoneEvent())
            self.flyTrack.pause()
            self.flyTrack = None
        DistributedSuitAI.delete(self)
class DistributedDeliveryGameSuitAI(DistributedSuitAI):
    notify = directNotify.newCategory('DistributedDeliveryGameSuitAI')

    def __init__(self, air, mg):
        DistributedSuitAI.__init__(self, air)
        self.mg = mg
        self.truck = random.choice(self.mg.trucks)
        self.truckIndex = self.mg.trucks.index(self.truck)
        self.spawnPoint = None

    def walkToTruck(self):
        index = DGG.WalkToTruckIndex
        pos = DGG.TruckSuitPointsByIndex[self.truckIndex]
        startPos = self.getPos(render)
        self.b_setSuitState(1, -1, index)
        durationFactor = 0.2
        pathName = self.uniqueName('WalkToTruck')
        self.walkTrack = NPCWalkInterval(self,
                                         pos,
                                         startPos=startPos,
                                         name=pathName,
                                         durationFactor=durationFactor,
                                         fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.acceptOnce(self.walkTrack.getDoneEvent(), self.__walkedToTruck)
        self.walkTrack.start()
        self.b_setAnimState(
            SuitGlobals.getAnimId(SuitGlobals.getAnimByName('walk')))

    def __walkedToTruck(self):
        self.truck.suitPickUpBarrel(self.doId)
        self.walkBackToSpawnPointWithBarrel()

    def walkBackToSpawnPointWithBarrel(self):
        pos = DGG.SpawnPoints[self.spawnPoint]
        startPos = self.getPos(render)
        self.b_setSuitState(1, -1, self.spawnPoint)
        durationFactor = 0.2
        pathName = self.uniqueName('WalkBackToSpawn')
        self.walkTrack = NPCWalkInterval(self,
                                         pos,
                                         startPos=startPos,
                                         name=pathName,
                                         durationFactor=durationFactor,
                                         fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.acceptOnce(self.walkTrack.getDoneEvent(), self.__walkedBack2Spawn)
        self.walkTrack.start()
        self.b_setAnimState(
            SuitGlobals.getAnimId(SuitGlobals.getAnimByName('tray-walk')))

    def __walkedBack2Spawn(self):
        self.b_setSuitState(3, self.spawnPoint, self.spawnPoint)
        base.taskMgr.doMethodLater(10, self.__finished,
                                   self.uniqueName('finishSuit'))

    def __finished(self, task):
        self.mg.suits.remove(self)
        self.truck.barrelDroppedOff()
        self.requestDelete()
        return task.done

    def spawn(self):
        pos = random.choice(DGG.SpawnPoints)
        index = DGG.SpawnPoints.index(pos)
        self.spawnPoint = index
        self.b_setSuitState(2, index, index)
        flyTrack = self.posInterval(3, pos, startPos=pos + (0, 0, 50))
        flyTrack.start()
        self.track = Sequence()
        self.track.append(Wait(5.4))
        self.track.append(Func(self.b_setAnimState, 'neutral'))
        self.track.append(Wait(1.0))
        self.track.append(Func(self.walkToTruck))
        self.track.start()
        self.b_setParent(CIGlobals.SPRender)

    def delete(self):
        base.taskMgr.remove(self.uniqueName('finishSuit'))
        if hasattr(self, 'walkTrack') and self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.finish()
            self.walkTrack = None
        self.mg = None
        self.truck = None
        self.truckIndex = None
        self.spawnPoint = None
        DistributedSuitAI.delete(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
    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)
Esempio n. 12
0
class DistributedTutorialSuitAI(DistributedSuitAI):
    notify = directNotify.newCategory('DistributedTutorialSuitAI')
    ATTACK_IVAL_RANGE = [3, 15]

    def __init__(self, air, index, tut, avatarId):
        DistributedSuitAI.__init__(self, air)
        self.tutPartIndex = index
        self.tutorial = tut
        self.avatarId = avatarId
        self.currentPath = None
        self.walkTrack = None
        return

    def delete(self):
        base.taskMgr.remove(self.uniqueName('monitorHealth'))
        base.taskMgr.remove(self.uniqueName('doAttack'))
        base.taskMgr.remove(self.uniqueName('scwaa'))
        self.stopAttacks()
        if self.track:
            self.track.pause()
            self.track = None
        if self.walkTrack:
            self.walkTrack.pause()
            self.walkTrack = None
        if self.currentPath:
            self.currentPath = None
        self.tutorial = None
        self.tutPartIndex = None
        self.avatarId = None
        DistributedSuitAI.delete(self)
        return

    def spawn(self):
        pos = TutorialGlobals.SUIT_POINTS[TutorialGlobals.SUIT_SPAWN_POINT]
        index = TutorialGlobals.SUIT_POINTS.index(pos)
        self.spawnPoint = index
        self.b_setSuitState(2, index, index)
        flyTrack = self.posInterval(3, pos, startPos=pos + (0, 0, 50))
        flyTrack.start()
        self.track = Sequence()
        self.track.append(Wait(5.4))
        self.track.append(Func(self.b_setAnimState, 'neutral'))
        self.track.append(Wait(1.0))
        self.track.append(Func(self.startAI))
        self.track.start()
        self.b_setParent(CIGlobals.SPRender)
        taskMgr.add(self.monitorHealth, self.uniqueName('monitorHealth'))

    def createPath(self, fromCurPos = False):
        durationFactor = 0.2
        if not hasattr(self, 'currentPath'):
            self.currentPath = None
        if self.currentPath == None:
            path = random.choice(TutorialGlobals.SUIT_POINTS)
            self.currentPath = TutorialGlobals.SUIT_POINTS.index(path)
            startIndex = -1
        else:
            if fromCurPos == False:
                startIndex = int(self.currentPath)
            else:
                startIndex = -1
            self.currentPath += 1
            if self.currentPath >= len(TutorialGlobals.SUIT_POINTS):
                self.currentPath = 0
            path = TutorialGlobals.SUIT_POINTS[self.currentPath]
        endIndex = self.currentPath
        startPos = self.getPos(render)
        pathName = self.uniqueName('suitPath')
        self.walkTrack = NPCWalkInterval(self, path, startPos=startPos, name=pathName, durationFactor=durationFactor, fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.acceptOnce(self.walkTrack.getDoneEvent(), self.createPath)
        self.walkTrack.start()
        self.b_setAnimState('walk')
        self.b_setSuitState(1, startIndex, endIndex)
        return

    def monitorHealth(self, task):
        if self.health <= 0:
            self.tutorial.sendUpdateToAvatarId(self.avatarId, 'suitNoHealth', [self.tutPartIndex])
            if self.walkTrack:
                self.ignore(self.walkTrack.getDoneEvent())
                self.walkTrack.pause()
                self.walkTrack = None
            self.b_setSuitState(0, -1, -1)
            currentAnim = SuitGlobals.getAnimByName(self.anim)
            self.clearTrack()
            base.taskMgr.remove(self.uniqueName('scwaa'))
            self.stopAttacks()
            if currentAnim:
                self.track = Sequence(Wait(currentAnim.getDeathHoldTime()), Func(self.killSuit))
                self.track.start()
            else:
                self.killSuit()
            return Task.done
        else:
            return Task.cont

    def setSuit(self, plan, variant = 0):
        DistributedSuitAI.setSuit(self, plan, variant, self.tutorial)

    def closeSuit(self):
        DistributedSuitAI.closeSuit(self)
        self.tutorial.sendUpdateToAvatarId(self.avatarId, 'suitExploded', [self.tutPartIndex])

    def startAttacks(self):
        base.taskMgr.doMethodLater(random.randint(*self.ATTACK_IVAL_RANGE), self.__doAttack, self.uniqueName('doAttack'))

    def __doAttack(self, task):
        base.taskMgr.remove(self.uniqueName('scwaa'))
        target = self.air.doId2do.get(self.avatarId)
        if not target:
            return task.done
        self.clearTrack()
        self.b_setSuitState(0, -1, -1)
        self.b_setAnimState('neutral')
        self.headsUp(target)
        attack = random.choice(self.suitPlan.getAttacks())
        attackIndex = SuitAttacks.SuitAttackLengths.keys().index(attack)
        attackTaunt = random.randint(0, len(CIGlobals.SuitAttackTaunts[attack]) - 1)
        timestamp = globalClockDelta.getFrameNetworkTime()
        if self.isDead():
            self.stopAttacks()
            return task.done
        self.sendUpdate('doAttack', [attackIndex, target.doId, timestamp])
        self.d_setChat(CIGlobals.SuitAttackTaunts[attack][attackTaunt])
        attackLength = SuitAttacks.SuitAttackLengths[attack]
        base.taskMgr.doMethodLater(attackLength, self.__suitContinueWalkAfterAttack, self.uniqueName('scwaa'))
        task.delayTime = random.randint(*self.ATTACK_IVAL_RANGE)
        return task.again

    def __suitContinueWalkAfterAttack(self, task):
        self.createPath(fromCurPos=True)
        return task.done

    def stopAttacks(self):
        base.taskMgr.remove(self.uniqueName('doAttack'))

    def startAI(self):
        if self.tutPartIndex == 0:
            self.b_setAnimState('neutral')
        elif self.tutPartIndex == 1:
            self.createPath()
        elif self.tutPartIndex == 2:
            self.createPath()
            self.startAttacks()
Esempio n. 13
0
class SuitPathBehavior(SuitBehaviorBase):

    def __init__(self, suit, exitOnWalkFinish = True):
        SuitBehaviorBase.__init__(self, suit)
        self.walkTrack = None
        self.exitOnWalkFinish = exitOnWalkFinish
        self.isEntered = 0
        return

    def unload(self):
        SuitBehaviorBase.unload(self)
        self.clearWalkTrack()
        del self.exitOnWalkFinish
        del self.walkTrack

    def createPath(self, pathKey = None, durationFactor = 0.2, fromCurPos = False):
        currentPathQueue = self.suit.getCurrentPathQueue()
        currentPath = self.suit.getCurrentPath()
        if pathKey == None:
            pathKeyList = CIGlobals.SuitPathData[self.suit.getHood()][self.suit.getCurrentPath()]
            pathKey = random.choice(pathKeyList)
        elif len(currentPathQueue):
            pathKey = currentPathQueue[0]
            currentPathQueue.remove(pathKey)
        endIndex = CIGlobals.SuitSpawnPoints[self.suit.getHood()].keys().index(pathKey)
        path = CIGlobals.SuitSpawnPoints[self.suit.getHood()][pathKey]
        self.clearWalkTrack()
        if not currentPath or fromCurPos:
            startIndex = -1
        else:
            oldPath = currentPath
            startIndex = CIGlobals.SuitSpawnPoints[self.suit.getHood()].keys().index(oldPath)
        self.suit.currentPath = pathKey
        startPos = self.suit.getPos(render)
        pathName = self.suit.uniqueName('suitPath')
        self.walkTrack = NPCWalkInterval(self.suit, path, startPos=startPos, name=pathName, durationFactor=durationFactor, fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.startFollow()
        self.suit.b_setSuitState(1, startIndex, endIndex)
        return

    def getDistance(self, point1, point2):
        return (point1.getXy() - point2.getXy()).length()

    def getPath(self, start_key, target_key, nodes):
        path = []
        for node in nodes:
            if node.key == start_key:
                start_node = node
            elif node.key == target_key:
                target_node = node

        current_node = target_node
        while current_node.parent != start_node:
            path.append(current_node.key)
            current_node = current_node.parent

        path.append(start_key)
        return list(reversed(path))

    def findPath(self, area, start_key, target_key):
        start_point = CIGlobals.SuitSpawnPoints[area][start_key]
        target_point = CIGlobals.SuitSpawnPoints[area][target_key]
        nodes = []
        open_nodes = []
        closed_nodes = []
        for key, point in CIGlobals.SuitSpawnPoints[area].items():
            g_cost = self.getDistance(point, start_point)
            h_cost = self.getDistance(point, target_point)
            node = Node(g_cost, h_cost, key, point)
            nodes.append(node)

        for node in nodes:
            if node.key == start_key:
                open_nodes.append(node)

        while len(open_nodes):
            f_cost_list = []
            for node in open_nodes:
                f_cost_list.append(node.f_cost)

            lowest_f_cost = min(f_cost_list)
            current = None
            for node in open_nodes:
                if lowest_f_cost == node.f_cost:
                    current = node

            open_nodes.remove(current)
            closed_nodes.append(current)
            if current.key == target_key:
                return self.getPath(start_key, target_key, nodes)
            neighbor_keys = CIGlobals.SuitPathData[area][current.key]
            for neighbor_key in neighbor_keys:
                isClosed = False
                for node in closed_nodes:
                    if node.key == neighbor_key:
                        isClosed = True
                        break

                if isClosed:
                    continue
                neighbor = None
                for node in nodes:
                    if node.key == neighbor_key:
                        neighbor = node
                        break

                nm_cost_2_neighbor = current.g_cost + self.getDistance(current.point, neighbor.point)
                if neighbor not in open_nodes or nm_cost_2_neighbor < neighbor.g_cost:
                    neighbor.g_cost = nm_cost_2_neighbor
                    neighbor.h_cost = self.getDistance(target_point, neighbor.point)
                    neighbor.f_cost = neighbor.g_cost + neighbor.h_cost
                    neighbor.parent = current
                    if neighbor not in open_nodes:
                        open_nodes.append(neighbor)

        return

    def clearWalkTrack(self):
        if self.walkTrack:
            self.ignore(self.walkTrack.getDoneEvent())
            self.walkTrack.clearToInitial()
            self.walkTrack = None
            if hasattr(self, 'suit'):
                self.suit.d_stopMoveInterval()
        return

    def startFollow(self):
        self.suit.b_setAnimState('walk')
        if self.walkTrack:
            self.acceptOnce(self.walkTrack.getName(), self.__walkDone)
            self.walkTrack.start()

    def __walkDone(self):
        self.clearWalkTrack()
        if not self.suit.isDead():
            self.suit.b_setAnimState('neutral')
            if self.exitOnWalkFinish == True:
                self.exit()

    def getWalkTrack(self):
        return self.walkTrack

    def isWalking(self):
        if self.walkTrack:
            return self.walkTrack.isPlaying()
        return False
class DistributedTutorialSuitAI(DistributedSuitAI):
    notify = directNotify.newCategory('DistributedTutorialSuitAI')

    ATTACK_IVAL_RANGE = [3, 15]

    def __init__(self, air, index, tut, avatarId):
        DistributedSuitAI.__init__(self, air)
        self.tutPartIndex = index
        self.tutorial = tut
        self.avatarId = avatarId
        self.currentPath = None
        self.walkTrack = None

    def delete(self):
        base.taskMgr.remove(self.uniqueName('monitorHealth'))
        base.taskMgr.remove(self.uniqueName('doAttack'))
        base.taskMgr.remove(self.uniqueName('scwaa'))
        self.stopAttacks()
        if self.track:
            self.track.pause()
            self.track = None
        if self.walkTrack:
            self.walkTrack.pause()
            self.walkTrack = None
        if self.currentPath:
            self.currentPath = None
        self.tutorial = None
        self.tutPartIndex = None
        self.avatarId = None
        DistributedSuitAI.delete(self)

    def spawn(self):
        pos = TutorialGlobals.SUIT_POINTS[TutorialGlobals.SUIT_SPAWN_POINT]
        index = TutorialGlobals.SUIT_POINTS.index(pos)
        self.spawnPoint = index
        self.b_setSuitState(2, index, index)
        flyTrack = self.posInterval(3, pos, startPos=pos + (0, 0, 50))
        flyTrack.start()
        self.track = Sequence()
        self.track.append(Wait(6.5))
        self.track.append(Func(self.b_setAnimState, 'neutral'))
        self.track.append(Wait(1.0))
        self.track.append(Func(self.startAI))
        self.track.start()
        self.b_setParent(CIGlobals.SPRender)
        taskMgr.add(self.monitorHealth, self.uniqueName('monitorHealth'))

    def createPath(self, fromCurPos=False):
        durationFactor = 0.2
        if not hasattr(self, 'currentPath'):
            self.currentPath = None
        if self.currentPath == None:
            path = random.choice(TutorialGlobals.SUIT_POINTS)
            self.currentPath = TutorialGlobals.SUIT_POINTS.index(path)
            startIndex = -1
        else:
            if fromCurPos == False:
                startIndex = int(self.currentPath)
            else:
                startIndex = -1
            self.currentPath += 1
            if self.currentPath >= len(TutorialGlobals.SUIT_POINTS):
                self.currentPath = 0
            path = TutorialGlobals.SUIT_POINTS[self.currentPath]
        endIndex = self.currentPath
        startPos = self.getPos(render)
        pathName = self.uniqueName('suitPath')
        self.walkTrack = NPCWalkInterval(self,
                                         path,
                                         startPos=startPos,
                                         name=pathName,
                                         durationFactor=durationFactor,
                                         fluid=1)
        self.walkTrack.setDoneEvent(self.walkTrack.getName())
        self.acceptOnce(self.walkTrack.getDoneEvent(), self.createPath)
        self.walkTrack.start()
        self.b_setAnimState('walk')
        self.b_setSuitState(1, startIndex, endIndex)

    def monitorHealth(self, task):
        if self.health <= 0:
            self.tutorial.sendUpdateToAvatarId(self.avatarId, 'suitNoHealth',
                                               [self.tutPartIndex])
            if self.walkTrack:
                self.ignore(self.walkTrack.getDoneEvent())
                self.walkTrack.pause()
                self.walkTrack = None
            self.b_setSuitState(0, -1, -1)
            currentAnim = SuitGlobals.getAnimByName(self.anim)
            self.clearTrack()
            base.taskMgr.remove(self.uniqueName('scwaa'))
            self.stopAttacks()
            if currentAnim:
                self.track = Sequence(Wait(currentAnim.getDeathHoldTime()),
                                      Func(self.killSuit))
                self.track.start()
            else:
                self.killSuit()
            return Task.done
        return Task.cont

    def setSuit(self, plan, variant=0):
        DistributedSuitAI.setSuit(self, plan, variant, self.tutorial)

    def closeSuit(self):
        DistributedSuitAI.closeSuit(self)
        self.tutorial.sendUpdateToAvatarId(self.avatarId, 'suitExploded',
                                           [self.tutPartIndex])

    def startAttacks(self):
        base.taskMgr.doMethodLater(random.randint(*self.ATTACK_IVAL_RANGE),
                                   self.__doAttack,
                                   self.uniqueName('doAttack'))

    def __doAttack(self, task):
        base.taskMgr.remove(self.uniqueName('scwaa'))
        target = self.air.doId2do.get(self.avatarId)
        if not target:
            return task.done
        self.clearTrack()
        self.b_setSuitState(0, -1, -1)
        self.b_setAnimState('neutral')
        self.headsUp(target)
        # Choose a random attack and start it.
        attack = random.choice(self.suitPlan.getAttacks())
        attackIndex = SuitAttacks.SuitAttackLengths.keys().index(attack)
        attackTaunt = random.randint(
            0,
            len(CIGlobals.SuitAttackTaunts[attack]) - 1)
        timestamp = globalClockDelta.getFrameNetworkTime()
        if self.isDead():
            self.stopAttacks()
            return task.done
        self.sendUpdate('doAttack', [attackIndex, target.doId, timestamp])
        self.d_setChat(CIGlobals.SuitAttackTaunts[attack][attackTaunt])
        attackLength = SuitAttacks.SuitAttackLengths[attack]
        base.taskMgr.doMethodLater(attackLength,
                                   self.__suitContinueWalkAfterAttack,
                                   self.uniqueName('scwaa'))

        task.delayTime = random.randint(*self.ATTACK_IVAL_RANGE)
        return task.again

    def __suitContinueWalkAfterAttack(self, task):
        self.createPath(fromCurPos=True)
        return task.done

    def stopAttacks(self):
        base.taskMgr.remove(self.uniqueName('doAttack'))

    def startAI(self):
        if self.tutPartIndex == 0:
            # This is part one, all we do is stand still and wait to die.
            self.b_setAnimState('neutral')
        elif self.tutPartIndex == 1:
            # This is part two, we just walk around and wait to die.
            self.createPath()
        elif self.tutPartIndex == 2:
            # This is part three, we walk around, attack a bit, and wait to die.
            self.createPath()
            self.startAttacks()