Beispiel #1
0
class HangUpAttack(Attack):
    notify = directNotify.newCategory('HangUpAttack')
    attack = 'hangup'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.phone = None
        self.receiver = None
        self.collNP = None
        self.phoneSfx = None
        self.hangupSfx = None
        self.shootIval = None
        self.cord = None
        self.receiverOutCord = None
        self.phoneOutCord = None
        return

    def loadAttack(self):
        self.phone = loader.loadModel('phase_3.5/models/props/phone.bam')
        self.phone.setHpr(0, 0, 180)
        if self.suit.type == 'B':
            self.phone.setPos(0.7, 0.15, 0)
        elif self.suit.type == 'C':
            self.phone.setPos(0.25, 0, 0)
        self.receiver = loader.loadModel('phase_3.5/models/props/receiver.bam')
        self.receiver.reparentTo(self.phone)
        self.cord = Rope()
        self.cord.ropeNode.setUseVertexColor(1)
        self.cord.ropeNode.setUseVertexThickness(1)
        self.cord.setup(3, ({'node': self.phone,
          'point': (0.8, 0, 0.2),
          'color': (0, 0, 0, 1),
          'thickness': 1000}, {'node': self.phone,
          'point': (2, 0, 0),
          'color': (0, 0, 0, 1),
          'thickness': 1000}, {'node': self.receiver,
          'point': (1.1, 0.25, 0.5),
          'color': (0, 0, 0, 1),
          'thickness': 1000}), [])
        self.cord.setH(180)
        self.phoneSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup.mp3')
        base.audio3d.attachSoundToObject(self.phoneSfx, self.phone)
        self.hangupSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup_place_down.mp3')
        base.audio3d.attachSoundToObject(self.hangupSfx, self.phone)
        collSphere = CollisionSphere(0, 0, 0, 2)
        collSphere.setTangible(0)
        collNode = CollisionNode('phone_shootout')
        collNode.addSolid(collSphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        self.collNP = self.phone.attachNewNode(collNode)

    def doAttack(self, ts = 0):
        self.loadAttack()
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doHangupAttack')
        else:
            name = 'doHangupAttack'
        if self.suit.type == 'A':
            delay2playSound = 1.0
            delayAfterSoundToPlaceDownReceiver = 0.2
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.0
            receiverInHandPos = Point3(-0.5, 0.5, -1)
        elif self.suit.type == 'B':
            delay2playSound = 1.5
            delayAfterSoundToPlaceDownReceiver = 0.7
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.5
            receiverInHandPos = Point3(-0.3, 0.5, -0.8)
        elif self.suit.type == 'C':
            delay2playSound = 1.0
            delayAfterSoundToPlaceDownReceiver = 1.15
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.5
            receiverInHandPos = Point3(-0.3, 0.5, -0.8)
        self.suitTrack = Parallel(name=name)
        self.suitTrack.append(ActorInterval(self.suit, 'phone'))
        self.suitTrack.append(Sequence(Wait(delay2playSound), SoundInterval(self.phoneSfx, duration=2.1), Wait(delayAfterSoundToPlaceDownReceiver), Func(self.receiver.setPos, 0, 0, 0), Func(self.receiver.setH, 0.0), Func(self.receiver.reparentTo, self.phone), Func(self.acceptOnce, 'enter' + self.collNP.node().getName(), self.handleCollision), Func(self.shootOut), Parallel(SoundInterval(self.hangupSfx), Sequence(Wait(delayAfterShootToIgnoreCollisions), Func(self.ignore, 'enter' + self.collNP.node().getName())))))
        self.suitTrack.append(Sequence(Func(self.phone.reparentTo, self.suit.find('**/joint_Lhold')), Func(self.cord.reparentTo, render), Wait(delay2PickUpReceiver), Func(self.receiver.reparentTo, self.suit.find('**/joint_Rhold')), Func(self.receiver.setPos, receiverInHandPos), Func(self.receiver.setH, 270.0)))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def handleCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def shootOut(self):
        pathNode = NodePath('path')
        pathNode.reparentTo(self.suit)
        pathNode.setPos(0, 50, self.phone.getZ(self.suit))
        self.collNP.reparentTo(render)
        self.shootIval = LerpPosInterval(self.collNP, duration=1.0, pos=pathNode.getPos(render), startPos=self.phone.getPos(render))
        self.shootIval.start()
        pathNode.removeNode()
        del pathNode

    def cleanup(self):
        Attack.cleanup(self)
        if self.shootIval:
            self.shootIval.pause()
            self.shootIval = None
        if self.cord:
            self.cord.removeNode()
            self.cord = None
        if self.phone:
            self.phone.removeNode()
            self.phone = None
        if self.receiver:
            self.receiver.removeNode()
            self.receiver = None
        if self.collNP:
            self.collNP.node().clearSolids()
            self.collNP.removeNode()
            self.collNP = None
        if self.phoneSfx:
            self.phoneSfx.stop()
            self.phoneSfx = None
        return
Beispiel #2
0
class ParticleAttack(Attack):
    notify = directNotify.newCategory('ParticleAttack')
    attack = 'particleattack'
    particleIvalDur = 1
    shooterDistance = 50

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.particles = []
        self.handObj = None
        self.shootOutCollNP = None
        self.particleSound = None
        self.particleMoveIval = None
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        return

    def handleWeaponTouch(self):
        pass

    def handleCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def doAttack(self, particlePaths, track_name, particleCollId, animation_name, delayUntilRelease, animationSpeed = 1, handObjPath = None, handObjParent = None, startRightAway = True, ts = 0):
        for path in particlePaths:
            particle = ParticleLoader.loadParticleEffect(path)
            self.particles.append(particle)

        sphere = CollisionSphere(0, 0, 0, 1)
        sphere.setTangible(0)
        node = CollisionNode(particleCollId)
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        if len(self.particles) == 1:
            self.shootOutCollNP = self.particles[0].attachNewNode(node)
        else:
            self.shootOutCollNP = self.suit.attachNewNode(node)
        if handObjPath and handObjParent:
            self.handObj = loader.loadModel(handObjPath)
            self.handObj.reparentTo(handObjParent)
        self.suit.setPlayRate(animationSpeed, animation_name)
        self.suit.play(animation_name)
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            particleCollId = self.suit.uniqueName(particleCollId)
        self.suitTrack = Sequence(name=track_name)
        self.suitTrack.append(Wait(delayUntilRelease))
        self.suitTrack.append(Func(self.releaseAttack))
        self.suitTrack.append(Wait(self.particleIvalDur))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        if startRightAway:
            self.suitTrack.start(ts)

    def releaseAttack(self, releaseFromJoint, onlyMoveColl = True, blendType = 'noBlend'):
        startNP = releaseFromJoint.attachNewNode('startNP')
        if None not in [self.targetX, self.targetY, self.targetZ]:
            startNP.lookAt(render, self.targetX, self.targetY, self.targetZ + 2)
            pathNP = NodePath('path')
            pathNP.reparentTo(startNP)
            pathNP.setScale(render, 1.0)
            pathNP.setPos(0, self.shooterDistance, 0)
            for particle in self.particles:
                if not onlyMoveColl:
                    particle.start(render)
                else:
                    particle.start(self.suit)
                particle.lookAt(pathNP)
                if self.attack == 'razzledazzle':
                    particle.setP(particle, 90)

            if onlyMoveColl:
                target = self.shootOutCollNP
                target.wrtReparentTo(render)
            else:
                target = self.particles[0]
            self.particleMoveIval = LerpPosInterval(target, duration=self.particleIvalDur, pos=pathNP.getPos(render), startPos=startNP.getPos(render), blendType=blendType)
            self.particleMoveIval.start()
            self.acceptOnce('enter' + self.shootOutCollNP.node().getName(), self.handleCollision)
            pathNP.removeNode()
            startNP.removeNode()
            del pathNP
            del startNP
        self.playParticleSound()
        return

    def playParticleSound(self):
        if self.particleSound:
            base.audio3d.attachSoundToObject(self.particleSound, self.suit)
            base.playSfx(self.particleSound)

    def cleanup(self):
        Attack.cleanup(self)
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        if self.particles:
            for particle in self.particles:
                particle.cleanup()

        self.particles = None
        if self.handObj:
            self.handObj.removeNode()
            self.handObj = None
        if self.shootOutCollNP:
            self.ignore('enter' + self.shootOutCollNP.node().getName())
            self.shootOutCollNP.removeNode()
            self.shootOutCollNP = None
        if self.particleMoveIval:
            self.particleMoveIval.pause()
            self.particleMoveIval = None
        self.particleSound = None
        self.particleIvalDur = None
        return
class DistributedEagleSuit(DistributedSuit):
    notify = directNotify.newCategory('DistributedEagleSuit')

    def __init__(self, cr):
        DistributedSuit.__init__(self, cr)
        self.eagleCry = base.audio3d.loadSfx(
            'phase_5/audio/sfx/tt_s_ara_cfg_eagleCry.ogg')
        base.audio3d.attachSoundToObject(self.eagleCry, self)
        self.fallWhistle = base.audio3d.loadSfx(
            'phase_5/audio/sfx/incoming_whistleALT.ogg')
        base.audio3d.attachSoundToObject(self.fallWhistle, self)
        self.explode = base.audio3d.loadSfx(
            'phase_3.5/audio/sfx/ENC_cogfall_apart.ogg')
        base.audio3d.attachSoundToObject(self.explode, self)
        self.eventSphereNodePath = None
        self.fallingPropeller = None
        self.fallingPropProjectile = None
        self.mg = None
        self.flySpeed = 0.0
        return

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

    def makeStateDict(self):
        self.suitFSM.addState(
            State('eagleFly', self.enterEagleFly, self.exitEagleFly))
        self.suitFSM.addState(
            State('eagleFall', self.enterEagleFall, self.exitEagleFall))
        self.stateIndex2suitState = {
            0: self.suitFSM.getStateNamed('off'),
            1: self.suitFSM.getStateNamed('walking'),
            2: self.suitFSM.getStateNamed('flyingDown'),
            3: self.suitFSM.getStateNamed('flyingUp'),
            4: self.suitFSM.getStateNamed('lured'),
            5: self.suitFSM.getStateNamed('eagleFly'),
            6: self.suitFSM.getStateNamed('eagleFall')
        }
        self.suitState2stateIndex = {}
        for stateId, state in self.stateIndex2suitState.items():
            self.suitState2stateIndex[state.getName()] = stateId

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

    def getFlySpeed(self):
        return self.flySpeed

    def enterEagleFly(self, startIndex, endIndex, ts=0.0):
        durationFactor = self.getFlySpeed()
        if startIndex > -1:
            startPos = EGG.EAGLE_FLY_POINTS[startIndex]
        else:
            startPos = self.getPos(render)
        endPos = EGG.EAGLE_FLY_POINTS[endIndex]
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        self.moveIval = NPCWalkInterval(self,
                                        endPos,
                                        durationFactor=durationFactor,
                                        startPos=startPos,
                                        fluid=1)
        self.moveIval.start(ts)
        return

    def exitEagleFly(self):
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        return

    def enterEagleFall(self, startIndex, endIndex, ts=0.0):
        self.moveIval = LerpPosInterval(self,
                                        duration=4.0,
                                        pos=self.getPos(render) - (0, 0, 75),
                                        startPos=self.getPos(render),
                                        blendType='easeIn')
        self.moveIval.start(ts)

    def exitEagleFall(self):
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        return

    def fallAndExplode(self):
        self.cleanupPropeller()
        self.fallingPropeller = Actor(
            'phase_4/models/props/propeller-mod.bam',
            {'chan': 'phase_4/models/props/propeller-chan.bam'})
        self.fallingPropeller.reparentTo(render)
        self.fallingPropeller.loop('chan', fromFrame=0, toFrame=3)
        parentNode = self.attachNewNode('fallingPropParentNode')
        h = random.randint(0, 359)
        parentNode.setH(h)
        dummyNode = parentNode.attachNewNode('dummyNode')
        dummyNode.setPos(0, 10, -50)
        self.fallingPropProjectile = FlightProjectileInterval(
            self.fallingPropeller,
            startPos=self.find('**/joint_head').getPos(render),
            endPos=dummyNode.getPos(render),
            duration=5.0,
            gravityMult=0.25)
        self.fallingPropProjectile.start()
        dummyNode.removeNode()
        del dummyNode
        parentNode.removeNode()
        del parentNode
        self.updateHealthBar(0)
        self.ignoreHit()
        base.playSfx(self.fallWhistle, node=self)
        taskMgr.doMethodLater(4.0, self.doExplodeSound,
                              self.uniqueName('DEagleSuit-doExplodeSound'))

    def doExplodeSound(self, task):
        base.playSfx(self.explode, node=self)
        return Task.done

    def __initializeEventSphere(self):
        sphere = CollisionSphere(0, 0, 0, 2)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('DEagleSuit-eventSphere'))
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        np = self.attachNewNode(node)
        np.setSz(2.5)
        np.setZ(5.5)
        self.eventSphereNodePath = np

    def removeEventSphere(self):
        if self.eventSphereNodePath:
            self.eventSphereNodePath.removeNode()
            self.eventSphereNodePath = None
        return

    def acceptHit(self):
        self.acceptOnce('enter' + self.eventSphereNodePath.node().getName(),
                        self.__handleHit)

    def ignoreHit(self):
        self.ignore('enter' + self.eventSphereNodePath.node().getName())

    def __handleHit(self, entry):
        messenger.send(EGG.EAGLE_HIT_EVENT, [self.doId])

    def setSuit(self, arg, variant):
        DistributedSuit.setSuit(self, arg, 3)
        self.deleteShadow()
        self.disableBodyCollisions()
        self.disableRay()
        self.__initializeEventSphere()
        self.show()
        self.setAnimState('flyNeutral')

    def __doEagleCry(self, task):
        base.playSfx(self.eagleCry, node=self)
        task.delayTime = random.uniform(3, 30)
        return Task.again

    def announceGenerate(self):
        DistributedSuit.announceGenerate(self)
        taskMgr.doMethodLater(random.uniform(5, 25), self.__doEagleCry,
                              self.uniqueName('DEagleSuit-doEagleCry'))
        self.acceptHit()

    def disable(self):
        self.ignoreHit()
        self.removeEventSphere()
        taskMgr.remove(self.uniqueName('DEagleSuit-doExplodeSound'))
        taskMgr.remove(self.uniqueName('DEagleSuit-doEagleCry'))
        if self.fallingPropProjectile:
            self.fallingPropProjectile.finish()
            self.fallingPropProjectile = None
        if self.fallingPropeller:
            self.fallingPropeller.cleanup()
            self.fallingPropeller = None
        base.audio3d.detachSound(self.fallWhistle)
        del self.fallWhistle
        base.audio3d.detachSound(self.explode)
        del self.explode
        base.audio3d.detachSound(self.eagleCry)
        del self.eagleCry
        self.mg = None
        DistributedSuit.disable(self)
        return
