class DistributedButterfly(DistributedObject.DistributedObject):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedButterfly')
    id = 0
    wingTypes = ('wings_1', 'wings_2', 'wings_3', 'wings_4', 'wings_5', 'wings_6')
    yellowColors = (Vec4(1, 1, 1, 1), Vec4(0.2, 0, 1, 1), Vec4(0.8, 0, 1, 1))
    whiteColors = (Vec4(0.8, 0, 0.8, 1),
     Vec4(0, 0.8, 0.8, 1),
     Vec4(0.9, 0.4, 0.6, 1),
     Vec4(0.9, 0.4, 0.4, 1),
     Vec4(0.8, 0.5, 0.9, 1),
     Vec4(0.4, 0.1, 0.7, 1))
    paleYellowColors = (Vec4(0.8, 0, 0.8, 1),
     Vec4(0.6, 0.6, 0.9, 1),
     Vec4(0.7, 0.6, 0.9, 1),
     Vec4(0.8, 0.6, 0.9, 1),
     Vec4(0.9, 0.6, 0.9, 1),
     Vec4(1, 0.6, 0.9, 1))
    shadowScaleBig = Point3(0.07, 0.07, 0.07)
    shadowScaleSmall = Point3(0.01, 0.01, 0.01)

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM.ClassicFSM('DistributedButterfly', [State.State('off', self.enterOff, self.exitOff, ['Flying', 'Landed']), State.State('Flying', self.enterFlying, self.exitFlying, ['Landed']), State.State('Landed', self.enterLanded, self.exitLanded, ['Flying'])], 'off', 'off')
        self.butterfly = None
        self.butterflyNode = None
        self.curIndex = 0
        self.destIndex = 0
        self.time = 0.0
        self.ival = None
        self.fsm.enterInitialState()
        return

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        if self.butterfly:
            return
        self.butterfly = Actor.Actor()
        self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod')
        self.butterfly.loadAnims({'flutter': 'phase_4/models/props/SZ_butterfly-flutter',
         'glide': 'phase_4/models/props/SZ_butterfly-glide',
         'land': 'phase_4/models/props/SZ_butterfly-land'})
        index = self.doId % len(self.wingTypes)
        chosenType = self.wingTypes[index]
        node = self.butterfly.getGeomNode()
        for type in self.wingTypes:
            wing = node.find('**/' + type)
            if type != chosenType:
                wing.removeNode()
            else:
                if index == 0 or index == 1:
                    color = self.yellowColors[self.doId % len(self.yellowColors)]
                elif index == 2 or index == 3:
                    color = self.whiteColors[self.doId % len(self.whiteColors)]
                elif index == 4:
                    color = self.paleYellowColors[self.doId % len(self.paleYellowColors)]
                else:
                    color = Vec4(1, 1, 1, 1)
                wing.setColor(color)

        self.butterfly2 = Actor.Actor(other=self.butterfly)
        self.butterfly.enableBlend(blendType=PartBundle.BTLinear)
        self.butterfly.loop('flutter')
        self.butterfly.loop('land')
        self.butterfly.loop('glide')
        rng = RandomNumGen.RandomNumGen(self.doId)
        playRate = 0.6 + 0.8 * rng.random()
        self.butterfly.setPlayRate(playRate, 'flutter')
        self.butterfly.setPlayRate(playRate, 'land')
        self.butterfly.setPlayRate(playRate, 'glide')
        self.butterfly2.setPlayRate(playRate, 'flutter')
        self.butterfly2.setPlayRate(playRate, 'land')
        self.butterfly2.setPlayRate(playRate, 'glide')
        self.glideWeight = rng.random() * 2
        lodNode = LODNode('butterfly-node')
        lodNode.addSwitch(100, 40)
        lodNode.addSwitch(40, 0)
        self.butterflyNode = NodePath(lodNode)
        self.butterfly2.setH(180.0)
        self.butterfly2.reparentTo(self.butterflyNode)
        self.butterfly.setH(180.0)
        self.butterfly.reparentTo(self.butterflyNode)
        self.__initCollisions()
        self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow')
        self.dropShadow.setColor(0, 0, 0, 0.3)
        self.dropShadow.setPos(0, 0.1, -0.05)
        self.dropShadow.setScale(self.shadowScaleBig)
        self.dropShadow.reparentTo(self.butterfly)

    def disable(self):
        self.butterflyNode.reparentTo(hidden)
        if self.ival != None:
            self.ival.finish()
        self.__ignoreAvatars()
        DistributedObject.DistributedObject.disable(self)
        return

    def delete(self):
        self.butterfly.cleanup()
        self.butterfly = None
        self.butterfly2.cleanup()
        self.butterfly2 = None
        self.butterflyNode.removeNode()
        self.__deleteCollisions()
        self.ival = None
        del self.fsm
        DistributedObject.DistributedObject.delete(self)
        return

    def uniqueButterflyName(self, name):
        DistributedButterfly.id += 1
        return name + '-%d' % DistributedButterfly.id

    def __detectAvatars(self):
        self.accept('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)

    def __ignoreAvatars(self):
        self.ignore('enter' + self.cSphereNode.getName())

    def __initCollisions(self):
        self.cSphere = CollisionSphere(0.0, 1.0, 0.0, 3.0)
        self.cSphere.setTangible(0)
        self.cSphereNode = CollisionNode(self.uniqueButterflyName('cSphereNode'))
        self.cSphereNode.addSolid(self.cSphere)
        self.cSphereNodePath = self.butterflyNode.attachNewNode(self.cSphereNode)
        self.cSphereNodePath.hide()
        self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def __deleteCollisions(self):
        del self.cSphere
        del self.cSphereNode
        self.cSphereNodePath.removeNode()
        del self.cSphereNodePath

    def __handleCollisionSphereEnter(self, collEntry):
        self.sendUpdate('avatarEnter', [])

    def setArea(self, playground, area):
        self.playground = playground
        self.area = area

    def setState(self, stateIndex, curIndex, destIndex, time, timestamp):
        self.curIndex = curIndex
        self.destIndex = destIndex
        self.time = time
        self.fsm.request(ButterflyGlobals.states[stateIndex], [globalClockDelta.localElapsedTime(timestamp)])

    def enterOff(self, ts = 0.0):
        if self.butterflyNode != None:
            self.butterflyNode.reparentTo(hidden)
        return

    def exitOff(self):
        if self.butterflyNode != None:
            self.butterflyNode.reparentTo(render)
        return

    def enterFlying(self, ts):
        self.__detectAvatars()
        curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.curIndex]
        destPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.destIndex]
        flyHeight = max(curPos[2], destPos[2]) + ButterflyGlobals.BUTTERFLY_HEIGHT[self.playground]
        curPosHigh = Point3(curPos[0], curPos[1], flyHeight)
        destPosHigh = Point3(destPos[0], destPos[1], flyHeight)
        if ts <= self.time:
            flyTime = self.time - (ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] + ButterflyGlobals.BUTTERFLY_LANDING[self.playground])
            self.butterflyNode.setPos(curPos)
            self.dropShadow.show()
            self.dropShadow.setScale(self.shadowScaleBig)
            oldHpr = self.butterflyNode.getHpr()
            self.butterflyNode.headsUp(destPos)
            newHpr = self.butterflyNode.getHpr()
            self.butterflyNode.setHpr(oldHpr)
            takeoffShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground]
            landShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_LANDING[self.playground]
            self.butterfly2.loop('flutter')
            self.ival = Sequence(Parallel(LerpPosHprInterval(self.butterflyNode, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], curPosHigh, newHpr), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], 'land', 'flutter'), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], None, 'glide', startWeight=0, endWeight=self.glideWeight), Sequence(LerpScaleInterval(self.dropShadow, takeoffShadowT, self.shadowScaleSmall, startScale=self.shadowScaleBig), HideInterval(self.dropShadow))), LerpPosInterval(self.butterflyNode, flyTime, destPosHigh), Parallel(LerpPosInterval(self.butterflyNode, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], destPos), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], 'flutter', 'land'), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], None, 'glide', startWeight=self.glideWeight, endWeight=0), Sequence(Wait(ButterflyGlobals.BUTTERFLY_LANDING[self.playground] - landShadowT), ShowInterval(self.dropShadow), LerpScaleInterval(self.dropShadow, landShadowT, self.shadowScaleBig, startScale=self.shadowScaleSmall))), name=self.uniqueName('Butterfly'))
            self.ival.start(ts)
        else:
            self.ival = None
            self.butterflyNode.setPos(destPos)
            self.butterfly.setControlEffect('land', 1.0)
            self.butterfly.setControlEffect('flutter', 0.0)
            self.butterfly.setControlEffect('glide', 0.0)
            self.butterfly2.loop('land')
        return

    def exitFlying(self):
        self.__ignoreAvatars()
        if self.ival != None:
            self.ival.finish()
            self.ival = None
        return

    def enterLanded(self, ts):
        self.__detectAvatars()
        curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.curIndex]
        self.butterflyNode.setPos(curPos)
        self.dropShadow.show()
        self.dropShadow.setScale(self.shadowScaleBig)
        self.butterfly.setControlEffect('land', 1.0)
        self.butterfly.setControlEffect('flutter', 0.0)
        self.butterfly.setControlEffect('glide', 0.0)
        self.butterfly2.pose('land', random.randrange(self.butterfly2.getNumFrames('land')))
        return None

    def exitLanded(self):
        self.__ignoreAvatars()
        return None
