class CogdoFlyingPlayer(FSM):
    notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingPlayer')

    def __init__(self, toon):
        FSM.__init__(self, 'CogdoFlyingPlayer')
        self.toon = toon
        self.toon.reparentTo(render)
        self.legalEaglesTargeting = []
        self.activeBuffs = []
        self.initModels()
        self.initIntervals()
        self.netTimeSentToStartDeath = 0
        self.backpackState = -1
        self.lastBackpackState = -1
        self.lastPropellerSpinRate = Globals.Gameplay.NormalPropSpeed
        self.propellerSpinRate = Globals.Gameplay.NormalPropSpeed
        self.setFuelState(Globals.Gameplay.FuelStates.FuelNoPropeller)
        self.setOldFuelState(self.fuelState)
        CogdoFlyingPlayer.setBlades(self, Globals.Gameplay.FuelStates.FuelNoPropeller)
        self.setBackpackState(Globals.Gameplay.BackpackStates.Normal)

    def initModels(self):
        self.createPropeller()
        self.createRedTapeRing()

    def createPropeller(self):
        self.propellerSmoke = DustCloud.DustCloud(self.toon, wantSound=False)
        self.propellerSmoke.setBillboardPointEye()
        self.propellerSmoke.setBin('fixed', 5002)
        self.backpack = CogdoUtil.loadFlyingModel('propellerPack')
        self.backpack.setScale(1.3)
        self.backpack.setHpr(180.0, 0.0, 0.0)
        self.backpackInstances = []
        self.backpackTextureCard = CogdoUtil.loadFlyingModel('propellerPack_card')
        parts = self.toon.getTorsoParts()
        for part in parts:
            backpackInstance = part.attachNewNode('backpackInstance')
            animal = self.toon.style.getAnimal()
            bodyScale = ToontownGlobals.toonBodyScales[animal]
            backpackHeight = ToontownGlobals.torsoHeightDict[self.toon.style.torso] * bodyScale - 0.5
            backpackInstance.setPos(0.0, -0.325, backpackHeight)
            self.backpackInstances.append(backpackInstance)
            self.backpack.instanceTo(backpackInstance)

        self.propInstances = []
        self.propeller = CogdoUtil.loadFlyingModel('toonPropeller')
        for part in self.backpackInstances:
            propInstance = part.attachNewNode('propInstance')
            propInstance.setPos(0.0, -0.275, 0.0)
            propInstance.setHpr(0.0, 20.0, 0.0)
            propInstance.setScale(1.0, 1.0, 1.25)
            self.propInstances.append(propInstance)
            self.propeller.instanceTo(propInstance)

        self.blades = []
        self.activeBlades = []
        index = 1
        blade = self.propeller.find('**/propeller%d' % index)
        while not blade.isEmpty():
            self.blades.append(blade)
            index += 1
            blade = self.propeller.find('**/propeller%d' % index)

        for blade in self.blades:
            self.activeBlades.append(blade)

    def createRedTapeRing(self):
        self.redTapeRing = CogdoUtil.loadFlyingModel('redTapeRing')
        self.redTapeRing.setTwoSided(True)
        self.redTapeRing.reparentTo(self.toon)
        self.redTapeRing.hide()
        self.redTapeRing.setScale(1.25)
        self.redTapeRing.setZ(self.toon.getHeight() / 2.0)

    def initIntervals(self):
        self.baseSpinDuration = 1.0
        self.propellerSpinLerp = LerpFunctionInterval(self.propeller.setH, fromData=0.0, toData=360.0, duration=self.baseSpinDuration, name='%s.propellerSpinLerp-%s' % (self.__class__.__name__, self.toon.doId))
        singleBlinkTime = Globals.Gameplay.TargetedWarningSingleBlinkTime
        blinkTime = Globals.Gameplay.TargetedWarningBlinkTime
        self.blinkLoop = Sequence(Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Attacked), Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Targeted), name='%s.blinkLoop-%s' % (self.__class__.__name__, self.toon.doId))
        self.blinkWarningSeq = Sequence(Func(self.blinkLoop.loop), Wait(blinkTime), Func(self.blinkLoop.clearToInitial), name='%s.blinkWarningSeq-%s' % (self.__class__.__name__, self.toon.doId))
        dur = Globals.Gameplay.BackpackRefuelDuration
        self.refuelSeq = Sequence(Func(self.setPropellerSpinRate, Globals.Gameplay.RefuelPropSpeed), Wait(dur), Func(self.returnBackpackToLastStateFunc), name='%s.refuelSeq-%s' % (self.__class__.__name__, self.toon.doId))
        scale = self.redTapeRing.getScale()
        pulseTime = 1.0
        self.pulseBubbleSeq = Parallel(Sequence(LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale, toData=scale * 1.1, duration=pulseTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale * 1.1, toData=scale, duration=pulseTime / 2.0, blendType='easeInOut')), LerpHprInterval(self.redTapeRing, pulseTime, Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)), name='%s.pulseBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
        bouncePercent = 1.2
        scaleTime = 0.5
        scaleBounceTime = 0.25
        self.popUpBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleTime, scale * bouncePercent, startScale=0.0, blendType='easeInOut')
        self.popUpBubbleSeq = Sequence(Func(self.updateLerpStartScale, self.popUpBubbleLerp, self.redTapeRing), Func(self.redTapeRing.show), self.popUpBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.pulseBubbleSeq.loop), name='%s.popUpBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
        self.removeBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale * bouncePercent, startScale=scale, blendType='easeInOut')
        self.removeBubbleSeq = Sequence(Func(self.pulseBubbleSeq.clearToInitial), Func(self.updateLerpStartScale, self.removeBubbleLerp, self.redTapeRing), self.removeBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleTime, 0.0, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.redTapeRing.hide), name='%s.removeBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
        self.redTapeRing.setScale(0.0)
        self.deathInterval = Sequence(Parallel(LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0)), Func(self.toon.stash), name='%s.deathInterval-%s' % (self.__class__.__name__, self.toon.doId))
        self.spawnInterval = Sequence(Func(self.toon.stash), Func(self.resetToon), Wait(1.0), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), name='%s.spawnInterval-%s' % (self.__class__.__name__, self.toon.doId))
        singleBlinkTime = Globals.Gameplay.InvulSingleBlinkTime
        blinkTime = Globals.Gameplay.InvulBlinkTime
        invulBuffTime = Globals.Gameplay.InvulBuffTime
        self.blinkBubbleLoop = Sequence(LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=1.0, toData=0.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=0.0, toData=1.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), name='%s.blinkBubbleLoop-%s' % (self.__class__.__name__, self.toon.doId))
        self.blinkBubbleSeq = Sequence(Wait(invulBuffTime - blinkTime), Func(self.blinkBubbleLoop.loop), Wait(blinkTime), Func(self.blinkBubbleLoop.finish), name='%s.blinkBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))

    def returnBackpackToLastStateFunc(self):
        if self.backpackState == Globals.Gameplay.BackpackStates.Refuel:
            self.returnBackpackToLastState()

    def setPropellerSpinRateFunc(self):
        if self.propellerSpinRate == Globals.Gameplay.RefuelPropSpeed:
            self.setPropellerSpinRate(self.lastPropellerSpinRate)

    def returnBackpackToLastState(self):
        self.setBackpackState(self.lastBackpackState)

    def setBackpackState(self, state):
        if state == self.backpackState:
            return
        self.lastBackpackState = self.backpackState
        self.backpackState = state
        self.blinkWarningSeq.clearToInitial()
        self.refuelSeq.clearToInitial()
        self.blinkLoop.clearToInitial()
        if self.lastBackpackState == Globals.Gameplay.BackpackStates.Refuel:
            self.setPropellerSpinRateFunc()
        if state in Globals.Gameplay.BackpackStates:
            if state == Globals.Gameplay.BackpackStates.Normal:
                pass
            elif state == Globals.Gameplay.BackpackStates.Targeted:
                pass
            elif state == Globals.Gameplay.BackpackStates.Refuel:
                self.refuelSeq.start()
            elif state == Globals.Gameplay.BackpackStates.Attacked:
                self.blinkWarningSeq.start()
            self.setBackpackTexture(state)

    def setBackpackTexture(self, state):
        texName = Globals.Gameplay.BackpackState2TextureName[state]
        tex = self.backpackTextureCard.findTexture(texName)
        self.backpack.setTexture(tex, 1)

    def updateLerpStartScale(self, lerp, nodepath):
        lerp.setStartScale(nodepath.getScale())

    def handleEnterGatherable(self, gatherable, elapsedTime):
        if gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
            self.blinkBubbleSeq.clearToInitial()
            self.blinkBubbleSeq.start(elapsedTime)
            self.removeBubbleSeq.clearToInitial()
            self.popUpBubbleSeq.start()
            if gatherable.type not in self.activeBuffs:
                self.activeBuffs.append(gatherable.type)
        elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
            self.setBackpackState(Globals.Gameplay.BackpackStates.Refuel)

    def handleDebuffPowerup(self, pickupType, elapsedTime):
        if pickupType == Globals.Level.GatherableTypes.InvulPowerup:
            self.blinkBubbleSeq.finish()
            self.popUpBubbleSeq.clearToInitial()
            self.removeBubbleSeq.start()
            if pickupType in self.activeBuffs:
                self.activeBuffs.remove(pickupType)

    def isBuffActive(self, pickupType):
        if pickupType in self.activeBuffs:
            return True
        return False

    def isInvulnerable(self):
        if Globals.Level.GatherableTypes.InvulPowerup in self.activeBuffs:
            return True
        return False

    def setFuelState(self, fuelState):
        self.fuelState = fuelState

    def setOldFuelState(self, fuelState):
        self.oldFuelState = fuelState

    def hasFuelStateChanged(self):
        if self.fuelState != self.oldFuelState:
            return True
        else:
            return False

    def updatePropellerSmoke(self):
        if not self.hasFuelStateChanged():
            return
        if self.fuelState in [Globals.Gameplay.FuelStates.FuelNoPropeller, Globals.Gameplay.FuelStates.FuelNormal]:
            self.propellerSmoke.stop()
        elif self.fuelState in [Globals.Gameplay.FuelStates.FuelVeryLow, Globals.Gameplay.FuelStates.FuelEmpty]:
            self.propellerSmoke.stop()
            self.propellerSmoke.setScale(0.25)
            self.propellerSmoke.setZ(self.toon.getHeight() + 2.5)
            self.propellerSmoke.loop(rate=48)
        elif self.fuelState in [Globals.Gameplay.FuelStates.FuelLow]:
            self.propellerSmoke.stop()
            self.propellerSmoke.setScale(0.0825)
            self.propellerSmoke.setZ(self.toon.getHeight() + 2.0)
            self.propellerSmoke.loop(rate=24)

    def resetBlades(self):
        self.setBlades(len(self.blades))

    def setAsLegalEagleTarget(self, legalEagle):
        if legalEagle not in self.legalEaglesTargeting:
            self.legalEaglesTargeting.append(legalEagle)

    def removeAsLegalEagleTarget(self, legalEagle):
        if legalEagle in self.legalEaglesTargeting:
            self.legalEaglesTargeting.remove(legalEagle)

    def isLegalEagleTarget(self):
        if len(self.legalEaglesTargeting) > 0:
            return True
        else:
            return False

    def setBlades(self, fuelState):
        if fuelState not in Globals.Gameplay.FuelStates:
            return
        numBlades = fuelState - 1
        if len(self.activeBlades) != numBlades:
            for i in xrange(len(self.activeBlades)):
                blade = self.activeBlades.pop()
                blade.stash()

            if numBlades > len(self.blades):
                numBlades = len(self.blades)
            if numBlades > 0:
                for i in xrange(numBlades):
                    blade = self.blades[i]
                    self.activeBlades.append(blade)
                    blade.unstash()

            if fuelState == Globals.Gameplay.FuelStates.FuelNoPropeller:
                for prop in self.propInstances:
                    prop.hide()

            else:
                for prop in self.propInstances:
                    prop.show()

            self.setFuelState(fuelState)
            self.updatePropellerSmoke()
            self.setOldFuelState(self.fuelState)

    def bladeLost(self):
        if len(self.activeBlades) > 0:
            blade = self.activeBlades.pop()
            blade.stash()
            self.setFuelState(len(self.activeBlades) + 1)
            self.updatePropellerSmoke()
            self.setOldFuelState(self.fuelState)

    def setPropellerSpinRate(self, newRate):
        self.lastPropellerSpinRate = self.propellerSpinRate
        self.propellerSpinRate = newRate
        self.notify.debug('(%s) New propeller speed:%s, old propeller speed:%s' % (self.toon.doId, self.propellerSpinRate, self.lastPropellerSpinRate))
        self.propellerSpinLerp.setPlayRate(newRate)

    def died(self, elapsedTime):
        self.deathInterval.start(elapsedTime)
        self.propellerSmoke.stop()

    def spawn(self, elapsedTime):
        self.spawnInterval.start(elapsedTime)

    def resetToon(self):
        self.toon.setScale(1.0)

    def enable(self):
        self.toon.setAnimState('Happy', 1.0)
        self.toon.setForceJumpIdle(True)
        self.toon.setSpeed(0, 0)
        self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
        self.propellerSpinLerp.loop()

    def disable(self):
        pass

    def unload(self):
        self.ignoreAll()
        if self.toon:
            self.toon.showName()
        self.backpackTextureCard.removeNode()
        del self.backpackTextureCard
        self.refuelSeq.clearToInitial()
        del self.refuelSeq
        self.pulseBubbleSeq.clearToInitial()
        del self.pulseBubbleSeq
        self.blinkBubbleLoop.clearToInitial()
        del self.blinkBubbleLoop
        self.blinkBubbleSeq.clearToInitial()
        del self.blinkBubbleSeq
        self.popUpBubbleLerp.clearToInitial()
        del self.popUpBubbleLerp
        self.popUpBubbleSeq.clearToInitial()
        del self.popUpBubbleSeq
        self.removeBubbleLerp.clearToInitial()
        del self.removeBubbleLerp
        self.removeBubbleSeq.clearToInitial()
        del self.removeBubbleSeq
        self.propellerSmoke.destroy()
        del self.propellerSmoke
        self.blinkWarningSeq.clearToInitial()
        del self.blinkWarningSeq
        self.blinkLoop.clearToInitial()
        del self.blinkLoop
        self.redTapeRing.removeNode()
        del self.redTapeRing
        self.propellerSpinLerp.clearToInitial()
        del self.propellerSpinLerp
        for prop in self.propInstances:
            prop.removeNode()

        del self.propInstances[:]
        self.propeller.removeNode()
        del self.propeller
        for backpack in self.backpackInstances:
            backpack.removeNode()

        del self.backpackInstances[:]
        self.backpack.removeNode()
        del self.backpack
        del self.activeBuffs[:]
        del self.legalEaglesTargeting[:]
        del self.toon
        self.toon = None
        if self.deathInterval:
            self.deathInterval.clearToInitial()
            self.deathInterval = None
        if self.spawnInterval:
            self.spawnInterval.clearToInitial()
            self.spawnInterval = None
        return

    def start(self):
        swapAvatarShadowPlacer(self.toon, self.toon.uniqueName('toonShadowPlacer'))
        self.toon.startSmooth()

    def exit(self):
        self.toon.setForceJumpIdle(False)
        self.propellerSmoke.reparentTo(hidden)
        self.propellerSmoke.stop()
        if self.toon:
            CogdoFlyingPlayer.resetToon(self)
            self.toon.setActiveShadow(0)
            self.toon.deleteDropShadow()
            self.toon.initializeDropShadow()
            self.toon.setActiveShadow(1)
        else:
            self.notify.warning("There's no toon in offstage, this is bad!")
class CogdoFlyingPlayer(FSM):
    notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingPlayer')

    def __init__(self, toon):
        FSM.__init__(self, 'CogdoFlyingPlayer')
        self.toon = toon
        self.toon.reparentTo(render)
        self.legalEaglesTargeting = []
        self.activeBuffs = []
        self.initModels()
        self.initIntervals()
        self.netTimeSentToStartDeath = 0
        self.backpackState = -1
        self.lastBackpackState = -1
        self.lastPropellerSpinRate = Globals.Gameplay.NormalPropSpeed
        self.propellerSpinRate = Globals.Gameplay.NormalPropSpeed
        self.setFuelState(Globals.Gameplay.FuelStates.FuelNoPropeller)
        self.setOldFuelState(self.fuelState)
        CogdoFlyingPlayer.setBlades(
            self, Globals.Gameplay.FuelStates.FuelNoPropeller)
        self.setBackpackState(Globals.Gameplay.BackpackStates.Normal)

    def initModels(self):
        self.createPropeller()
        self.createRedTapeRing()

    def createPropeller(self):
        self.propellerSmoke = DustCloud.DustCloud(self.toon, wantSound=False)
        self.propellerSmoke.setBillboardPointEye()
        self.propellerSmoke.setBin('fixed', 5002)
        self.backpack = CogdoUtil.loadFlyingModel('propellerPack')
        self.backpack.setScale(1.3)
        self.backpack.setHpr(180.0, 0.0, 0.0)
        self.backpackInstances = []
        self.backpackTextureCard = CogdoUtil.loadFlyingModel(
            'propellerPack_card')
        parts = self.toon.getTorsoParts()
        for part in parts:
            backpackInstance = part.attachNewNode('backpackInstance')
            animal = self.toon.style.getAnimal()
            bodyScale = ToontownGlobals.toonBodyScales[animal]
            backpackHeight = ToontownGlobals.torsoHeightDict[
                self.toon.style.torso] * bodyScale - 0.5
            backpackInstance.setPos(0.0, -0.325, backpackHeight)
            self.backpackInstances.append(backpackInstance)
            self.backpack.instanceTo(backpackInstance)

        self.propInstances = []
        self.propeller = CogdoUtil.loadFlyingModel('toonPropeller')
        for part in self.backpackInstances:
            propInstance = part.attachNewNode('propInstance')
            propInstance.setPos(0.0, -0.275, 0.0)
            propInstance.setHpr(0.0, 20.0, 0.0)
            propInstance.setScale(1.0, 1.0, 1.25)
            self.propInstances.append(propInstance)
            self.propeller.instanceTo(propInstance)

        self.blades = []
        self.activeBlades = []
        index = 1
        blade = self.propeller.find('**/propeller%d' % index)
        while not blade.isEmpty():
            self.blades.append(blade)
            index += 1
            blade = self.propeller.find('**/propeller%d' % index)

        for blade in self.blades:
            self.activeBlades.append(blade)

    def createRedTapeRing(self):
        self.redTapeRing = CogdoUtil.loadFlyingModel('redTapeRing')
        self.redTapeRing.setTwoSided(True)
        self.redTapeRing.reparentTo(self.toon)
        self.redTapeRing.hide()
        self.redTapeRing.setScale(1.25)
        self.redTapeRing.setZ(self.toon.getHeight() / 2.0)

    def initIntervals(self):
        self.baseSpinDuration = 1.0
        self.propellerSpinLerp = LerpFunctionInterval(
            self.propeller.setH,
            fromData=0.0,
            toData=360.0,
            duration=self.baseSpinDuration,
            name='%s.propellerSpinLerp-%s' %
            (self.__class__.__name__, self.toon.doId))
        singleBlinkTime = Globals.Gameplay.TargetedWarningSingleBlinkTime
        blinkTime = Globals.Gameplay.TargetedWarningBlinkTime
        self.blinkLoop = Sequence(
            Wait(singleBlinkTime / 2.0),
            Func(self.setBackpackTexture,
                 Globals.Gameplay.BackpackStates.Attacked),
            Wait(singleBlinkTime / 2.0),
            Func(self.setBackpackTexture,
                 Globals.Gameplay.BackpackStates.Targeted),
            name='%s.blinkLoop-%s' % (self.__class__.__name__, self.toon.doId))
        self.blinkWarningSeq = Sequence(
            Func(self.blinkLoop.loop),
            Wait(blinkTime),
            Func(self.blinkLoop.clearToInitial),
            name='%s.blinkWarningSeq-%s' %
            (self.__class__.__name__, self.toon.doId))
        dur = Globals.Gameplay.BackpackRefuelDuration
        self.refuelSeq = Sequence(
            Func(self.setPropellerSpinRate, Globals.Gameplay.RefuelPropSpeed),
            Wait(dur),
            Func(self.returnBackpackToLastStateFunc),
            name='%s.refuelSeq-%s' % (self.__class__.__name__, self.toon.doId))
        scale = self.redTapeRing.getScale()
        pulseTime = 1.0
        self.pulseBubbleSeq = Parallel(
            Sequence(
                LerpFunctionInterval(self.redTapeRing.setScale,
                                     fromData=scale,
                                     toData=scale * 1.1,
                                     duration=pulseTime / 2.0,
                                     blendType='easeInOut'),
                LerpFunctionInterval(self.redTapeRing.setScale,
                                     fromData=scale * 1.1,
                                     toData=scale,
                                     duration=pulseTime / 2.0,
                                     blendType='easeInOut')),
            LerpHprInterval(self.redTapeRing,
                            pulseTime,
                            Vec3(360, 0, 0),
                            startHpr=Vec3(0, 0, 0)),
            name='%s.pulseBubbleSeq-%s' %
            (self.__class__.__name__, self.toon.doId))
        bouncePercent = 1.2
        scaleTime = 0.5
        scaleBounceTime = 0.25
        self.popUpBubbleLerp = LerpScaleInterval(self.redTapeRing,
                                                 scaleTime,
                                                 scale * bouncePercent,
                                                 startScale=0.0,
                                                 blendType='easeInOut')
        self.popUpBubbleSeq = Sequence(
            Func(self.updateLerpStartScale, self.popUpBubbleLerp,
                 self.redTapeRing),
            Func(self.redTapeRing.show),
            self.popUpBubbleLerp,
            LerpScaleInterval(self.redTapeRing,
                              scaleBounceTime,
                              scale,
                              startScale=scale * bouncePercent,
                              blendType='easeInOut'),
            Func(self.pulseBubbleSeq.loop),
            name='%s.popUpBubbleSeq-%s' %
            (self.__class__.__name__, self.toon.doId))
        self.removeBubbleLerp = LerpScaleInterval(self.redTapeRing,
                                                  scaleBounceTime,
                                                  scale * bouncePercent,
                                                  startScale=scale,
                                                  blendType='easeInOut')
        self.removeBubbleSeq = Sequence(
            Func(self.pulseBubbleSeq.clearToInitial),
            Func(self.updateLerpStartScale, self.removeBubbleLerp,
                 self.redTapeRing),
            self.removeBubbleLerp,
            LerpScaleInterval(self.redTapeRing,
                              scaleTime,
                              0.0,
                              startScale=scale * bouncePercent,
                              blendType='easeInOut'),
            Func(self.redTapeRing.hide),
            name='%s.removeBubbleSeq-%s' %
            (self.__class__.__name__, self.toon.doId))
        self.redTapeRing.setScale(0.0)
        self.deathInterval = Sequence(
            Parallel(
                LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)),
                LerpFunctionInterval(self.toon.setScale,
                                     fromData=1.0,
                                     toData=0.1,
                                     duration=1.0)),
            Func(self.toon.stash),
            name='%s.deathInterval-%s' %
            (self.__class__.__name__, self.toon.doId))
        self.spawnInterval = Sequence(
            Func(self.toon.stash),
            Func(self.resetToon),
            Wait(1.0),
            Func(self.toon.setAnimState, 'TeleportIn'),
            Func(self.toon.unstash),
            name='%s.spawnInterval-%s' %
            (self.__class__.__name__, self.toon.doId))
        singleBlinkTime = Globals.Gameplay.InvulSingleBlinkTime
        blinkTime = Globals.Gameplay.InvulBlinkTime
        invulBuffTime = Globals.Gameplay.InvulBuffTime
        self.blinkBubbleLoop = Sequence(
            LerpFunctionInterval(self.redTapeRing.setAlphaScale,
                                 fromData=1.0,
                                 toData=0.0,
                                 duration=singleBlinkTime / 2.0,
                                 blendType='easeInOut'),
            LerpFunctionInterval(self.redTapeRing.setAlphaScale,
                                 fromData=0.0,
                                 toData=1.0,
                                 duration=singleBlinkTime / 2.0,
                                 blendType='easeInOut'),
            name='%s.blinkBubbleLoop-%s' %
            (self.__class__.__name__, self.toon.doId))
        self.blinkBubbleSeq = Sequence(
            Wait(invulBuffTime - blinkTime),
            Func(self.blinkBubbleLoop.loop),
            Wait(blinkTime),
            Func(self.blinkBubbleLoop.finish),
            name='%s.blinkBubbleSeq-%s' %
            (self.__class__.__name__, self.toon.doId))

    def returnBackpackToLastStateFunc(self):
        if self.backpackState == Globals.Gameplay.BackpackStates.Refuel:
            self.returnBackpackToLastState()

    def setPropellerSpinRateFunc(self):
        if self.propellerSpinRate == Globals.Gameplay.RefuelPropSpeed:
            self.setPropellerSpinRate(self.lastPropellerSpinRate)

    def returnBackpackToLastState(self):
        self.setBackpackState(self.lastBackpackState)

    def setBackpackState(self, state):
        if state == self.backpackState:
            return
        self.lastBackpackState = self.backpackState
        self.backpackState = state
        self.blinkWarningSeq.clearToInitial()
        self.refuelSeq.clearToInitial()
        self.blinkLoop.clearToInitial()
        if self.lastBackpackState == Globals.Gameplay.BackpackStates.Refuel:
            self.setPropellerSpinRateFunc()
        if state in Globals.Gameplay.BackpackStates:
            if state == Globals.Gameplay.BackpackStates.Normal:
                pass
            elif state == Globals.Gameplay.BackpackStates.Targeted:
                pass
            elif state == Globals.Gameplay.BackpackStates.Refuel:
                self.refuelSeq.start()
            elif state == Globals.Gameplay.BackpackStates.Attacked:
                self.blinkWarningSeq.start()
            self.setBackpackTexture(state)

    def setBackpackTexture(self, state):
        texName = Globals.Gameplay.BackpackState2TextureName[state]
        tex = self.backpackTextureCard.findTexture(texName)
        self.backpack.setTexture(tex, 1)

    def updateLerpStartScale(self, lerp, nodepath):
        lerp.setStartScale(nodepath.getScale())

    def handleEnterGatherable(self, gatherable, elapsedTime):
        if gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
            self.blinkBubbleSeq.clearToInitial()
            self.blinkBubbleSeq.start(elapsedTime)
            self.removeBubbleSeq.clearToInitial()
            self.popUpBubbleSeq.start()
            if gatherable.type not in self.activeBuffs:
                self.activeBuffs.append(gatherable.type)
        elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
            self.setBackpackState(Globals.Gameplay.BackpackStates.Refuel)

    def handleDebuffPowerup(self, pickupType, elapsedTime):
        if pickupType == Globals.Level.GatherableTypes.InvulPowerup:
            self.blinkBubbleSeq.finish()
            self.popUpBubbleSeq.clearToInitial()
            self.removeBubbleSeq.start()
            if pickupType in self.activeBuffs:
                self.activeBuffs.remove(pickupType)

    def isBuffActive(self, pickupType):
        if pickupType in self.activeBuffs:
            return True
        return False

    def isInvulnerable(self):
        if Globals.Level.GatherableTypes.InvulPowerup in self.activeBuffs:
            return True
        return False

    def setFuelState(self, fuelState):
        self.fuelState = fuelState

    def setOldFuelState(self, fuelState):
        self.oldFuelState = fuelState

    def hasFuelStateChanged(self):
        if self.fuelState != self.oldFuelState:
            return True
        else:
            return False

    def updatePropellerSmoke(self):
        if not self.hasFuelStateChanged():
            return
        if self.fuelState in [
                Globals.Gameplay.FuelStates.FuelNoPropeller,
                Globals.Gameplay.FuelStates.FuelNormal
        ]:
            self.propellerSmoke.stop()
        elif self.fuelState in [
                Globals.Gameplay.FuelStates.FuelVeryLow,
                Globals.Gameplay.FuelStates.FuelEmpty
        ]:
            self.propellerSmoke.stop()
            self.propellerSmoke.setScale(0.25)
            self.propellerSmoke.setZ(self.toon.getHeight() + 2.5)
            self.propellerSmoke.loop(rate=48)
        elif self.fuelState in [Globals.Gameplay.FuelStates.FuelLow]:
            self.propellerSmoke.stop()
            self.propellerSmoke.setScale(0.0825)
            self.propellerSmoke.setZ(self.toon.getHeight() + 2.0)
            self.propellerSmoke.loop(rate=24)

    def resetBlades(self):
        self.setBlades(len(self.blades))

    def setAsLegalEagleTarget(self, legalEagle):
        if legalEagle not in self.legalEaglesTargeting:
            self.legalEaglesTargeting.append(legalEagle)

    def removeAsLegalEagleTarget(self, legalEagle):
        if legalEagle in self.legalEaglesTargeting:
            self.legalEaglesTargeting.remove(legalEagle)

    def isLegalEagleTarget(self):
        if len(self.legalEaglesTargeting) > 0:
            return True
        else:
            return False

    def setBlades(self, fuelState):
        if fuelState not in Globals.Gameplay.FuelStates:
            return
        numBlades = fuelState - 1
        if len(self.activeBlades) != numBlades:
            for i in xrange(len(self.activeBlades)):
                blade = self.activeBlades.pop()
                blade.stash()

            if numBlades > len(self.blades):
                numBlades = len(self.blades)
            if numBlades > 0:
                for i in xrange(numBlades):
                    blade = self.blades[i]
                    self.activeBlades.append(blade)
                    blade.unstash()

            if fuelState == Globals.Gameplay.FuelStates.FuelNoPropeller:
                for prop in self.propInstances:
                    prop.hide()

            else:
                for prop in self.propInstances:
                    prop.show()

            self.setFuelState(fuelState)
            self.updatePropellerSmoke()
            self.setOldFuelState(self.fuelState)

    def bladeLost(self):
        if len(self.activeBlades) > 0:
            blade = self.activeBlades.pop()
            blade.stash()
            self.setFuelState(len(self.activeBlades) + 1)
            self.updatePropellerSmoke()
            self.setOldFuelState(self.fuelState)

    def setPropellerSpinRate(self, newRate):
        self.lastPropellerSpinRate = self.propellerSpinRate
        self.propellerSpinRate = newRate
        self.notify.debug(
            '(%s) New propeller speed:%s, old propeller speed:%s' %
            (self.toon.doId, self.propellerSpinRate,
             self.lastPropellerSpinRate))
        self.propellerSpinLerp.setPlayRate(newRate)

    def died(self, elapsedTime):
        self.deathInterval.start(elapsedTime)
        self.propellerSmoke.stop()

    def spawn(self, elapsedTime):
        self.spawnInterval.start(elapsedTime)

    def resetToon(self):
        self.toon.setScale(1.0)

    def enable(self):
        self.toon.setAnimState('Happy', 1.0)
        self.toon.setForceJumpIdle(True)
        self.toon.setSpeed(0, 0)
        self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
        self.propellerSpinLerp.loop()

    def disable(self):
        pass

    def unload(self):
        self.ignoreAll()
        if self.toon:
            self.toon.showName()
        self.backpackTextureCard.removeNode()
        del self.backpackTextureCard
        self.refuelSeq.clearToInitial()
        del self.refuelSeq
        self.pulseBubbleSeq.clearToInitial()
        del self.pulseBubbleSeq
        self.blinkBubbleLoop.clearToInitial()
        del self.blinkBubbleLoop
        self.blinkBubbleSeq.clearToInitial()
        del self.blinkBubbleSeq
        self.popUpBubbleLerp.clearToInitial()
        del self.popUpBubbleLerp
        self.popUpBubbleSeq.clearToInitial()
        del self.popUpBubbleSeq
        self.removeBubbleLerp.clearToInitial()
        del self.removeBubbleLerp
        self.removeBubbleSeq.clearToInitial()
        del self.removeBubbleSeq
        self.propellerSmoke.destroy()
        del self.propellerSmoke
        self.blinkWarningSeq.clearToInitial()
        del self.blinkWarningSeq
        self.blinkLoop.clearToInitial()
        del self.blinkLoop
        self.redTapeRing.removeNode()
        del self.redTapeRing
        self.propellerSpinLerp.clearToInitial()
        del self.propellerSpinLerp
        for prop in self.propInstances:
            prop.removeNode()

        del self.propInstances[:]
        self.propeller.removeNode()
        del self.propeller
        for backpack in self.backpackInstances:
            backpack.removeNode()

        del self.backpackInstances[:]
        self.backpack.removeNode()
        del self.backpack
        del self.activeBuffs[:]
        del self.legalEaglesTargeting[:]
        del self.toon
        self.toon = None
        if self.deathInterval:
            self.deathInterval.clearToInitial()
            self.deathInterval = None
        if self.spawnInterval:
            self.spawnInterval.clearToInitial()
            self.spawnInterval = None
        return

    def start(self):
        swapAvatarShadowPlacer(self.toon,
                               self.toon.uniqueName('toonShadowPlacer'))
        self.toon.startSmooth()

    def exit(self):
        self.toon.setForceJumpIdle(False)
        self.propellerSmoke.reparentTo(hidden)
        self.propellerSmoke.stop()
        if self.toon:
            CogdoFlyingPlayer.resetToon(self)
            self.toon.setActiveShadow(0)
            self.toon.deleteDropShadow()
            self.toon.initializeDropShadow()
            self.toon.setActiveShadow(1)
        else:
            self.notify.warning("There's no toon in offstage, this is bad!")