Beispiel #4
0
class ThrowAttack(Attack):
    notify = directNotify.newCategory('ThrowAttack')
    attack = 'throw'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.weapon_state = None
        self.weapon = None
        self.wss = None
        self.wsnp = None
        self.suitTrack = None
        self.weaponSfx = None
        self.throwTrajectory = None
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        self.startNP = None
        return

    def handleWeaponCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)
            self.suit.b_handleWeaponTouch()

    def doAttack(self, weapon_path, weapon_scale, track_name, animation_name, collsphere_radius, weapon_coll_id, weapon_h = 0, weapon_p = 0, weapon_r = 0, weapon_x = 0, weapon_y = 0, weapon_z = 0, ts = 0):
        self.weapon_state = 'start'
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            weapon_coll_id = self.suit.uniqueName(weapon_coll_id)
        self.weapon = loader.loadModel(weapon_path)
        self.weapon.setScale(weapon_scale)
        self.weapon.setHpr(weapon_h, weapon_p, weapon_r)
        self.weapon.setPos(weapon_x, weapon_y, weapon_z)
        self.wss = CollisionSphere(0, 0, 0, collsphere_radius)
        self.wss.setTangible(0)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        self.suitTrack = Sequence(name=track_name)
        if self.attack not in ('glowerpower',):
            self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
            self.suitTrack.append(Wait(1.2))
            self.suitTrack.append(Func(self.suit.setPlayRate, 1.0, animation_name))
            if self.suit.type == 'C':
                self.suitTrack.append(Wait(0))
            else:
                self.suitTrack.append(Wait(0.7))
            self.suit.setPlayRate(2.0, animation_name)
            self.suitTrack.append(Func(self.throwObject))
            self.suitTrack.append(Wait(1.0))
            self.suitTrack.append(Func(self.delWeapon))
        else:
            self.suitTrack.append(Wait(1))
            self.suitTrack.append(Func(self.throwObject))
            self.suitTrack.append(Wait(0.5))
            self.suitTrack.append(Func(self.delWeapon))
        self.suit.play(animation_name)
        wsnode = CollisionNode(weapon_coll_id)
        wsnode.addSolid(self.wss)
        wsnode.setCollideMask(CIGlobals.WallBitmask)
        self.wsnp = self.weapon.attachNewNode(wsnode)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, track_name)
        self.suitTrack.start(ts)

    def playWeaponSound(self):
        if self.weapon and self.weaponSfx:
            base.audio3d.attachSoundToObject(self.weaponSfx, self.suit)
            self.weaponSfx.play()

    def throwObject(self, projectile = True):
        if not self.weapon:
            return
        self.acceptOnce('enter' + self.wsnp.node().getName(), self.handleWeaponCollision)
        self.playWeaponSound()
        if self.weapon:
            self.weapon.wrtReparentTo(render)
            self.weapon.setHpr(Vec3(0, 0, 0))
        if self.attack not in ('glowerpower',):
            parent = self.suit.find('**/joint_Rhold')
        else:
            parent = self.suit.find('**/joint_head')
        startNP = parent.attachNewNode('startNp')
        startNP.lookAt(render, self.targetX, self.targetY, self.targetZ)
        pathNP = NodePath('throwPath')
        pathNP.reparentTo(startNP)
        pathNP.setScale(render, 1.0)
        pathNP.setPos(0, 50, 0)
        if self.attack in ('clipontie', 'powertie', 'halfwindsor'):
            self.weapon.setHpr(pathNP.getHpr(render))
        if projectile == True:
            self.throwTrajectory = ProjectileInterval(self.weapon, startPos=self.suit.find('**/joint_Rhold').getPos(render), endPos=pathNP.getPos(render), gravityMult=0.7, duration=1.0)
        else:
            self.weapon.setH(pathNP.getH(render))
            self.throwTrajectory = LerpPosInterval(self.weapon, duration=0.5, pos=pathNP.getPos(render), startPos=startNP.getPos(render) + (0, 3, 0))
        self.throwTrajectory.start()
        self.weapon_state = 'released'
        startNP.removeNode()
        del startNP
        pathNP.removeNode()
        del pathNP

    def interruptAttack(self):
        if self.throwTrajectory:
            if self.throwTrajectory.isStopped():
                self.delWeapon()

    def handleWeaponTouch(self):
        if self.throwTrajectory:
            self.throwTrajectory.pause()
            self.throwTrajectory = None
        self.delWeapon()
        return

    def delWeapon(self):
        if self.weapon:
            self.weapon.removeNode()
            self.weapon = None
        return

    def cleanup(self):
        Attack.cleanup(self)
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        self.weapon_state = None
        if self.weaponSfx:
            self.weaponSfx.stop()
            self.weaponSfx = None
        if self.throwTrajectory:
            self.throwTrajectory.pause()
            self.throwTrajectory = None
        self.delWeapon()
        self.wss = None
        if self.wsnp:
            self.wsnp.node().clearSolids()
            self.wsnp.removeNode()
            self.wsnp = None
        return