예제 #2
0
class DistributedButterfly(DistributedObject.DistributedObject):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedButterfly')
    id = 0
    wingTypes = ('wings_1', 'wings_2', 'wings_3', 'wings_4', 'wings_5',
                 'wings_6')
    yellowColors = (Vec4(1, 1, 1, 1), Vec4(0.2, 0, 1, 1), Vec4(0.8, 0, 1, 1))
    whiteColors = (Vec4(0.8, 0, 0.8, 1), Vec4(0, 0.8, 0.8,
                                              1), Vec4(0.9, 0.4, 0.6, 1),
                   Vec4(0.9, 0.4, 0.4, 1), Vec4(0.8, 0.5, 0.9,
                                                1), Vec4(0.4, 0.1, 0.7, 1))
    paleYellowColors = (Vec4(0.8, 0, 0.8, 1), Vec4(0.6, 0.6, 0.9,
                                                   1), Vec4(0.7, 0.6, 0.9, 1),
                        Vec4(0.8, 0.6, 0.9, 1), Vec4(0.9, 0.6, 0.9,
                                                     1), Vec4(1, 0.6, 0.9, 1))
    shadowScaleBig = Point3(0.07, 0.07, 0.07)
    shadowScaleSmall = Point3(0.01, 0.01, 0.01)

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM.ClassicFSM('DistributedButterfly', [
            State.State('off', self.enterOff, self.exitOff,
                        ['Flying', 'Landed']),
            State.State('Flying', self.enterFlying, self.exitFlying,
                        ['Landed']),
            State.State('Landed', self.enterLanded, self.exitLanded,
                        ['Flying'])
        ], 'off', 'off')
        self.butterfly = None
        self.butterflyNode = None
        self.curIndex = 0
        self.destIndex = 0
        self.time = 0.0
        self.ival = None
        self.fsm.enterInitialState()
        return

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        if self.butterfly:
            return
        self.butterfly = Actor.Actor()
        self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod')
        self.butterfly.loadAnims({
            'flutter':
            'phase_4/models/props/SZ_butterfly-flutter',
            'glide':
            'phase_4/models/props/SZ_butterfly-glide',
            'land':
            'phase_4/models/props/SZ_butterfly-land'
        })
        index = self.doId % len(self.wingTypes)
        chosenType = self.wingTypes[index]
        node = self.butterfly.getGeomNode()
        for type in self.wingTypes:
            wing = node.find('**/' + type)
            if type != chosenType:
                wing.removeNode()
            else:
                if index == 0 or index == 1:
                    color = self.yellowColors[self.doId %
                                              len(self.yellowColors)]
                elif index == 2 or index == 3:
                    color = self.whiteColors[self.doId % len(self.whiteColors)]
                elif index == 4:
                    color = self.paleYellowColors[self.doId %
                                                  len(self.paleYellowColors)]
                else:
                    color = Vec4(1, 1, 1, 1)
                wing.setColor(color)

        self.butterfly2 = Actor.Actor(other=self.butterfly)
        self.butterfly.enableBlend(blendType=PartBundle.BTLinear)
        self.butterfly.loop('flutter')
        self.butterfly.loop('land')
        self.butterfly.loop('glide')
        rng = RandomNumGen.RandomNumGen(self.doId)
        playRate = 0.6 + 0.8 * rng.random()
        self.butterfly.setPlayRate(playRate, 'flutter')
        self.butterfly.setPlayRate(playRate, 'land')
        self.butterfly.setPlayRate(playRate, 'glide')
        self.butterfly2.setPlayRate(playRate, 'flutter')
        self.butterfly2.setPlayRate(playRate, 'land')
        self.butterfly2.setPlayRate(playRate, 'glide')
        self.glideWeight = rng.random() * 2
        lodNode = LODNode('butterfly-node')
        lodNode.addSwitch(100, 40)
        lodNode.addSwitch(40, 0)
        self.butterflyNode = NodePath(lodNode)
        self.butterfly2.setH(180.0)
        self.butterfly2.reparentTo(self.butterflyNode)
        self.butterfly.setH(180.0)
        self.butterfly.reparentTo(self.butterflyNode)
        self.__initCollisions()
        self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow')
        self.dropShadow.setColor(0, 0, 0, 0.3)
        self.dropShadow.setPos(0, 0.1, -0.05)
        self.dropShadow.setScale(self.shadowScaleBig)
        self.dropShadow.reparentTo(self.butterfly)

    def disable(self):
        self.butterflyNode.reparentTo(hidden)
        if self.ival != None:
            self.ival.finish()
        self.__ignoreAvatars()
        DistributedObject.DistributedObject.disable(self)
        return

    def delete(self):
        self.butterfly.cleanup()
        self.butterfly = None
        self.butterfly2.cleanup()
        self.butterfly2 = None
        self.butterflyNode.removeNode()
        self.__deleteCollisions()
        self.ival = None
        del self.fsm
        DistributedObject.DistributedObject.delete(self)
        return

    def uniqueButterflyName(self, name):
        DistributedButterfly.id += 1
        return name + '-%d' % DistributedButterfly.id

    def __detectAvatars(self):
        self.accept('enter' + self.cSphereNode.getName(),
                    self.__handleCollisionSphereEnter)

    def __ignoreAvatars(self):
        self.ignore('enter' + self.cSphereNode.getName())

    def __initCollisions(self):
        self.cSphere = CollisionSphere(0.0, 1.0, 0.0, 3.0)
        self.cSphere.setTangible(0)
        self.cSphereNode = CollisionNode(
            self.uniqueButterflyName('cSphereNode'))
        self.cSphereNode.addSolid(self.cSphere)
        self.cSphereNodePath = self.butterflyNode.attachNewNode(
            self.cSphereNode)
        self.cSphereNodePath.hide()
        self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def __deleteCollisions(self):
        del self.cSphere
        del self.cSphereNode
        self.cSphereNodePath.removeNode()
        del self.cSphereNodePath

    def __handleCollisionSphereEnter(self, collEntry):
        self.sendUpdate('avatarEnter', [])

    def setArea(self, playground, area):
        self.playground = playground
        self.area = area

    def setState(self, stateIndex, curIndex, destIndex, time, timestamp):
        self.curIndex = curIndex
        self.destIndex = destIndex
        self.time = time
        self.fsm.request(ButterflyGlobals.states[stateIndex],
                         [globalClockDelta.localElapsedTime(timestamp)])

    def enterOff(self, ts=0.0):
        if self.butterflyNode != None:
            self.butterflyNode.reparentTo(hidden)
        return

    def exitOff(self):
        if self.butterflyNode != None:
            self.butterflyNode.reparentTo(render)
        return

    def enterFlying(self, ts):
        self.__detectAvatars()
        curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][
            self.curIndex]
        destPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][
            self.destIndex]
        flyHeight = max(
            curPos[2],
            destPos[2]) + ButterflyGlobals.BUTTERFLY_HEIGHT[self.playground]
        curPosHigh = Point3(curPos[0], curPos[1], flyHeight)
        destPosHigh = Point3(destPos[0], destPos[1], flyHeight)
        if ts <= self.time:
            flyTime = self.time - (
                ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] +
                ButterflyGlobals.BUTTERFLY_LANDING[self.playground])
            self.butterflyNode.setPos(curPos)
            self.dropShadow.show()
            self.dropShadow.setScale(self.shadowScaleBig)
            oldHpr = self.butterflyNode.getHpr()
            self.butterflyNode.headsUp(destPos)
            newHpr = self.butterflyNode.getHpr()
            self.butterflyNode.setHpr(oldHpr)
            takeoffShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_TAKEOFF[
                self.playground]
            landShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_LANDING[
                self.playground]
            self.butterfly2.loop('flutter')
            self.ival = Sequence(
                Parallel(
                    LerpPosHprInterval(
                        self.butterflyNode,
                        ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground],
                        curPosHigh, newHpr),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground],
                        'land', 'flutter'),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground],
                        None,
                        'glide',
                        startWeight=0,
                        endWeight=self.glideWeight),
                    Sequence(
                        LerpScaleInterval(self.dropShadow,
                                          takeoffShadowT,
                                          self.shadowScaleSmall,
                                          startScale=self.shadowScaleBig),
                        HideInterval(self.dropShadow))),
                LerpPosInterval(self.butterflyNode, flyTime, destPosHigh),
                Parallel(
                    LerpPosInterval(
                        self.butterflyNode,
                        ButterflyGlobals.BUTTERFLY_LANDING[self.playground],
                        destPos),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_LANDING[self.playground],
                        'flutter', 'land'),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_LANDING[self.playground],
                        None,
                        'glide',
                        startWeight=self.glideWeight,
                        endWeight=0),
                    Sequence(
                        Wait(ButterflyGlobals.BUTTERFLY_LANDING[
                            self.playground] - landShadowT),
                        ShowInterval(self.dropShadow),
                        LerpScaleInterval(self.dropShadow,
                                          landShadowT,
                                          self.shadowScaleBig,
                                          startScale=self.shadowScaleSmall))),
                name=self.uniqueName('Butterfly'))
            self.ival.start(ts)
        else:
            self.ival = None
            self.butterflyNode.setPos(destPos)
            self.butterfly.setControlEffect('land', 1.0)
            self.butterfly.setControlEffect('flutter', 0.0)
            self.butterfly.setControlEffect('glide', 0.0)
            self.butterfly2.loop('land')
        return

    def exitFlying(self):
        self.__ignoreAvatars()
        if self.ival != None:
            self.ival.finish()
            self.ival = None
        return

    def enterLanded(self, ts):
        self.__detectAvatars()
        curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][
            self.curIndex]
        self.butterflyNode.setPos(curPos)
        self.dropShadow.show()
        self.dropShadow.setScale(self.shadowScaleBig)
        self.butterfly.setControlEffect('land', 1.0)
        self.butterfly.setControlEffect('flutter', 0.0)
        self.butterfly.setControlEffect('glide', 0.0)
        self.butterfly2.pose(
            'land', random.randrange(self.butterfly2.getNumFrames('land')))
        return None

    def exitLanded(self):
        self.__ignoreAvatars()
        return None