class CogdoFlyingFuelGui(DirectFrame):

    def __init__(self, parent):
        DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
        self.reparentTo(parent)
        self.active = 0
        self._initModel()
        self._initIntervals()
        return

    def _initModel(self):
        self.setPos(Globals.Gui.FuelPos2D[0], 0.0, Globals.Gui.FuelPos2D[1])
        self.gui = CogdoUtil.loadFlyingModel('propellerMeter', group='gui')
        self.gui.reparentTo(self)
        self.gui.setBin('fixed', 0)
        self.healthBar = self.gui.find('**/healthBar')
        self.healthBar.setBin('fixed', 1)
        self.healthBar.setColor(*Globals.Gui.FuelNormalColor)
        bottomBarLocator = self.gui.find('**/bottomOfBar_loc')
        bottomBarPos = bottomBarLocator.getPos(render)
        topBarLocator = self.gui.find('**/topOfBar_loc')
        topBarPos = topBarLocator.getPos(render)
        zDist = topBarPos.getZ() - bottomBarPos.getZ()
        self.fuelLowIndicator = self.gui.find('**/fuelLowIndicator')
        self.fuelLowIndicator.setBin('fixed', 2)
        pos = self.fuelLowIndicator.getPos(render)
        newPos = pos
        newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelLowAmt)
        self.fuelLowIndicator.setPos(render, newPos)
        self.fuelVeryLowIndicator = self.gui.find('**/fuelVeryLowIndicator')
        self.fuelVeryLowIndicator.setBin('fixed', 2)
        pos = self.fuelVeryLowIndicator.getPos(render)
        newPos = pos
        newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelVeryLowAmt)
        self.fuelVeryLowIndicator.setPos(render, newPos)
        self.propellerMain = self.gui.find('**/propellers')
        self.propellerMain.setBin('fixed', 3)
        self.propellerHead = self.gui.find('**/propellerHead')
        self.propellerHead.setBin('fixed', 4)
        self.blades = []
        self.activeBlades = []
        index = 1
        blade = self.propellerMain.find('**/propeller%d' % index)
        while not blade.isEmpty():
            self.blades.append(blade)
            index += 1
            blade = self.propellerMain.find('**/propeller%d' % index)

        for blade in self.blades:
            self.activeBlades.append(blade)

        self.bladeNumberLabel = DirectLabel(parent=self.propellerHead, relief=None, pos=(Globals.Gui.FuelNumBladesPos2D[0], 0, Globals.Gui.FuelNumBladesPos2D[1]), scale=Globals.Gui.FuelNumBladesScale, text=str(len(self.activeBlades)), text_align=TextNode.ACenter, text_fg=(0.0,
         0.0,
         -0.002,
         1), text_shadow=(0.75, 0.75, 0.75, 1), text_font=ToontownGlobals.getInterfaceFont())
        self.bladeNumberLabel.setBin('fixed', 5)
        return

    def _initIntervals(self):
        self._healthIval = LerpFunctionInterval(self.healthBar.setSz, fromData=0.0, toData=1.0, duration=2.0)
        self.baseSpinDuration = 2.0
        self._spinIval = LerpFunctionInterval(self.propellerMain.setR, fromData=0.0, toData=-360.0, duration=self.baseSpinDuration)

    def show(self):
        DirectFrame.show(self)
        self._spinIval.loop()

    def hide(self):
        DirectFrame.hide(self)
        self._spinIval.pause()

    def resetBlades(self):
        self.setBlades(len(self.blades))

    def setBlades(self, fuelState):
        if fuelState not in Globals.Gameplay.FuelStates:
            return
        numBlades = fuelState - 1
        if len(self.activeBlades) != numBlades:
            for i in range(len(self.activeBlades)):
                blade = self.activeBlades.pop()
                blade.stash()

            if numBlades > len(self.blades):
                numBlades = len(self.blades)
            for i in range(numBlades):
                blade = self.blades[i]
                self.activeBlades.append(blade)
                blade.unstash()

            self.bladeNumberLabel['text'] = str(len(self.activeBlades))
            self.bladeNumberLabel.setText()
        self.updateHealthBarColor()

    def bladeLost(self):
        if len(self.activeBlades) > 0:
            blade = self.activeBlades.pop()
            blade.stash()
            self.bladeNumberLabel['text'] = str(len(self.activeBlades))
            self.bladeNumberLabel.setText()
        self.updateHealthBarColor()

    def updateHealthBarColor(self):
        color = Globals.Gui.NumBlades2FuelColor[len(self.activeBlades)]
        self.healthBar.setColor(*color)

    def setPropellerSpinRate(self, newRate):
        self._spinIval.setPlayRate(newRate)

    def setRefuelLerpFromData(self):
        startScale = self.healthBar.getSz()
        self._healthIval.fromData = startScale

    def setFuel(self, fuel):
        self.fuel = fuel

    def update(self):
        self.healthBar.setSz(self.fuel)

    def destroy(self):
        self.bladeNumberLabel.removeNode()
        self.bladeNumberLabel = None
        self._healthIval.clearToInitial()
        del self._healthIval
        self.healthBar = None
        self.fuelLowIndicator = None
        self.fuelVeryLowIndicator = None
        self.propellerMain = None
        self.propellerHead = None
        del self.blades[:]
        del self.activeBlades[:]
        self.gui.detachNode()
        self.gui = None
        DirectFrame.destroy(self)
        return