class DistributedSuit(Suit, DistributedAvatar, DistributedSmoothNode,
                      DelayDeletable):
    notify = directNotify.newCategory('DistributedSuit')

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

        self.anim = None
        self._state = SuitState.ALIVE
        self.dept = None
        self.variant = None
        self.suitPlan = None
        self.level = None
        self.moveIval = None
        self.hpFlash = None

        # For PythonCTMusicManager:
        # Are we in range of the localAvatar?
        self.isInRange = False

        self.chaseTarget = 0

        self.suitFSM = ClassicFSM('DistributedSuit', [
            State('off', self.enterSuitOff, self.exitSuitOff),
            State('walking', self.enterWalking, self.exitWalking),
            State('flyingDown', self.enterFlyingDown, self.exitFlyingDown),
            State('flyingUp', self.enterFlyingUp, self.exitFlyingUp),
            State('lured', self.enterLured, self.exitLured)
        ], 'off', 'off')
        self.stateIndex2suitState = {}
        self.suitFSM.enterInitialState()
        self.makeStateDict()

    def setChaseTarget(self, avId):
        if avId != base.localAvatar.doId:
            if self.chaseTarget == base.localAvatar.doId:
                messenger.send(PCTMM.getCogLostTargetEvent())
        else:
            messenger.send(PCTMM.getCogChasingEvent())

        self.chaseTarget = avId

    def setWalkPath(self, path, timestamp):
        elapsedT = globalClockDelta.localElapsedTime(timestamp)
        self.suitFSM.request('walking', [path, elapsedT])

    def showAvId(self):
        self.setDisplayName(self.getName() + "\n" + str(self.doId))

    def showName(self):
        self.setDisplayName(self.getName())

    def setDisplayName(self, name):
        self.setupNameTag(tempName=name)

    def enterWalking(self, path, elapsedT):
        # path: A list of point2s.
        #
        # We will make a sequence of NPCWalkIntervals for each point2 in the path.

        self.clearMoveTrack()
        self.moveIval = getMoveIvalFromPath(self, path, elapsedT, True,
                                            'suitMoveIval')
        self.moveIval.start(elapsedT)

    def clearMoveTrack(self):
        if self.moveIval:
            self.ignore(self.moveIval.getDoneEvent())
            self.moveIval.pause()
            self.moveIval = None
        if not self.isDead():
            self.animFSM.request('neutral')

    def exitWalking(self):
        self.clearMoveTrack()
        if not self.isDead():
            self.animFSM.request('neutral')

    def enterFlyingDown(self, startIndex, endIndex, ts=0.0):
        if self.getHood() != '' and startIndex != -1 and endIndex != -1:
            duration = 3.5
            startPoint = CIGlobals.SuitSpawnPoints[
                self.getHood()].keys()[startIndex]
            startPos = CIGlobals.SuitSpawnPoints[
                self.getHood()][startPoint] + (0, 0, 6.5 * 4.8)
            endPoint = CIGlobals.SuitSpawnPoints[
                self.getHood()].keys()[endIndex]
            endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]
            self.stopMoving(finish=1)
            groundF = 28
            dur = self.getDuration('land')
            fr = self.getFrameRate('land')
            if fr:
                animTimeInAir = groundF / fr
            else:
                animTimeInAir = groundF
            impactLength = dur - animTimeInAir
            timeTillLanding = 6.5 - impactLength
            self.moveIval = LerpPosInterval(self,
                                            duration=timeTillLanding,
                                            pos=endPos,
                                            startPos=startPos,
                                            fluid=1)
            self.moveIval.start(ts)
        self.animFSM.request('flyDown', [ts])

    def exitFlyingDown(self):
        self.stopMoving(finish=1)
        self.animFSM.request('neutral')

    def enterFlyingUp(self, startIndex, endIndex, ts=0.0):
        if self.getHood() != '':
            duration = 3
            if startIndex > -1:
                startPoint = CIGlobals.SuitSpawnPoints[
                    self.getHood()].keys()[startIndex]
                startPos = CIGlobals.SuitSpawnPoints[
                    self.getHood()][startPoint]
            else:
                startPos = self.getPos(render)
            if endIndex > -1:
                endPoint = CIGlobals.SuitSpawnPoints[
                    self.getHood()].keys()[endIndex]
                endPos = CIGlobals.SuitSpawnPoints[
                    self.getHood()][endPoint] + (0, 0, 6.5 * 4.8)
            else:
                endPos = self.getPos(render) + (0, 0, 6.5 * 4.8)

            self.stopMoving(finish=1)
            groundF = 28
            dur = self.getDuration('land')
            fr = self.getFrameRate('land')
            if fr:
                animTimeInAir = groundF / fr
            else:
                animTimeInAir = groundF
            impactLength = dur - animTimeInAir
            timeTillLanding = 6.5 - impactLength
            self.moveIval = Sequence(
                Wait(impactLength),
                LerpPosInterval(self,
                                duration=timeTillLanding,
                                pos=endPos,
                                startPos=startPos,
                                fluid=1))
            self.moveIval.start(ts)
        self.animFSM.request('flyAway', [ts, 1])

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

    def enterLured(self, _, __, ___):
        self.loop('lured')

    def exitLured(self):
        self.stop()

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

    def exitSuitOff(self):
        pass

    def setName(self, name):
        Suit.setName(self, name, self.suitPlan.getName())

    def setLevel(self, level):
        self.level = level
        if self.level == 12:
            self.maxHealth = 200
        elif self.level > 0:
            self.maxHealth = (self.level + 1) * (self.level + 2)
        else:
            self.maxHealth = 1
        self.health = self.maxHealth
        self.updateHealthBar(self.health)

    def getLevel(self):
        return self.level

    def startMoveInterval(self, startX, startY, startZ, endX, endY, endZ,
                          duration):
        self.stopMoving()
        endPos = Point3(endX, endY, endZ)
        self.moveIval = NPCWalkInterval(self,
                                        endPos,
                                        durationFactor=duration,
                                        fluid=1)
        self.moveIval.start()

    def stopMoveInterval(self, andTurnAround=0):
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        if andTurnAround == 1:
            if self.health > 0:
                self.animFSM.request('neutral')
            self.setH(self.getH() - 180)

    def toggleRay(self, ray=1):
        if ray:
            Suit.initializeRay(self, self.avatarType, 2)
        else:
            Suit.disableRay(self)

    def startProjInterval(self,
                          startX,
                          startY,
                          startZ,
                          endX,
                          endY,
                          endZ,
                          duration,
                          gravityMult,
                          ts=0):
        if isinstance(ts, int) and ts != 0:
            ts = globalClockDelta.localElapsedTime(ts)

        self.disableRay()
        self.stopMoveInterval()
        startPos = Point3(startX, startY, startZ)
        endPos = Point3(endX, endY, endZ)
        oldHpr = self.getHpr(render)
        self.headsUp(endPos)
        newHpr = self.getHpr(render)
        self.setHpr(oldHpr)
        self.moveIval = Parallel(
            LerpHprInterval(self,
                            duration=0.5,
                            hpr=newHpr,
                            startHpr=oldHpr,
                            blendType='easeInOut'),
            Sequence(Func(self.animFSM.request, 'flyAway', [ts]), Wait(3.5),
                     Func(self.animFSM.request, 'flyDown', [1.0])),
            Sequence(
                Wait(2.0), Func(self.headsUp, endPos),
                ProjectileInterval(self,
                                   startPos=startPos,
                                   endPos=endPos,
                                   gravityMult=gravityMult,
                                   duration=duration)))
        self.moveIval.start(ts)

    def startPosInterval(self,
                         startX,
                         startY,
                         startZ,
                         endX,
                         endY,
                         endZ,
                         duration,
                         blendType,
                         ts=0.0):
        if ts != 0.0:
            ts = globalClockDelta.localElapsedTime(ts)
        self.stopMoveInterval()
        startPos = Point3(startX, startY, startZ)
        endPos = Point3(endX, endY, endZ)
        self.moveIval = LerpPosInterval(self,
                                        duration=duration,
                                        pos=endPos,
                                        startPos=startPos,
                                        blendType=blendType)
        self.moveIval.start(ts)

    def stopMoving(self, finish=0):
        if self.moveIval:
            if finish:
                self.moveIval.finish()
            else:
                self.moveIval.pause()
            self.moveIval = None

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

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

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

    def setHealth(self, health):
        if health > self.health:
            # We got an hp boost. Flash green.
            flashColor = VBase4(0, 1, 0, 1)
        elif health < self.health:
            # We got an hp loss. Flash red.
            flashColor = VBase4(1, 0, 0, 1)
        DistributedAvatar.setHealth(self, health)

        def doBossFlash():
            if not self.isEmpty():
                LerpColorScaleInterval(self, 0.2, flashColor).start()

        def clearBossFlash():
            if not self.isEmpty():
                self.clearColorScale()

        if self.isDead():
            self.setChaseTarget(0)
            base.taskMgr.remove(self.uniqueName('monitorLocalAvDistance'))
            if self.isInRange:
                messenger.send(PCTMM.getCogOutOfRangeEvent())
                self.isInRange = False
            self.interruptAttack()

        if self.getLevel() > 12:
            if self.hpFlash:
                self.hpFlash.finish()
                self.hpFlash = None
            self.hpFlash = Sequence(Func(doBossFlash), Wait(0.2),
                                    Func(clearBossFlash))
            self.hpFlash.start()
        self.updateHealthBar(health)

    def announceHealth(self, level, hp):
        DistributedAvatar.announceHealth(self, level, hp)
        if level == 1:
            healthSfx = base.audio3d.loadSfx(SuitGlobals.healedSfx)
            base.audio3d.attachSoundToObject(healthSfx, self)
            SoundInterval(healthSfx, node=self).start()
            del healthSfx

    #
    #    'setSuit' sets the suit type and generates it.
    #    'arg' is an id for a SuitPlan as defined in SuitBank or
    #        an instance of SuitPlan.
    #    'variant' is an optional argument that sets the variant.
    #        It takes an id for the variant or an instance of Variant.
    #        Default is Variant.NORMAL.

    def setSuit(self, arg, variant=0):
        if isinstance(arg, SuitPlan):
            plan = arg
        else:
            plan = SuitBank.getSuitById(arg)

        voice = Voice.NORMAL
        if variant:
            if isinstance(variant, (int, long, float, complex)):
                variant = Variant.getVariantById(variant)

        if plan.getForcedVoice():
            voice = plan.getForcedVoice()

        Suit.generate(self, plan, variant, voice=voice)
        self.suitPlan = plan
        self.variant = Variant.getVariantById(variant)

    def getSuit(self):
        return tuple((self.suitPlan, self.variant))

    def spawn(self, startIndex, endIndex, spawnMode=SpawnMode.FLYDOWN):
        if spawnMode == SpawnMode.FLYDOWN:
            startPoint = CIGlobals.SuitSpawnPoints[
                self.getHood()].keys()[startIndex]
            startPos = CIGlobals.SuitSpawnPoints[
                self.getHood()][startPoint] + (0, 0, 50)
            endPoint = CIGlobals.SuitSpawnPoints[
                self.getHood()].keys()[endIndex]
            endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]
            if self.moveIval:
                self.moveIval.finish()
                self.moveIval = None
            self.moveIval = LerpPosInterval(self,
                                            duration=3,
                                            pos=endPos,
                                            startPos=startPos,
                                            fluid=1)

    def makeStateDict(self):
        self.stateIndex2suitState = {
            0: self.suitFSM.getStateNamed('off'),
            1: self.suitFSM.getStateNamed('walking'),
            2: self.suitFSM.getStateNamed('flyingDown'),
            3: self.suitFSM.getStateNamed('flyingUp'),
            4: self.suitFSM.getStateNamed('lured')
        }
        self.suitState2stateIndex = {}
        for stateId, state in self.stateIndex2suitState.items():
            self.suitState2stateIndex[state.getName()] = stateId

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

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

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

    def getSuitState(self):
        return self.suitState

    def setAnimState(self, anim, loop=1, timestamp=None):
        prevAnim = self.anim
        self.anim = anim

        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)

        if type(anim) == types.IntType:
            if anim != 44 and anim != 45:
                anim = SuitGlobals.getAnimById(anim)
                animName = anim.getName()
            elif anim == 44:
                animName = 'die'
            elif anim == 45:
                animName = 'flyNeutral'
        elif type(anim) == types.StringType:
            animName = anim

        if self.animFSM.hasStateNamed(animName):
            self.animFSM.request(animName, [ts])
        else:
            if loop:
                self.loop(animName)
            else:
                self.play(animName)
        messenger.send(SuitGlobals.animStateChangeEvent % (self.uniqueName),
                       [anim, prevAnim])

    def doAttack(self, attackId, avId, timestamp=None):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        attackName = SuitAttacks.SuitAttackLengths.keys()[attackId]
        attackTaunt = CIGlobals.SuitAttackTaunts[attackName][random.randint(
            0,
            len(CIGlobals.SuitAttackTaunts[attackName]) - 1)]
        avatar = self.cr.doId2do.get(avId)
        shouldChat = 0
        if self.suitPlan in [SuitBank.VicePresident, SuitBank.LucyCrossbill]:
            shouldChat = random.randint(0, 2)
        if shouldChat == 0:
            self.setChat(attackTaunt)

        self.animFSM.request('attack', [attackName, avatar, 0.0])

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

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

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

    def __monitorLocalAvDistance(self, task):
        if self.getDistance(base.localAvatar) <= PCTMM.getCogInRangeDistance(
        ) and not self.isInRange:
            self.isInRange = True
            messenger.send(PCTMM.getCogInRangeEvent())
        elif self.getDistance(base.localAvatar) > PCTMM.getCogInRangeDistance(
        ) and self.isInRange:
            self.isInRange = False
            messenger.send(PCTMM.getCogOutOfRangeEvent())

        return task.cont

    def announceGenerate(self):
        DistributedAvatar.announceGenerate(self)
        self.setAnimState('neutral')
        base.taskMgr.add(self.__monitorLocalAvDistance,
                         self.uniqueName('monitorLocalAvDistance'))

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

    def disable(self):
        base.taskMgr.remove(self.uniqueName('monitorLocalAvDistance'))
        self.anim = None
        self._state = None
        self.dept = None
        self.variant = None
        self.suitPlan = None
        if self.hpFlash:
            self.hpFlash.finish()
            self.hpFlash = None
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        Suit.disable(self)
        DistributedAvatar.disable(self)

    def delete(self):
        Suit.delete(self)
        del self.anim
        del self._state
        del self.dept
        del self.variant
        del self.suitPlan
        del self.moveIval
        DistributedAvatar.delete(self)
        DistributedSmoothNode.delete(self)