예제 #3
0
class DistributedButterfly(DistributedObject.DistributedObject):

    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedButterfly')

    id = 0
    # wings_1 (solid yellow)
    # wings_2 (yellow w/ dots) (1, 1, 1), (0.2, 0, 1), (1, 0, 1), (0.8, 0, 1)
    # wings_3 (solid white) (0.8, 0, 0.8), (0, 0.8, 0.8), (0.9, 0.4, 0.6)
    #           (0.9, 0.4, 0.4), (0.8, 0.5, 0.9), (0.4, 0.1, 0.7)
    # wings_4 (white w/ dots)
    # wings_5 (pale yellow w/ lines) (0.8, 0, 0.8), (0.6, 0.6, 0.9)
    #           (0.7, 0.6, 0.9), (0.8, 0.6, 0.9), (0.9, 0.6, 0.9),
    #           (1, 0.6, 0.9)
    # wings_6 (blue & yellow)
    wingTypes = ('wings_1', 'wings_2', 'wings_3', 'wings_4', 'wings_5',
                 'wings_6')
    yellowColors = (Vec4(1, 1, 1, 1), Vec4(0.2, 0, 1, 1), Vec4(0.8, 0, 1, 1))
    whiteColors = (Vec4(0.8, 0, 0.8, 1), Vec4(0, 0.8, 0.8,
                                              1), Vec4(0.9, 0.4, 0.6, 1),
                   Vec4(0.9, 0.4, 0.4, 1), Vec4(0.8, 0.5, 0.9,
                                                1), Vec4(0.4, 0.1, 0.7, 1))
    paleYellowColors = (Vec4(0.8, 0, 0.8, 1), Vec4(0.6, 0.6, 0.9,
                                                   1), Vec4(0.7, 0.6, 0.9, 1),
                        Vec4(0.8, 0.6, 0.9, 1), Vec4(0.9, 0.6, 0.9,
                                                     1), Vec4(1, 0.6, 0.9, 1))
    shadowScaleBig = Point3(0.07, 0.07, 0.07)
    shadowScaleSmall = Point3(0.01, 0.01, 0.01)

    def __init__(self, cr):
        """__init__(cr)
        """
        DistributedObject.DistributedObject.__init__(self, cr)

        self.fsm = ClassicFSM.ClassicFSM(
            'DistributedButterfly',
            [
                State.State('off', self.enterOff, self.exitOff,
                            ['Flying', 'Landed']),
                State.State('Flying', self.enterFlying, self.exitFlying,
                            ['Landed']),
                State.State('Landed', self.enterLanded, self.exitLanded,
                            ['Flying'])
            ],
            # Initial State
            'off',
            # Final State
            'off',
        )
        self.butterfly = None
        self.butterflyNode = None
        self.curIndex = 0
        self.destIndex = 0
        self.time = 0.0
        self.ival = None
        self.fsm.enterInitialState()

    def generate(self):
        """generate(self)
        This method is called when the DistributedObject is reintroduced
        to the world, either for the first time or from the cache.
        """
        DistributedObject.DistributedObject.generate(self)
        if self.butterfly:
            return

        self.butterfly = Actor.Actor()
        self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod.bam')
        self.butterfly.loadAnims({
            'flutter':
            'phase_4/models/props/SZ_butterfly-flutter.bam',
            'glide':
            'phase_4/models/props/SZ_butterfly-glide.bam',
            'land':
            'phase_4/models/props/SZ_butterfly-land.bam'
        })

        # Randomly choose one of the butterfly wing patterns
        index = self.doId % len(self.wingTypes)
        chosenType = self.wingTypes[index]
        node = self.butterfly.getGeomNode()
        for type in self.wingTypes:
            wing = node.find('**/' + type)
            if (type != chosenType):
                wing.removeNode()
            else:
                # Choose an appropriate blend color
                if (index == 0 or index == 1):
                    color = self.yellowColors[self.doId %
                                              len(self.yellowColors)]
                elif (index == 2 or index == 3):
                    color = self.whiteColors[self.doId % len(self.whiteColors)]
                elif (index == 4):
                    color = self.paleYellowColors[self.doId %
                                                  len(self.paleYellowColors)]
                else:
                    color = Vec4(1, 1, 1, 1)
                wing.setColor(color)

        # Make another copy of the butterfly model so we can LOD the
        # blending.  Butterflies that are far away won't bother to
        # blend animations; nearby butterflies will use dynamic
        # blending to combine two or more animations at once on
        # playback for a nice fluttering and landing effect.
        self.butterfly2 = Actor.Actor(other=self.butterfly)

        # Allow the nearby butterfly to blend between its three
        # animations.  All animations will be playing all the time;
        # we'll control which one is visible by varying the control
        # effect.
        self.butterfly.enableBlend(blendType=PartBundle.BTLinear)
        self.butterfly.loop('flutter')
        self.butterfly.loop('land')
        self.butterfly.loop('glide')

        # Make a random play rate so all the butterflies will be
        # flapping at slightly different rates.  This doesn't affect
        # the rate at which the butterfly moves, just the rate at
        # which the animation plays on the butterfly.
        rng = RandomNumGen.RandomNumGen(self.doId)
        playRate = 0.6 + 0.8 * rng.random()
        self.butterfly.setPlayRate(playRate, 'flutter')
        self.butterfly.setPlayRate(playRate, 'land')
        self.butterfly.setPlayRate(playRate, 'glide')
        self.butterfly2.setPlayRate(playRate, 'flutter')
        self.butterfly2.setPlayRate(playRate, 'land')
        self.butterfly2.setPlayRate(playRate, 'glide')

        # Also, a random glide contribution ratio.  We'll blend a bit
        # of the glide animation in with the flutter animation to
        # dampen the effect of flutter.  The larger the number here,
        # the greater the dampening effect.  Some butterflies will be
        # more active than others.  (Except when seen from a long way
        # off, because of the LODNode, below.)
        self.glideWeight = rng.random() * 2

        lodNode = LODNode('butterfly-node')
        lodNode.addSwitch(100, 40)  # self.butterfly2
        lodNode.addSwitch(40, 0)  # self.butterfly

        self.butterflyNode = NodePath(lodNode)
        self.butterfly2.setH(180.0)
        self.butterfly2.reparentTo(self.butterflyNode)
        self.butterfly.setH(180.0)
        self.butterfly.reparentTo(self.butterflyNode)
        self.__initCollisions()

        # Set up the drop shadow
        self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow')
        self.dropShadow.setColor(0, 0, 0, 0.3)
        self.dropShadow.setPos(0, 0.1, -0.05)
        self.dropShadow.setScale(self.shadowScaleBig)
        self.dropShadow.reparentTo(self.butterfly)

    def disable(self):
        """disable(self)
        This method is called when the DistributedObject is removed from
        active duty and stored in a cache.
        """
        self.butterflyNode.reparentTo(hidden)
        if (self.ival != None):
            self.ival.finish()
        self.__ignoreAvatars()
        DistributedObject.DistributedObject.disable(self)

    def delete(self):
        """delete(self)
        This method is called when the DistributedObject is permanently
        removed from the world and deleted from the cache.
        """
        self.butterfly.cleanup()
        self.butterfly = None
        self.butterfly2.cleanup()
        self.butterfly2 = None
        self.butterflyNode.removeNode()
        self.__deleteCollisions()
        self.ival = None
        del self.fsm
        DistributedObject.DistributedObject.delete(self)

    def uniqueButterflyName(self, name):
        DistributedButterfly.id += 1
        return (name + '-%d' % DistributedButterfly.id)

    def __detectAvatars(self):
        self.accept('enter' + self.cSphereNode.getName(),
                    self.__handleCollisionSphereEnter)

    def __ignoreAvatars(self):
        self.ignore('enter' + self.cSphereNode.getName())

    def __initCollisions(self):
        self.cSphere = CollisionSphere(0., 1., 0., 3.)
        self.cSphere.setTangible(0)
        self.cSphereNode = CollisionNode(
            self.uniqueButterflyName('cSphereNode'))
        self.cSphereNode.addSolid(self.cSphere)
        self.cSphereNodePath = self.butterflyNode.attachNewNode(
            self.cSphereNode)
        self.cSphereNodePath.hide()
        self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def __deleteCollisions(self):
        del self.cSphere
        del self.cSphereNode
        self.cSphereNodePath.removeNode()
        del self.cSphereNodePath

    def __handleCollisionSphereEnter(self, collEntry):
        """ Response for a toon walking up to this NPC
        """
        assert (self.notify.debug("Entering collision sphere..."))
        # Tell the server
        self.sendUpdate('avatarEnter', [])

    def setArea(self, playground, area):
        self.playground = playground
        self.area = area

    def setState(self, stateIndex, curIndex, destIndex, time, timestamp):
        self.curIndex = curIndex
        self.destIndex = destIndex
        self.time = time
        self.fsm.request(ButterflyGlobals.states[stateIndex],
                         [globalClockDelta.localElapsedTime(timestamp)])

    ##### Off state #####

    def enterOff(self, ts=0.0):
        if (self.butterflyNode != None):
            self.butterflyNode.reparentTo(hidden)
        return None

    def exitOff(self):
        if (self.butterflyNode != None):
            self.butterflyNode.reparentTo(render)
        return None

    ##### Flying state #####

    def enterFlying(self, ts):
        self.__detectAvatars()
        curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][
            self.curIndex]
        destPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][
            self.destIndex]
        # We'll hit the ground if we go straight from curPos to destPos
        flyHeight = max(
            curPos[2],
            destPos[2]) + ButterflyGlobals.BUTTERFLY_HEIGHT[self.playground]
        curPosHigh = Point3(curPos[0], curPos[1], flyHeight)
        destPosHigh = Point3(destPos[0], destPos[1], flyHeight)

        if (ts <= self.time):
            flyTime = self.time - (
                ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] +
                ButterflyGlobals.BUTTERFLY_LANDING[self.playground])
            self.butterflyNode.setPos(curPos)
            self.dropShadow.show()
            self.dropShadow.setScale(self.shadowScaleBig)
            oldHpr = self.butterflyNode.getHpr()
            self.butterflyNode.headsUp(destPos)
            newHpr = self.butterflyNode.getHpr()
            self.butterflyNode.setHpr(oldHpr)
            takeoffShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_TAKEOFF[
                self.playground]
            landShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_LANDING[
                self.playground]
            self.butterfly2.loop('flutter')
            self.ival = Sequence(
                Parallel(
                    LerpPosHprInterval(
                        self.butterflyNode,
                        ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground],
                        curPosHigh, newHpr),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground],
                        'land', 'flutter'),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground],
                        None,
                        'glide',
                        startWeight=0,
                        endWeight=self.glideWeight),
                    Sequence(
                        LerpScaleInterval(self.dropShadow,
                                          takeoffShadowT,
                                          self.shadowScaleSmall,
                                          startScale=self.shadowScaleBig),
                        HideInterval(self.dropShadow)),
                ),
                LerpPosInterval(self.butterflyNode, flyTime, destPosHigh),
                Parallel(
                    LerpPosInterval(
                        self.butterflyNode,
                        ButterflyGlobals.BUTTERFLY_LANDING[self.playground],
                        destPos),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_LANDING[self.playground],
                        'flutter', 'land'),
                    LerpAnimInterval(
                        self.butterfly,
                        ButterflyGlobals.BUTTERFLY_LANDING[self.playground],
                        None,
                        'glide',
                        startWeight=self.glideWeight,
                        endWeight=0),
                    Sequence(
                        Wait(ButterflyGlobals.BUTTERFLY_LANDING[
                            self.playground] - landShadowT),
                        ShowInterval(self.dropShadow),
                        LerpScaleInterval(self.dropShadow,
                                          landShadowT,
                                          self.shadowScaleBig,
                                          startScale=self.shadowScaleSmall)),
                ),
                name=self.uniqueName("Butterfly"))
            self.ival.start(ts)
        else:
            self.ival = None
            self.butterflyNode.setPos(destPos)
            self.butterfly.setControlEffect('land', 1.0)
            self.butterfly.setControlEffect('flutter', 0.0)
            self.butterfly.setControlEffect('glide', 0.0)
            self.butterfly2.loop('land')
        return None

    def exitFlying(self):
        self.__ignoreAvatars()
        if (self.ival != None):
            self.ival.finish()
            self.ival = None
        return None

    ##### Landed state #####

    def enterLanded(self, ts):
        self.__detectAvatars()
        curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][
            self.curIndex]
        self.butterflyNode.setPos(curPos)
        self.dropShadow.show()
        self.dropShadow.setScale(self.shadowScaleBig)
        self.butterfly.setControlEffect('land', 1.0)
        self.butterfly.setControlEffect('flutter', 0.0)
        self.butterfly.setControlEffect('glide', 0.0)
        self.butterfly2.pose(
            'land', random.randrange(self.butterfly2.getNumFrames('land')))
        return None

    def exitLanded(self):
        self.__ignoreAvatars()
        return None