class CogdoFlyingFuelGui(DirectFrame):

    def __init__(self, parent):
        DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX)
        self.reparentTo(parent)
        self.active = 0
        self._initModel()
        self._initIntervals()
        return

    def _initModel(self):
        self.setPos(Globals.Gui.FuelPos2D[0], 0.0, Globals.Gui.FuelPos2D[1])
        self.gui = CogdoUtil.loadFlyingModel('propellerMeter', group='gui')
        self.gui.reparentTo(self)
        self.gui.setBin('fixed', 0)
        self.healthBar = self.gui.find('**/healthBar')
        self.healthBar.setBin('fixed', 1)
        self.healthBar.setColor(*Globals.Gui.FuelNormalColor)
        bottomBarLocator = self.gui.find('**/bottomOfBar_loc')
        bottomBarPos = bottomBarLocator.getPos(render)
        topBarLocator = self.gui.find('**/topOfBar_loc')
        topBarPos = topBarLocator.getPos(render)
        zDist = topBarPos.getZ() - bottomBarPos.getZ()
        self.fuelLowIndicator = self.gui.find('**/fuelLowIndicator')
        self.fuelLowIndicator.setBin('fixed', 2)
        pos = self.fuelLowIndicator.getPos(render)
        newPos = pos
        newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelLowAmt)
        self.fuelLowIndicator.setPos(render, newPos)
        self.fuelVeryLowIndicator = self.gui.find('**/fuelVeryLowIndicator')
        self.fuelVeryLowIndicator.setBin('fixed', 2)
        pos = self.fuelVeryLowIndicator.getPos(render)
        newPos = pos
        newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelVeryLowAmt)
        self.fuelVeryLowIndicator.setPos(render, newPos)
        self.propellerMain = self.gui.find('**/propellers')
        self.propellerMain.setBin('fixed', 3)
        self.propellerHead = self.gui.find('**/propellerHead')
        self.propellerHead.setBin('fixed', 4)
        self.blades = []
        self.activeBlades = []
        index = 1
        blade = self.propellerMain.find('**/propeller%d' % index)
        while not blade.isEmpty():
            self.blades.append(blade)
            index += 1
            blade = self.propellerMain.find('**/propeller%d' % index)

        for blade in self.blades:
            self.activeBlades.append(blade)

        self.bladeNumberLabel = DirectLabel(parent=self.propellerHead, relief=None, pos=(Globals.Gui.FuelNumBladesPos2D[0], 0, Globals.Gui.FuelNumBladesPos2D[1]), scale=Globals.Gui.FuelNumBladesScale, text=str(len(self.activeBlades)), text_align=TextNode.ACenter, text_fg=(0.0,
         0.0,
         -0.002,
         1), text_shadow=(0.75, 0.75, 0.75, 1), text_font=ToontownGlobals.getInterfaceFont())
        self.bladeNumberLabel.setBin('fixed', 5)
        return

    def _initIntervals(self):
        self._healthIval = LerpFunctionInterval(self.healthBar.setSz, fromData=0.0, toData=1.0, duration=2.0)
        self.baseSpinDuration = 2.0
        self._spinIval = LerpFunctionInterval(self.propellerMain.setR, fromData=0.0, toData=-360.0, duration=self.baseSpinDuration)

    def show(self):
        DirectFrame.show(self)
        self._spinIval.loop()

    def hide(self):
        DirectFrame.hide(self)
        self._spinIval.pause()

    def resetBlades(self):
        self.setBlades(len(self.blades))

    def setBlades(self, fuelState):
        if fuelState not in Globals.Gameplay.FuelStates:
            return
        numBlades = fuelState - 1
        if len(self.activeBlades) != numBlades:
            for i in range(len(self.activeBlades)):
                blade = self.activeBlades.pop()
                blade.stash()

            if numBlades > len(self.blades):
                numBlades = len(self.blades)
            for i in range(numBlades):
                blade = self.blades[i]
                self.activeBlades.append(blade)
                blade.unstash()

            self.bladeNumberLabel['text'] = str(len(self.activeBlades))
            self.bladeNumberLabel.setText()
        self.updateHealthBarColor()

    def bladeLost(self):
        if len(self.activeBlades) > 0:
            blade = self.activeBlades.pop()
            blade.stash()
            self.bladeNumberLabel['text'] = str(len(self.activeBlades))
            self.bladeNumberLabel.setText()
        self.updateHealthBarColor()

    def updateHealthBarColor(self):
        color = Globals.Gui.NumBlades2FuelColor[len(self.activeBlades)]
        self.healthBar.setColor(*color)

    def setPropellerSpinRate(self, newRate):
        self._spinIval.setPlayRate(newRate)

    def setRefuelLerpFromData(self):
        startScale = self.healthBar.getSz()
        self._healthIval.fromData = startScale

    def setFuel(self, fuel):
        self.fuel = fuel

    def update(self):
        self.healthBar.setSz(self.fuel)

    def destroy(self):
        self.bladeNumberLabel.removeNode()
        self.bladeNumberLabel = None
        self._healthIval.clearToInitial()
        del self._healthIval
        self.healthBar = None
        self.fuelLowIndicator = None
        self.fuelVeryLowIndicator = None
        self.propellerMain = None
        self.propellerHead = None
        del self.blades[:]
        del self.activeBlades[:]
        self.gui.detachNode()
        self.gui = None
        DirectFrame.destroy(self)
        return