class DistributedEagleSuit(DistributedSuit):
    notify = directNotify.newCategory('DistributedEagleSuit')

    def __init__(self, cr):
        DistributedSuit.__init__(self, cr)
        self.eagleCry = base.audio3d.loadSfx('phase_5/audio/sfx/tt_s_ara_cfg_eagleCry.mp3')
        base.audio3d.attachSoundToObject(self.eagleCry, self)
        self.fallWhistle = base.audio3d.loadSfx('phase_5/audio/sfx/incoming_whistleALT.mp3')
        base.audio3d.attachSoundToObject(self.fallWhistle, self)
        self.explode = base.audio3d.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3')
        base.audio3d.attachSoundToObject(self.explode, self)
        self.eventSphereNodePath = None
        self.fallingPropeller = None
        self.fallingPropProjectile = None
        self.mg = None
        self.flySpeed = 0.0
        return

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

    def makeStateDict(self):
        self.suitFSM.addState(State('eagleFly', self.enterEagleFly, self.exitEagleFly))
        self.suitFSM.addState(State('eagleFall', self.enterEagleFall, self.exitEagleFall))
        self.stateIndex2suitState = {0: self.suitFSM.getStateNamed('off'),
         1: self.suitFSM.getStateNamed('walking'),
         2: self.suitFSM.getStateNamed('flyingDown'),
         3: self.suitFSM.getStateNamed('flyingUp'),
         4: self.suitFSM.getStateNamed('lured'),
         5: self.suitFSM.getStateNamed('eagleFly'),
         6: self.suitFSM.getStateNamed('eagleFall')}
        self.suitState2stateIndex = {}
        for stateId, state in self.stateIndex2suitState.items():
            self.suitState2stateIndex[state.getName()] = stateId

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

    def getFlySpeed(self):
        return self.flySpeed

    def enterEagleFly(self, startIndex, endIndex, ts = 0.0):
        durationFactor = self.getFlySpeed()
        if startIndex > -1:
            startPos = EGG.EAGLE_FLY_POINTS[startIndex]
        else:
            startPos = self.getPos(render)
        endPos = EGG.EAGLE_FLY_POINTS[endIndex]
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        self.moveIval = NPCWalkInterval(self, endPos, durationFactor=durationFactor, startPos=startPos, fluid=1)
        self.moveIval.start(ts)
        return

    def exitEagleFly(self):
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        return

    def enterEagleFall(self, startIndex, endIndex, ts = 0.0):
        self.moveIval = LerpPosInterval(self, duration=4.0, pos=self.getPos(render) - (0, 0, 75), startPos=self.getPos(render), blendType='easeIn')
        self.moveIval.start(ts)

    def exitEagleFall(self):
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        return

    def fallAndExplode(self):
        self.cleanupPropeller()
        self.fallingPropeller = Actor('phase_4/models/props/propeller-mod.bam', {'chan': 'phase_4/models/props/propeller-chan.bam'})
        self.fallingPropeller.reparentTo(render)
        self.fallingPropeller.loop('chan', fromFrame=0, toFrame=3)
        parentNode = self.attachNewNode('fallingPropParentNode')
        h = random.randint(0, 359)
        parentNode.setH(h)
        dummyNode = parentNode.attachNewNode('dummyNode')
        dummyNode.setPos(0, 10, -50)
        self.fallingPropProjectile = FlightProjectileInterval(self.fallingPropeller, startPos=self.find('**/joint_head').getPos(render), endPos=dummyNode.getPos(render), duration=5.0, gravityMult=0.25)
        self.fallingPropProjectile.start()
        dummyNode.removeNode()
        del dummyNode
        parentNode.removeNode()
        del parentNode
        self.updateHealthBar(0)
        self.ignoreHit()
        self.fallWhistle.play()
        taskMgr.doMethodLater(4.0, self.doExplodeSound, self.uniqueName('DEagleSuit-doExplodeSound'))

    def doExplodeSound(self, task):
        self.explode.play()
        return Task.done

    def __initializeEventSphere(self):
        sphere = CollisionSphere(0, 0, 0, 2)
        sphere.setTangible(0)
        node = CollisionNode(self.uniqueName('DEagleSuit-eventSphere'))
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        np = self.attachNewNode(node)
        np.setSz(2.5)
        np.setZ(5.5)
        self.eventSphereNodePath = np

    def removeEventSphere(self):
        if self.eventSphereNodePath:
            self.eventSphereNodePath.removeNode()
            self.eventSphereNodePath = None
        return

    def acceptHit(self):
        self.acceptOnce('enter' + self.eventSphereNodePath.node().getName(), self.__handleHit)

    def ignoreHit(self):
        self.ignore('enter' + self.eventSphereNodePath.node().getName())

    def __handleHit(self, entry):
        messenger.send(EGG.EAGLE_HIT_EVENT, [self.doId])

    def setSuit(self, arg, variant):
        DistributedSuit.setSuit(self, arg, 3)
        self.deleteShadow()
        self.disableBodyCollisions()
        self.disableRay()
        self.__initializeEventSphere()
        self.show()
        self.setAnimState('flyNeutral')

    def __doEagleCry(self, task):
        self.eagleCry.play()
        task.delayTime = random.uniform(3, 30)
        return Task.again

    def announceGenerate(self):
        DistributedSuit.announceGenerate(self)
        taskMgr.doMethodLater(random.uniform(5, 25), self.__doEagleCry, self.uniqueName('DEagleSuit-doEagleCry'))
        self.acceptHit()

    def disable(self):
        self.ignoreHit()
        self.removeEventSphere()
        taskMgr.remove(self.uniqueName('DEagleSuit-doExplodeSound'))
        taskMgr.remove(self.uniqueName('DEagleSuit-doEagleCry'))
        if self.fallingPropProjectile:
            self.fallingPropProjectile.finish()
            self.fallingPropProjectile = None
        if self.fallingPropeller:
            self.fallingPropeller.cleanup()
            self.fallingPropeller = None
        base.audio3d.detachSound(self.fallWhistle)
        del self.fallWhistle
        base.audio3d.detachSound(self.explode)
        del self.explode
        base.audio3d.detachSound(self.eagleCry)
        del self.eagleCry
        self.mg = None
        DistributedSuit.disable(self)
        return
class ParticleAttack(Attack):
    notify = directNotify.newCategory('ParticleAttack')
    attack = 'particleattack'
    particleIvalDur = 1
    shooterDistance = 50

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.particles = []
        self.handObj = None
        self.shootOutCollNP = None
        self.particleSound = None
        self.particleMoveIval = None
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        return

    def handleWeaponTouch(self):
        pass

    def handleCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def doAttack(self, particlePaths, track_name, particleCollId, animation_name, delayUntilRelease, animationSpeed=1, handObjPath=None, handObjParent=None, startRightAway=True, ts=0):
        for path in particlePaths:
            particle = ParticleLoader.loadParticleEffect(path)
            self.particles.append(particle)

        sphere = CollisionSphere(0, 0, 0, 1)
        sphere.setTangible(0)
        node = CollisionNode(particleCollId)
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        if len(self.particles) == 1:
            self.shootOutCollNP = self.particles[0].attachNewNode(node)
        else:
            self.shootOutCollNP = self.suit.attachNewNode(node)
        if handObjPath and handObjParent:
            self.handObj = loader.loadModel(handObjPath)
            self.handObj.reparentTo(handObjParent)
        self.suit.setPlayRate(animationSpeed, animation_name)
        self.suit.play(animation_name)
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            particleCollId = self.suit.uniqueName(particleCollId)
        self.suitTrack = Sequence(name=track_name)
        self.suitTrack.append(Wait(delayUntilRelease))
        self.suitTrack.append(Func(self.releaseAttack))
        self.suitTrack.append(Wait(self.particleIvalDur))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        if startRightAway:
            self.suitTrack.start(ts)

    def releaseAttack(self, releaseFromJoint, onlyMoveColl=True, blendType='noBlend'):
        startNP = releaseFromJoint.attachNewNode('startNP')
        if None not in [self.targetX, self.targetY, self.targetZ]:
            startNP.lookAt(render, self.targetX, self.targetY, self.targetZ + 2)
            pathNP = NodePath('path')
            pathNP.reparentTo(startNP)
            pathNP.setScale(render, 1.0)
            pathNP.setPos(0, self.shooterDistance, 0)
            for particle in self.particles:
                if not onlyMoveColl:
                    particle.start(render)
                else:
                    particle.start(self.suit)
                particle.lookAt(pathNP)
                if self.attack == 'razzledazzle':
                    particle.setP(particle, 90)

            if onlyMoveColl:
                target = self.shootOutCollNP
                target.wrtReparentTo(render)
            else:
                target = self.particles[0]
            self.particleMoveIval = LerpPosInterval(target, duration=self.particleIvalDur, pos=pathNP.getPos(render), startPos=startNP.getPos(render), blendType=blendType)
            self.particleMoveIval.start()
            self.acceptOnce('enter' + self.shootOutCollNP.node().getName(), self.handleCollision)
            pathNP.removeNode()
            startNP.removeNode()
            del pathNP
            del startNP
        self.playParticleSound()
        return

    def playParticleSound(self):
        if self.particleSound:
            base.audio3d.attachSoundToObject(self.particleSound, self.suit)
            base.playSfx(self.particleSound)

    def cleanup(self):
        Attack.cleanup(self)
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        if self.particles:
            for particle in self.particles:
                particle.cleanup()

        self.particles = None
        if self.handObj:
            self.handObj.removeNode()
            self.handObj = None
        if self.shootOutCollNP:
            self.ignore('enter' + self.shootOutCollNP.node().getName())
            self.shootOutCollNP.removeNode()
            self.shootOutCollNP = None
        if self.particleMoveIval:
            self.particleMoveIval.pause()
            self.particleMoveIval = None
        self.particleSound = None
        self.particleIvalDur = None
        return
class HangUpAttack(Attack):
    notify = directNotify.newCategory('HangUpAttack')
    attack = 'hangup'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.phone = None
        self.receiver = None
        self.collNP = None
        self.phoneSfx = None
        self.hangupSfx = None
        self.shootIval = None
        self.cord = None
        self.receiverOutCord = None
        self.phoneOutCord = None
        return

    def loadAttack(self):
        self.phone = loader.loadModel('phase_3.5/models/props/phone.bam')
        self.phone.setHpr(0, 0, 180)
        if self.suit.type == 'B':
            self.phone.setPos(0.7, 0.15, 0)
        else:
            if self.suit.type == 'C':
                self.phone.setPos(0.25, 0, 0)
        self.receiver = loader.loadModel('phase_3.5/models/props/receiver.bam')
        self.receiver.reparentTo(self.phone)
        self.cord = Rope()
        self.cord.ropeNode.setUseVertexColor(1)
        self.cord.ropeNode.setUseVertexThickness(1)
        self.cord.setup(3, ({'node': self.phone, 'point': (0.8, 0, 0.2), 'color': (0, 0, 0, 1), 'thickness': 1000}, {'node': self.phone, 'point': (2, 0, 0), 'color': (0, 0, 0, 1), 'thickness': 1000}, {'node': self.receiver, 'point': (1.1, 0.25, 0.5), 'color': (0, 0, 0, 1), 'thickness': 1000}), [])
        self.cord.setH(180)
        self.phoneSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup.ogg')
        base.audio3d.attachSoundToObject(self.phoneSfx, self.phone)
        self.hangupSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/SA_hangup_place_down.ogg')
        base.audio3d.attachSoundToObject(self.hangupSfx, self.phone)
        collSphere = CollisionSphere(0, 0, 0, 2)
        collSphere.setTangible(0)
        collNode = CollisionNode('phone_shootout')
        collNode.addSolid(collSphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        self.collNP = self.phone.attachNewNode(collNode)

    def doAttack(self, ts=0):
        self.loadAttack()
        if hasattr(self.suit, 'uniqueName'):
            name = self.suit.uniqueName('doHangupAttack')
        else:
            name = 'doHangupAttack'
        if self.suit.type == 'A':
            delay2playSound = 1.0
            delayAfterSoundToPlaceDownReceiver = 0.2
            delayAfterShootToIgnoreCollisions = 1.0
            delay2PickUpReceiver = 1.0
            receiverInHandPos = Point3(-0.5, 0.5, -1)
        else:
            if self.suit.type == 'B':
                delay2playSound = 1.5
                delayAfterSoundToPlaceDownReceiver = 0.7
                delayAfterShootToIgnoreCollisions = 1.0
                delay2PickUpReceiver = 1.5
                receiverInHandPos = Point3(-0.3, 0.5, -0.8)
            else:
                if self.suit.type == 'C':
                    delay2playSound = 1.0
                    delayAfterSoundToPlaceDownReceiver = 1.15
                    delayAfterShootToIgnoreCollisions = 1.0
                    delay2PickUpReceiver = 1.5
                    receiverInHandPos = Point3(-0.3, 0.5, -0.8)
        self.suitTrack = Parallel(name=name)
        self.suitTrack.append(ActorInterval(self.suit, 'phone'))
        self.suitTrack.append(Sequence(Wait(delay2playSound), SoundInterval(self.phoneSfx, duration=2.1), Wait(delayAfterSoundToPlaceDownReceiver), Func(self.receiver.setPos, 0, 0, 0), Func(self.receiver.setH, 0.0), Func(self.receiver.reparentTo, self.phone), Func(self.acceptOnce, 'enter' + self.collNP.node().getName(), self.handleCollision), Func(self.shootOut), Parallel(SoundInterval(self.hangupSfx), Sequence(Wait(delayAfterShootToIgnoreCollisions), Func(self.ignore, 'enter' + self.collNP.node().getName())))))
        self.suitTrack.append(Sequence(Func(self.phone.reparentTo, self.suit.find('**/joint_Lhold')), Func(self.cord.reparentTo, render), Wait(delay2PickUpReceiver), Func(self.receiver.reparentTo, self.suit.find('**/joint_Rhold')), Func(self.receiver.setPos, receiverInHandPos), Func(self.receiver.setH, 270.0)))
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, name)
        self.suitTrack.start(ts)

    def handleCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)

    def shootOut(self):
        pathNode = NodePath('path')
        pathNode.reparentTo(self.suit)
        pathNode.setPos(0, 50, self.phone.getZ(self.suit))
        self.collNP.reparentTo(render)
        self.shootIval = LerpPosInterval(self.collNP, duration=1.0, pos=pathNode.getPos(render), startPos=self.phone.getPos(render))
        self.shootIval.start()
        pathNode.removeNode()
        del pathNode

    def cleanup(self):
        Attack.cleanup(self)
        if self.shootIval:
            self.shootIval.pause()
            self.shootIval = None
        if self.cord:
            self.cord.removeNode()
            self.cord = None
        if self.phone:
            self.phone.removeNode()
            self.phone = None
        if self.receiver:
            self.receiver.removeNode()
            self.receiver = None
        if self.collNP:
            self.collNP.node().clearSolids()
            self.collNP.removeNode()
            self.collNP = None
        if self.phoneSfx:
            self.phoneSfx.stop()
            self.phoneSfx = None
        return
class ThrowAttack(Attack):
    notify = directNotify.newCategory('ThrowAttack')
    attack = 'throw'

    def __init__(self, attacksClass, suit):
        Attack.__init__(self, attacksClass, suit)
        self.weapon_state = None
        self.weapon = None
        self.wss = None
        self.wsnp = None
        self.suitTrack = None
        self.weaponSfx = None
        self.throwTrajectory = None
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        self.startNP = None
        return

    def handleWeaponCollision(self, entry):
        if self.suit:
            self.suit.sendUpdate('toonHitByWeapon', [self.getAttackId(self.attack), base.localAvatar.doId])
            base.localAvatar.b_handleSuitAttack(self.getAttackId(self.attack), self.suit.doId)
            self.suit.b_handleWeaponTouch()

    def doAttack(self, weapon_path, weapon_scale, track_name, animation_name, collsphere_radius, weapon_coll_id, weapon_h=0, weapon_p=0, weapon_r=0, weapon_x=0, weapon_y=0, weapon_z=0, ts=0):
        self.weapon_state = 'start'
        if hasattr(self.suit, 'uniqueName'):
            track_name = self.suit.uniqueName(track_name)
            weapon_coll_id = self.suit.uniqueName(weapon_coll_id)
        self.weapon = loader.loadModel(weapon_path)
        self.weapon.setScale(weapon_scale)
        self.weapon.setHpr(weapon_h, weapon_p, weapon_r)
        self.weapon.setPos(weapon_x, weapon_y, weapon_z)
        self.wss = CollisionSphere(0, 0, 0, collsphere_radius)
        self.wss.setTangible(0)
        self.targetX = self.attacksClass.target.getX(render)
        self.targetY = self.attacksClass.target.getY(render)
        self.targetZ = self.attacksClass.target.getZ(render)
        self.suitTrack = Sequence(name=track_name)
        if self.attack not in ('glowerpower', ):
            self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
            self.suitTrack.append(Wait(1.2))
            self.suitTrack.append(Func(self.suit.setPlayRate, 1.0, animation_name))
            if self.suit.type == 'C':
                self.suitTrack.append(Wait(0))
            else:
                self.suitTrack.append(Wait(0.7))
            self.suit.setPlayRate(2.0, animation_name)
            self.suitTrack.append(Func(self.throwObject))
            self.suitTrack.append(Wait(1.0))
            self.suitTrack.append(Func(self.delWeapon))
        else:
            self.suitTrack.append(Wait(1))
            self.suitTrack.append(Func(self.throwObject))
            self.suitTrack.append(Wait(0.5))
            self.suitTrack.append(Func(self.delWeapon))
        self.suit.play(animation_name)
        wsnode = CollisionNode(weapon_coll_id)
        wsnode.addSolid(self.wss)
        wsnode.setCollideMask(CIGlobals.WallBitmask)
        self.wsnp = self.weapon.attachNewNode(wsnode)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.finishedAttack)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self.suit, track_name)
        self.suitTrack.start(ts)

    def playWeaponSound(self):
        if self.weapon and self.weaponSfx:
            base.audio3d.attachSoundToObject(self.weaponSfx, self.suit)
            self.weaponSfx.play()

    def throwObject(self, projectile=True):
        if not self.weapon:
            return
        self.acceptOnce('enter' + self.wsnp.node().getName(), self.handleWeaponCollision)
        self.playWeaponSound()
        if self.weapon:
            self.weapon.wrtReparentTo(render)
            self.weapon.setHpr(Vec3(0, 0, 0))
        if self.attack not in ('glowerpower', ):
            parent = self.suit.find('**/joint_Rhold')
        else:
            parent = self.suit.find('**/joint_head')
        startNP = parent.attachNewNode('startNp')
        startNP.lookAt(render, self.targetX, self.targetY, self.targetZ)
        pathNP = NodePath('throwPath')
        pathNP.reparentTo(startNP)
        pathNP.setScale(render, 1.0)
        pathNP.setPos(0, 50, 0)
        if self.attack in ('clipontie', 'powertie', 'halfwindsor'):
            self.weapon.setHpr(pathNP.getHpr(render))
        if projectile == True:
            self.throwTrajectory = ProjectileInterval(self.weapon, startPos=self.suit.find('**/joint_Rhold').getPos(render), endPos=pathNP.getPos(render), gravityMult=0.7, duration=1.0)
        else:
            self.weapon.setH(pathNP.getH(render))
            self.throwTrajectory = LerpPosInterval(self.weapon, duration=0.5, pos=pathNP.getPos(render), startPos=startNP.getPos(render) + (0,
                                                                                                                                            3,
                                                                                                                                            0))
        self.throwTrajectory.start()
        self.weapon_state = 'released'
        startNP.removeNode()
        del startNP
        pathNP.removeNode()
        del pathNP

    def interruptAttack(self):
        if self.throwTrajectory:
            if self.throwTrajectory.isStopped():
                self.delWeapon()

    def handleWeaponTouch(self):
        if self.throwTrajectory:
            self.throwTrajectory.pause()
            self.throwTrajectory = None
        self.delWeapon()
        return

    def delWeapon(self):
        if self.weapon:
            self.weapon.removeNode()
            self.weapon = None
        return

    def cleanup(self):
        Attack.cleanup(self)
        self.targetX = None
        self.targetY = None
        self.targetZ = None
        self.weapon_state = None
        if self.weaponSfx:
            self.weaponSfx.stop()
            self.weaponSfx = None
        if self.throwTrajectory:
            self.throwTrajectory.pause()
            self.throwTrajectory = None
        self.delWeapon()
        self.wss = None
        if self.wsnp:
            self.wsnp.node().clearSolids()
            self.wsnp.removeNode()
            self.wsnp = None
        return