Exemplo n.º 1
0
    def loadAssets(self):
        """Load our assets."""
        self.root = render.attachNewNode('golfSpot-%d' % self.index)
        self.root.setPos(*self.positions[self.index])
        self.ballModel = loader.loadModel('phase_6/models/golf/golf_ball')
        self.ballColor = VBase4(1, 1, 1, 1)
        if self.index < len(GolfGlobals.PlayerColors):
            self.ballColor = VBase4(*GolfGlobals.PlayerColors[self.index])
            self.ballModel.setColorScale(self.ballColor)
        self.ballModel.reparentTo(self.root)
        self.club = loader.loadModel('phase_6/models/golf/putter')
        self.clubLookatSpot = self.root.attachNewNode('clubLookat')
        self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1))

        # create a collision sphere to trigger when we touch the ball

        # Make a trigger sphere so we can detect when the local avatar
        # runs up to the controls.  We bury the sphere mostly under
        # the floor to minimize accidental collisions.
        cs = CollisionSphere(0, 0, 0, 1)
        cs.setTangible(0)
        cn = CollisionNode(self.triggerName)
        cn.addSolid(cs)
        cn.setIntoCollideMask(ToontownGlobals.WallBitmask)
        self.trigger = self.root.attachNewNode(cn)
        self.trigger.stash()

        self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.mp3')
    def _initCollisions(self):
        name = "CogdoMazeLock-%d" % self.id
        collSphere = CollisionSphere(0, 0, 0.0, 0.25)
        collSphere.setTangible(0)
        collNode = CollisionNode(name)
        collNode.setFromCollideMask(ToontownGlobals.CatchGameBitmask)
        collNode.addSolid(collSphere)
        self.model.attachNewNode(collNode)

        self.enterCollisionEventName = "enter" + name
Exemplo n.º 3
0
 def createThrowGag(self, gag):
     throwGag = CogdoMazePlayer.createThrowGag(self, gag)
     collSphere = CollisionSphere(0, 0, 0, 0.5)
     collSphere.setTangible(0)
     name = Globals.GagCollisionName
     collNode = CollisionNode(name)
     collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
     collNode.addSolid(collSphere)
     colNp = throwGag.attachNewNode(collNode)
     base.cTrav.addCollider(colNp, self.gagHandler)
     return throwGag
Exemplo n.º 4
0
    def pieThrow(self, avId, timestamp, heading, pos, power):
        """Show local or remote toon throwing a pie."""

        toon = self.activity.getAvatar(avId)

        if toon is None:
            return

        tossTrack, pieTrack, flyPie = self.getTossPieInterval(
            toon, pos[0], pos[1], pos[2], heading, 0, 0, power)

        if avId == base.localAvatar.doId:
            flyPie.setTag('throwerId', str(avId))

            collSphere = CollisionSphere(0, 0, 0, 0.5)
            # Make the sphere intangible
            collSphere.setTangible(0)
            name = "PieSphere-%d" % avId
            collSphereName = self.activity.uniqueName(name)
            collNode = CollisionNode(collSphereName)
            collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
            collNode.addSolid(collSphere)
            collNP = flyPie.attachNewNode(collNode)

            base.cTrav.addCollider(collNP, self.pieHandler)

            self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName
            self.accept(self.toonPieEventNames[collNP],
                        self.handlePieCollision)
        else:
            player = self.players.get(avId)
            if player is not None:
                player.faceForward()

        def matchRunningAnim(toon=toon):
            toon.playingAnim = None
            toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed)

        newTossTrack = Sequence(tossTrack, Func(matchRunningAnim))

        pieTrack = Parallel(newTossTrack,
                            pieTrack,
                            name="PartyCogActivity.pieTrack-%d-%s" %
                            (avId, timestamp))

        elapsedTime = globalClockDelta.localElapsedTime(timestamp)

        if elapsedTime < 16. / 24.:
            elapsedTime = 16. / 24.  # make the pie fly immediately

        pieTrack.start(elapsedTime)

        self.pieIvals.append(pieTrack)
        self.toonPieTracks[avId] = pieTrack
Exemplo n.º 5
0
 def __initCollisions(self):
     collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius)
     collSphere.setTangible(0)
     self.mazeCollisionName = Globals.LocalPlayerCollisionName
     collNode = CollisionNode(self.mazeCollisionName)
     collNode.addSolid(collSphere)
     collNodePath = self.toon.attachNewNode(collNode)
     collNodePath.hide()
     handler = CollisionHandlerEvent()
     handler.addInPattern('%fn-into-%in')
     base.cTrav.addCollider(collNodePath, handler)
     self.handler = handler
     self._collNodePath = collNodePath
Exemplo n.º 6
0
 def setupHeadSphere(self, avatarNodePath):
     collSphere = CollisionSphere(0, 0, 0, 1)
     collSphere.setTangible(1)
     collNode = CollisionNode('Flyer.cHeadCollSphere')
     collNode.setFromCollideMask(ToontownGlobals.CeilingBitmask)
     collNode.setIntoCollideMask(BitMask32.allOff())
     collNode.addSolid(collSphere)
     self.cHeadSphereNodePath = avatarNodePath.attachNewNode(collNode)
     self.cHeadSphereNodePath.setZ(base.localAvatar.getHeight() + 1.0)
     self.headCollisionEvent = CollisionHandlerEvent()
     self.headCollisionEvent.addInPattern('%fn-enter-%in')
     self.headCollisionEvent.addOutPattern('%fn-exit-%in')
     base.cTrav.addCollider(self.cHeadSphereNodePath,
                            self.headCollisionEvent)
Exemplo n.º 7
0
 def setupEventSphere(self, bitmask, avatarRadius):
     self.avatarRadius = avatarRadius
     cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75,
                               self.avatarRadius * 1.04)
     cSphere.setTangible(0)
     cSphereNode = CollisionNode('Flyer.cEventSphereNode')
     cSphereNode.addSolid(cSphere)
     cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
     cSphereNode.setFromCollideMask(bitmask)
     cSphereNode.setIntoCollideMask(BitMask32.allOff())
     self.event = CollisionHandlerEvent()
     self.event.addInPattern('enter%in')
     self.event.addOutPattern('exit%in')
     self.cEventSphereNodePath = cSphereNodePath
Exemplo n.º 8
0
    def loadCollision( self ):
        collTube = CollisionTube(0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 5.4)
        collTube.setTangible(True)
        self.trampolineCollision = CollisionNode(self.uniqueName("TrampolineCollision"))
        self.trampolineCollision.addSolid(collTube)
        self.trampolineCollision.setCollideMask(OTPGlobals.CameraBitmask | OTPGlobals.WallBitmask)
        self.trampolineCollisionNP = self.tramp.attachNewNode(self.trampolineCollision)

        collSphere = CollisionSphere(0.0, 0.0, 0.0, 7.0)
        collSphere.setTangible(False)
        self.trampolineTrigger = CollisionNode(self.uniqueName("TrampolineTrigger"))
        self.trampolineTrigger.addSolid(collSphere)
        self.trampolineTrigger.setIntoCollideMask(OTPGlobals.WallBitmask)
        self.trampolineTriggerNP = self.tramp.attachNewNode(self.trampolineTrigger)
        self.accept( "enter%s" % self.uniqueName("TrampolineTrigger"), self.onTrampolineTrigger )
Exemplo n.º 9
0
    def generate(self):
        """generate(self)
        This method is called when the DistributedObject is reintroduced
        to the world, either for the first time or from the cache.
        """
        DistributedObject.DistributedObject.generate(self)

        loader = self.cr.playGame.hood.loader
        partyGate = loader.geom.find('**/partyGate_grp')
        if partyGate.isEmpty():
            self.notify.warning('Could not find partyGate_grp in loader.geom')
            return
        self.clockFlat = partyGate.find("**/clock_flat")
        collSphere = CollisionSphere(0, 0, 0, 6.9)
        collSphere.setTangible(1)
        self.partyGateSphere = CollisionNode("PartyGateSphere")
        self.partyGateSphere.addSolid(collSphere)
        self.partyGateCollNodePath = partyGate.find(
            "**/partyGate_stepsLocator").attachNewNode(self.partyGateSphere)
        self.__enableCollisions()
        #        self.tunnelOrigin = NodePath("PartyGateTunnelOrigin")
        #        self.tunnelOrigin.reparentTo(partyGate)
        #        self.tunnelOrigin.setPos(partyGate.find("**/clockText_locator").getPos() + Point3(0.0, 0.0, -12.0))

        self.toontownTimeGui = ServerTimeGui(partyGate,
                                             hourCallback=self.hourChange)
        self.toontownTimeGui.setPos(
            partyGate.find("**/clockText_locator").getPos() +
            Point3(0.0, 0.0, -0.2))
        self.toontownTimeGui.setHpr(
            partyGate.find("**/clockText_locator").getHpr())
        self.toontownTimeGui.setScale(12.0, 1.0, 26.0)
        self.toontownTimeGui.amLabel.setPos(-0.035, 0, -0.032)
        self.toontownTimeGui.amLabel.setScale(0.5)
        self.toontownTimeGui.updateTime()
        self.setupSignText()
Exemplo n.º 10
0
    def loadLever(self):
        """
        SubClasses can override this if they want to move their lever somewhere
        special... call this, then change the position.
        """
        self.lever = self.root.attachNewNode('%sLever' % self.activityName)
        self.leverModel = self.party.defaultLeverModel.copyTo(self.lever)
        # Do some crazy reparenting so you can scale the whole thing nicely
        self.controlColumn = NodePath('cc')
        column = self.leverModel.find('**/column')
        column.getChildren().reparentTo(self.controlColumn)
        self.controlColumn.reparentTo(column)
        self.stickHinge = self.controlColumn.attachNewNode('stickHinge')
        self.stick = self.party.defaultStickModel.copyTo(self.stickHinge)
        self.stickHinge.setHpr(0.0, 90.0, 0.0)
        self.stick.setHpr(0, -90.0, 0)
        self.stick.flattenLight()
        self.bottom = self.leverModel.find('**/bottom')
        self.bottom.wrtReparentTo(self.controlColumn)
        self.bottomPos = self.bottom.getPos()

        # Make a trigger sphere so we can detect when the local avatar
        # runs up to the lever.
        cs = CollisionSphere(0.0, 1.35, 2.0, 1.0)
        cs.setTangible(False)
        cn = CollisionNode(self.leverTriggerEvent)
        cn.addSolid(cs)
        cn.setIntoCollideMask(OTPGlobals.WallBitmask)
        self.leverTrigger = self.root.attachNewNode(cn)
        self.leverTrigger.reparentTo(self.lever)
        self.leverTrigger.stash()

        # Also, a solid tube to keep us from running through the
        # lever itself.  This one scales with the control
        # model.
        cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2)
        cn = CollisionNode('levertube')
        cn.addSolid(cs)
        cn.setIntoCollideMask(OTPGlobals.WallBitmask)
        self.leverTube = self.leverModel.attachNewNode(cn)

        # Let's set the height of the lever to the height of the host
        host = base.cr.doId2do.get(self.party.partyInfo.hostId)
        if host is None:
            self.notify.debug(
                "%s loadLever : Host has left the game before lever could be created."
                % self.activityName)
            return

        # We start by figuring out where we are going by setting the
        # scale and position appropriately
#        origScale = self.leverModel.getSz()
#        origCcPos = self.controlColumn.getPos()
#        origBottomPos = self.bottom.getPos()
#        origStickHingeHpr = self.stickHinge.getHpr()

# First, scale the thing overall to match the host's scale,
# including cheesy effect scales.
        scale = host.getGeomNode().getChild(0).getSz(render)
        self.leverModel.setScale(scale)

        # Then get the position of the host's right hand when he's
        # standing at the controls in a leverNeutral pose.
        self.controlColumn.setPos(0, 0, 0)
        host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0)
        host.pose('leverNeutral', 0)
        host.update()
        pos = host.rightHand.getPos(self.controlColumn)

        # Now set the control column to the right height and position
        # to put the top of the stick approximately in his hand.
        self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1)

        # And put the bottom piece back on the floor, wherever that
        # is from here.
        self.bottom.setZ(host, 0.0)
        self.bottom.setPos(self.bottomPos[0], self.bottomPos[1],
                           self.bottom.getZ())

        # Also put the joystick in his hand.
        lookAtPoint = Point3(0.3, 0, 0.1)
        lookAtUp = Vec3(0, -1, 0)
        self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp)

        host.play('walk')
        host.update()
Exemplo n.º 11
0
 def _initCollisions(self):
     collSphere = CollisionSphere(0, 0, 0, 3.0)
     collSphere.setTangible(0)
     self.collNode = CollisionNode(self.getName())
     self.collNode.addSolid(collSphere)
     self.collNP = self.attachNewNode(self.collNode)
Exemplo n.º 12
0
class CogdoMazeWaterCooler(NodePath, DirectObject):
    UpdateTaskName = 'CogdoMazeWaterCooler_Update'

    def __init__(self, serialNum, model):
        NodePath.__init__(self, 'CogdoMazeWaterCooler-%i' % serialNum)
        self.serialNum = serialNum
        self._model = model
        self._model.reparentTo(self)
        self._model.setPosHpr(0, 0, 0, 0, 0, 0)
        self._initCollisions()
        self._initArrow()
        self._update = None
        self.__startUpdateTask()
        return

    def destroy(self):
        self.ignoreAll()
        self.__stopUpdateTask()
        self.collNodePath.removeNode()
        self.removeNode()

    def _initCollisions(self):
        offset = Globals.WaterCoolerTriggerOffset
        self.collSphere = CollisionSphere(offset[0], offset[1], offset[2], Globals.WaterCoolerTriggerRadius)
        self.collSphere.setTangible(0)
        name = Globals.WaterCoolerCollisionName
        self.collNode = CollisionNode(name)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.attachNewNode(self.collNode)

    def _initArrow(self):
        matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui')
        arrow = matchingGameGui.find('**/minnieArrow')
        arrow.setScale(Globals.CoolerArrowScale)
        arrow.setColor(*Globals.CoolerArrowColor)
        arrow.setPos(0, 0, Globals.CoolerArrowZ)
        arrow.setHpr(0, 0, 90)
        arrow.setBillboardAxis()
        self._arrow = NodePath('Arrow')
        arrow.reparentTo(self._arrow)
        self._arrow.reparentTo(self)
        self._arrowTime = 0
        self.accept(Globals.WaterCoolerShowEventName, self.showArrow)
        self.accept(Globals.WaterCoolerHideEventName, self.hideArrow)
        matchingGameGui.removeNode()

    def showArrow(self):
        self._arrow.unstash()

    def hideArrow(self):
        self._arrow.stash()

    def update(self, dt):
        newZ = math.sin(globalClock.getFrameTime() * Globals.CoolerArrowSpeed) * Globals.CoolerArrowBounce
        self._arrow.setZ(newZ)

    def __startUpdateTask(self):
        self.__stopUpdateTask()
        self._update = taskMgr.add(self._updateTask, self.UpdateTaskName, 45)

    def __stopUpdateTask(self):
        if self._update is not None:
            taskMgr.remove(self._update)
        return

    def _updateTask(self, task):
        dt = globalClock.getDt()
        self.update(dt)
        return Task.cont
Exemplo n.º 13
0
class IceTreasure(DirectObject):
    """
    Treasures toons can pickup swinging from ice to ice.  Based on MazeTreasure
    """

    notify = DirectNotifyGlobal.directNotify.newCategory("IceTreasure")

    RADIUS = 1.0

    def __init__(self, model, pos, serialNum, gameId, penalty=False):
        # there are going to be MANY (~650) of these created and destroyed
        # all at once for 4-player games; make it lean
        self.serialNum = serialNum

        self.penalty = penalty

        # the fruit has a bit of height, lets recenter
        center = model.getBounds().getCenter()
        center = Point3(0, 0, 0)
        self.nodePath = model.copyTo(render)
        self.nodePath.setPos(pos[0] - center[0], pos[1] - center[1],
                             pos[2] - center[2])
        self.nodePath.setZ(0)  # real assets have bottom at zero
        self.notify.debug('newPos = %s' % self.nodePath.getPos())
        #self.nodePath.setScale(1.0)

        #if self.penalty:
        #    self.nodePath.setColorScale(0.5,0.5,0.5,1.0)

        # Make a sphere, name it uniquely, and child it
        # to the nodepath.
        if self.penalty:
            self.sphereName = "penaltySphere-%s-%s" % (gameId, self.serialNum)
        else:
            self.sphereName = "treasureSphere-%s-%s" % (gameId, self.serialNum)
        self.collSphere = CollisionSphere(center[0], center[1], center[2],
                                          self.RADIUS)
        # Make the sphere intangible
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode(self.sphereName)
        self.collNode.setIntoCollideMask(ToontownGlobals.PieBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = render.attachNewNode(self.collNode)
        self.collNodePath.setPos(pos[0] - center[0], pos[1] - center[1],
                                 pos[2] - center[2])
        self.collNodePath.hide()
        self.track = None

        # Add a hook looking for collisions with localToon
        #self.accept('enter' + self.sphereName, self.__handleEnterSphere)

        # now that the treasure and sphere have been placed, flatten the
        # whole silly thing
        # self.nodePath.flattenLight()

        if self.penalty:
            #self.nodePath.setScale(1,1,0.5)
            self.tip = self.nodePath.find('**/fusetip')
            #self.tip.setX(2)
            #self.tip.setY(0.5)
            #self.tip.setZ(1.5)
            sparks = BattleParticles.createParticleEffect(file='icetnt')
            self.sparksEffect = sparks
            sparks.start(self.tip)
            self.penaltyGrabSound = loader.loadSfx(
                "phase_4/audio/sfx/MG_cannon_fire_alt.mp3")
            self.penaltyGrabSound.setVolume(0.75)
            kaboomAttachPoint = self.nodePath.attachNewNode('kaboomAttach')
            kaboomAttachPoint.setZ(3)
            self.kaboom = loader.loadModel(
                'phase_4/models/minigames/ice_game_kaboom')
            self.kaboom.reparentTo(kaboomAttachPoint)
            #self.kaboom.hide()
            self.kaboom.setScale(2.0)
            self.kaboom.setBillboardPointEye()
            #self.kaboom.setBin('fixed', serialNum)
            #self.kaboom.setDepthTest(False)
            #self.kaboom.setDepthWrite(False)

    def destroy(self):
        self.ignoreAll()
        if self.penalty:
            self.sparksEffect.cleanup()
            if self.track:
                self.track.finish()

        self.nodePath.removeNode()
        del self.nodePath
        del self.collSphere
        self.collNodePath.removeNode()
        del self.collNodePath
        del self.collNode

##     def __handleEnterSphere(self, collEntry):
##         self.ignoreAll()
##         # announce that this treasure was grabbed
##         self.notify.debug('treasuerGrabbed')
##         messenger.send("IceTreasureGrabbed", [self.serialNum])

    def showGrab(self):
        self.nodePath.hide()
        self.collNodePath.hide()
        # disable collisions
        self.collNode.setIntoCollideMask(BitMask32(0))
        if self.penalty:
            self.track = Parallel(
                SoundInterval(self.penaltyGrabSound),
                Sequence(
                    Func(self.kaboom.showThrough),
                    LerpScaleInterval(self.kaboom,
                                      duration=0.5,
                                      scale=Point3(10, 10, 10),
                                      blendType='easeOut'),
                    Func(self.kaboom.hide),
                ))
            self.track.start()
Exemplo n.º 14
0
class DistributedCogKart(DistributedElevatorExt.DistributedElevatorExt):

    notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogKart")
    JumpOutOffsets = ((6.5, -2, -0.025),
                      (-6.5, -2, -0.025),
                      (3.75, 5, -0.025),
                      (-3.75, 5, -0.025))

    def __init__(self, cr):
        """__init__(cr)
        """
        DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr)
        self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB
        # note since we did elevator init last, self.fsm is DistributedElevator.fsm
        self.kartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam'
        self.leftDoor = None
        self.rightDoor = None
        self.fillSlotTrack = None

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

        # Get the state machine stuff for playGame
        self.loader = self.cr.playGame.hood.loader
        if(self.loader):
            self.notify.debug("Loader has been loaded")
            self.notify.debug(str(self.loader))
        else:
            self.notify.debug("Loader has not been loaded")

        self.golfKart = render.attachNewNode('golfKartNode')
        self.kart = loader.loadModel(self.kartModelPath)
        self.kart.setPos(0, 0, 0)
        self.kart.setScale(1)
        self.kart.reparentTo(self.golfKart)
        self.golfKart.reparentTo(self.loader.geom)

        # Wheels
        self.wheels = self.kart.findAllMatches('**/wheelNode*')
        self.numWheels = self.wheels.getNumPaths()


    def announceGenerate(self):
        """Setup other fields dependent on the required fields."""
        DistributedElevatorExt.DistributedElevatorExt.announceGenerate(self)

        angle = self.startingHpr[0]
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3( math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 45.0
        self.endPos =  self.startingPos + unitVec
        self.endPos.setZ(0.5)

        dist = Vec3(self.endPos - self.enteringPos).length()
        wheelAngle = (dist / (4.8 * 1.4 * math.pi)) * 360

        self.kartEnterAnimateInterval = Parallel(
            # start a lerp HPR for each wheel
            LerpHprInterval(self.wheels[0], 5.0, Vec3(self.wheels[0].getH(), wheelAngle, self.wheels[0].getR())),
            LerpHprInterval(self.wheels[1], 5.0, Vec3(self.wheels[1].getH(), wheelAngle, self.wheels[1].getR())),
            LerpHprInterval(self.wheels[2], 5.0, Vec3(self.wheels[2].getH(), wheelAngle, self.wheels[2].getR())),
            LerpHprInterval(self.wheels[3], 5.0, Vec3(self.wheels[3].getH(), wheelAngle, self.wheels[3].getR())),
            name = "CogKartAnimate")

        trolleyExitTrack1 = Parallel(
            LerpPosInterval(self.golfKart, 5.0, self.endPos),
            self.kartEnterAnimateInterval,
            name = "CogKartExitTrack")
        self.trolleyExitTrack = Sequence(
            trolleyExitTrack1,
            # Func(self.hideSittingToons), # we may not need this
            )

        self.trolleyEnterTrack = Sequence(
            LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos = self.enteringPos))

        self.closeDoors = Sequence(
            self.trolleyExitTrack,
            Func(self.onDoorCloseFinish))
        self.openDoors = Sequence(
            self.trolleyEnterTrack
            )

    def delete(self):
        """
        This method is called when the DistributedObject is permanently
        removed from the world and deleted from the cache.
        """
        DistributedElevatorExt.DistributedElevatorExt.delete(self)
        if hasattr(self, 'elevatorFSM') :
            del self.elevatorFSM

    def setBldgDoId(self, bldgDoId):
        """Handle the AI telling us the associated bldg doid."""
        # The doId is junk, there is no building object for the factory
        # exterior elevators. Do the appropriate things that
        # DistributedElevator.gotBldg does.
        self.bldg = None
        self.setupElevatorKart()

    def setupElevatorKart(self):
        """Setup elevator related fields."""
        # Establish a collision sphere. There must be an easier way!
        collisionRadius = ElevatorConstants.ElevatorData[self.type]['collRadius']
        self.elevatorSphere = CollisionSphere(0, 0, 0, collisionRadius)
        self.elevatorSphere.setTangible(1)
        self.elevatorSphereNode = CollisionNode(self.uniqueName("elevatorSphere"))
        self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        self.elevatorSphereNode.addSolid(self.elevatorSphere)
        self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(
            self.elevatorSphereNode)
        self.elevatorSphereNodePath.hide()
        self.elevatorSphereNodePath.reparentTo(self.getElevatorModel())
        self.elevatorSphereNodePath.stash()

        self.boardedAvIds = {}
        self.finishSetup()

    def setColor(self, r, g, b):
        """Ignore this AI message to make it look cog grayish."""
        pass

    def getElevatorModel(self):
        return self.golfKart

    def enterWaitEmpty(self, ts):
        """Handle entering the wait empty state."""
        DistributedElevatorExt.DistributedElevatorExt.enterWaitEmpty(self, ts)

    def exitWaitEmpty(self):
        """Handle exiting the wait empty state."""
        DistributedElevatorExt.DistributedElevatorExt.exitWaitEmpty(self)

    def forceDoorsOpen(self):
        """Deliberately do nothing."""
        pass

    def forceDoorsClosed(self):
        """Deliberately do nothing."""
        pass

    def setPosHpr(self, x, y, z, h, p ,r):
        """Set the pos hpr as dictated by the AI."""
        self.startingPos = Vec3(x, y, z)
        self.enteringPos = Vec3(x, y, z - 10)
        self.startingHpr = Vec3(h, 0, 0)
        self.golfKart.setPosHpr( x, y, z, h, 0, 0 )

    def enterClosing(self, ts):
        # Close the elevator doors
        if self.localToonOnBoard:
            elevator = self.getPlaceElevator()
            if elevator:
                elevator.fsm.request("elevatorClosing")
        self.closeDoors.start(ts)

    def enterClosed(self, ts):
        self.forceDoorsClosed()
        self.kartDoorsClosed(self.getZoneId())
        return

    def kartDoorsClosed(self, zoneId):
        assert(self.notify.debug('doorsClosed()'))
        if (self.localToonOnBoard):
            hoodId = ZoneUtil.getHoodId(zoneId)
            doneStatus = {
                'loader' : 'suitInterior',
                'where' : 'suitInterior',
                'hoodId' : hoodId,
                'zoneId' : zoneId,
                'shardId' : None,
                }

            elevator = self.elevatorFSM #self.getPlaceElevator()
            del self.elevatorFSM
            elevator.signalDone(doneStatus)


    def setCountryClubInteriorZone(self, zoneId):
        if (self.localToonOnBoard):
            hoodId = self.cr.playGame.hood.hoodId
            countryClubId = self.countryClubId
            if bboard.has('countryClubIdOverride'):
                countryClubId = bboard.get('countryClubIdOverride')
            doneStatus = {
                'loader' : "cogHQLoader",
                'where'  : "countryClubInterior",
                'how'    : "teleportIn",
                'zoneId' : zoneId,
                'countryClubId' : self.countryClubId,
                'hoodId' : hoodId,
                }
            self.cr.playGame.getPlace().elevator.signalDone(doneStatus)

    def setCountryClubInteriorZoneForce(self, zoneId):
        place = self.cr.playGame.getPlace()
        if place:
            place.fsm.request("elevator", [self, 1])
            hoodId = self.cr.playGame.hood.hoodId
            countryClubId = self.countryClubId
            if bboard.has('countryClubIdOverride'):
                countryClubId = bboard.get('countryClubIdOverride')
            doneStatus = {
                'loader' : "cogHQLoader",
                'where'  : "countryClubInterior",
                'how'    : "teleportIn",
                'zoneId' : zoneId,
                'countryClubId' : self.countryClubId,
                'hoodId' : hoodId,
                }
            if hasattr(place, 'elevator') and place.elevator:
                place.elevator.signalDone(doneStatus)
            else:
                self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" %zoneId)
        else:
            self.notify.warning("setCountryClubInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" %zoneId)

    def setCountryClubId(self, countryClubId):
        self.countryClubId = countryClubId

    def getZoneId(self):
        return 0

    def fillSlot(self, index, avId, wantBoardingShow = 0):
        """Put someone in the kart, as dictated by the AI."""
        self.notify.debug("%s.fillSlot(%s, %s, ... %s)" % (self.doId, index, avId, globalClock.getRealTime()))
        request = self.toonRequests.get(index)
        if request:
            self.cr.relatedObjectMgr.abortRequest(request)
            del self.toonRequests[index]

        if avId == 0:
            # This means that the slot is now empty, and no action should
            # be taken.
            pass

        elif avId not in self.cr.doId2do:
            # It's someone who hasn't been generated yet.
            func = PythonUtil.Functor(
                self.gotToon, index, avId)

            assert index not in self.toonRequests
            self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects(
                [avId], allCallback = func)

        elif not self.isSetup:
            # We haven't set up the elevator yet.
            self.deferredSlots.append((index, avId, wantBoardingShow))

        else:
            # If localToon is boarding, he needs to change state
            if avId == base.localAvatar.getDoId():
                place = base.cr.playGame.getPlace()
                if not place:
                    return
                elevator = self.getPlaceElevator()
                if elevator == None:
                    place.fsm.request('elevator')
                    elevator = self.getPlaceElevator()
                if not elevator:
                    return

                self.localToonOnBoard = 1

                if hasattr(localAvatar, "boardingParty") and localAvatar.boardingParty:
                    localAvatar.boardingParty.forceCleanupInviteePanel()
                    localAvatar.boardingParty.forceCleanupInviterPanels()

                # Cleanup any leftover elevator messages before boarding the elevator.
                if hasattr(base.localAvatar, "elevatorNotifier"):
                    base.localAvatar.elevatorNotifier.cleanup()

                cameraTrack = Sequence()
                # Move the camera towards and face the elevator.
                cameraTrack.append(Func(elevator.fsm.request, "boarding", [self.getElevatorModel()]))
                # Enable the Hop off button.
                cameraTrack.append(Func(elevator.fsm.request, "boarded"))

            toon = self.cr.doId2do[avId]
            # Parent it to the elevator
            toon.stopSmooth()
            toon.wrtReparentTo(self.golfKart)

            sitStartDuration = toon.getDuration("sit-start")
            jumpTrack = self.generateToonJumpTrack(toon, index)

            track = Sequence(
				jumpTrack,
                Func(toon.setAnimState, "Sit", 1.0),
                Func(self.clearToonTrack, avId),
                name = toon.uniqueName("fillElevator"),
                autoPause = 1)

            if wantBoardingShow:
                boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, True)
                track = Sequence(boardingTrack, track)

                if avId == base.localAvatar.getDoId():
                    cameraWaitTime = 2.5
                    if (boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN):
                        cameraWaitTime = 0.5
                    cameraTrack = Sequence(Wait(cameraWaitTime), cameraTrack)

            if self.canHideBoardingQuitBtn(avId):
                track = Sequence(Func(localAvatar.boardingParty.groupPanel.disableQuitButton),
                                 track)

            # Start the camera track in parallel here
            if avId == base.localAvatar.getDoId():
                track = Parallel(cameraTrack, track)

            track.delayDelete = DelayDelete.DelayDelete(toon, 'CogKart.fillSlot')
            self.storeToonTrack(avId, track)
            track.start()

            self.fillSlotTrack = track

            assert avId not in self.boardedAvIds
            self.boardedAvIds[avId] = None

    def generateToonJumpTrack(self, av, seatIndex):
        """Return an interval of the toon jumping into the golf kart."""
        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)

        def getToonJumpTrack( av, seatIndex ):
            # using a local func allows the ProjectileInterval to
            # calculate this pos at run-time
            def getJumpDest(av = av, node = self.golfKart):
                dest = Point3(0,0,0)
                if hasattr(self, 'golfKart') and self.golfKart:
                    dest = Vec3(self.golfKart.getPos(av.getParent()))
                    seatNode = self.golfKart.find("**/seat" + str(seatIndex + 1))
                    dest += seatNode.getPos(self.golfKart)
                    dna = av.getStyle()
                    dest -= hipOffset
                    if(seatIndex < 2):
                        dest.setY( dest.getY() + 2 * hipOffset.getY())
                    dest.setZ(dest.getZ() + 0.1)
                else:
                    self.notify.warning('getJumpDestinvalid golfKart, returning (0,0,0)')
                return dest

            def getJumpHpr(av = av, node = self.golfKart):
                hpr = Point3(0,0,0)
                if hasattr(self, 'golfKart') and self.golfKart:
                    hpr = self.golfKart.getHpr(av.getParent())
                    if(seatIndex < 2):
                        hpr.setX( hpr.getX() + 180)
                    else:
                        hpr.setX( hpr.getX() )
                    angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                    hpr.setX(angle)
                else:
                    self.notify.warning('getJumpHpr invalid golfKart, returning (0,0,0)')
                return hpr

            toonJumpTrack = Parallel(ActorInterval( av, 'jump' ),
                                     Sequence(Wait( 0.43 ),
                                              Parallel(LerpHprInterval(av,
                                                                       hpr = getJumpHpr,
                                                                       duration = .9 ),
                                                       ProjectileInterval(av,
                                                                          endPos = getJumpDest,
                                                                          duration = .9 ),
                                                      ),
                                             ),
                                    )
            return toonJumpTrack

        def getToonSitTrack( av ):
            toonSitTrack = Sequence(

                ActorInterval( av, 'sit-start' ),
                Func( av.loop, 'sit' )
                )
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack( av, seatIndex )
        toonSitTrack = getToonSitTrack( av )

        jumpTrack = Sequence(
            Parallel(
                toonJumpTrack,
                Sequence( Wait(1),
                          toonSitTrack,
                          ),
                ),
##            Func( av.wrtReparentTo, self.golfKart ),
            )

        return jumpTrack


    def emptySlot(self, index, avId, bailFlag, timestamp, timeSent=0):
        """Remove someone as dictated by the AI."""
        if self.fillSlotTrack:
            self.fillSlotTrack.finish()
            self.fillSlotTrack = None

        # If localToon is exiting, he needs to change state
        if avId == 0:
            # This means that no one is currently exiting, and no action
            # should be taken
            pass

        elif not self.isSetup:
            # We haven't set up the elevator yet.  Remove the toon
            # from the deferredSlots list, if it is there.
            newSlots = []
            for slot in self.deferredSlots:
                if slot[0] != index:
                    newSlots.append(slot)

            self.deferredSlots = newSlots

        else:
            if avId in self.cr.doId2do:
                # See if we need to reset the clock
                # (countdown assumes we've created a clockNode already)
                if (bailFlag == 1 and hasattr(self, 'clockNode')):
                    if (timestamp < self.countdownTime and
                        timestamp >= 0):
                        self.countdown(self.countdownTime - timestamp)
                    else:
                        self.countdown(self.countdownTime)
                # If the toon exists, look it up
                toon = self.cr.doId2do[avId]
                # avoid wrtReparent so that we don't muck with the toon's scale
                # Parent it to render
                #toon.wrtReparentTo(render)
                toon.stopSmooth()

                sitStartDuration = toon.getDuration("sit-start")
                jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)

                # Place it on the appropriate spot relative to the
                # elevator

                track = Sequence(
                    # TODO: Find the right coords for the elevator
                    jumpOutTrack,
                    # Tell the toon he is free to roam now
                    Func(self.notifyToonOffElevator, toon),
                    Func(self.clearToonTrack, avId),
                    name = toon.uniqueName("emptyElevator"),
                    autoPause = 1)

                if self.canHideBoardingQuitBtn(avId):
                    # Enable the Boarding Group Panel Quit Button here if it is relevant.
                    track.append(Func(localAvatar.boardingParty.groupPanel.enableQuitButton))
                    # Enable the Boarding Group GO Button here if it is relevant.
                    track.append(Func(localAvatar.boardingParty.enableGoButton))

                track.delayDelete = DelayDelete.DelayDelete(toon, 'CogKart.emptySlot')
                self.storeToonTrack(toon.doId, track)
                track.start()

                # Tell localToon he is exiting (if localToon is on board)
                if avId == base.localAvatar.getDoId():
                    messenger.send("exitElevator")

                # if the elevator is generated as a toon is leaving it,
                # we will not have gotten a corresponding 'fillSlot' message
                # for that toon, hence the toon will not be found in
                # boardedAvIds
                if avId in self.boardedAvIds:
                    del self.boardedAvIds[avId]

            else:
                self.notify.warning("toon: " + str(avId) +
                                                  " doesn't exist, and" +
                                                  " cannot exit the elevator!")

    def generateToonReverseJumpTrack( self, av, seatIndex ):
        """Return an interval of the toon jumping out of the golf kart."""
        self.notify.debug("av.getH() = %s" % av.getH())
        def getToonJumpTrack( av, destNode ):
            # using a local func allows the ProjectileInterval to
            # calculate this pos at run-time
            def getJumpDest(av = av, node = destNode):
                dest = node.getPos(av.getParent())
                dest += Vec3(*self.JumpOutOffsets[seatIndex])
                return dest

            def getJumpHpr(av = av, node = destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX( hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(
                ActorInterval( av, 'jump' ),
                Sequence(
                  Wait( 0.1), #43 ),
                  Parallel( #LerpHprInterval( av,
                            #                 hpr = getJumpHpr,
                            #                 duration = .9 ),
                            ProjectileInterval( av,
                                                endPos = getJumpDest,
                                                duration = .9 ) )
                  )
                )
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack( av, self.golfKart)
        jumpTrack = Sequence(
            toonJumpTrack,
            Func( av.loop, 'neutral' ),
            Func( av.wrtReparentTo, render ),
            #Func( self.av.setPosHpr, self.exitMovieNode, 0,0,0,0,0,0 ),
            )
        return jumpTrack

    def startCountdownClock(self, countdownTime, ts):
        """Start the countdown clock."""
        # just reverse the text counter
        DistributedElevatorExt.DistributedElevatorExt.startCountdownClock(self, countdownTime, ts)
        self.clock.setH(self.clock.getH() + 180)

    def rejectBoard(self, avId, reason = 0):
        """Show the reason why he was rejected."""
        # Only difference from base clase is the use of KartMinLaff
        # This should only be sent to us if our localToon requested
        # permission to board the elevator.
        # reason 0: unknown, 1: shuffle, 2: too low laff, 3: no seat, 4: need promotion
        print(("rejectBoard %s" % (reason)))
        if hasattr(base.localAvatar, "elevatorNotifier"):
            if reason == ElevatorConstants.REJECT_SHUFFLE:
                base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorHoppedOff)
            elif reason == ElevatorConstants.REJECT_MINLAFF:
                base.localAvatar.elevatorNotifier.showMe((TTLocalizer.KartMinLaff % (self.minLaff)))
            elif reason == ElevatorConstants.REJECT_PROMOTION:
                base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage)
            elif reason == ElevatorConstants.REJECT_NOT_YET_AVAILABLE:
                base.localAvatar.elevatorNotifier.showMe(TTLocalizer.NotYetAvailable)
        assert(base.localAvatar.getDoId() == avId)

        doneStatus = {
                'where' : 'reject',
                }
        elevator = self.getPlaceElevator()
        if elevator:
            elevator.signalDone(doneStatus)

    def getDestName(self):
        if self.countryClubId == ToontownGlobals.BossbotCountryClubIntA:
            return TTLocalizer.ElevatorBossBotCourse0
        elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntB:
            return TTLocalizer.ElevatorBossBotCourse1
        elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntC:
            return TTLocalizer.ElevatorBossBotCourse2
Exemplo n.º 15
0
class CogThief(DirectObject):
    """This represents a single cog thief in the cog thief game"""
    notify = directNotify.newCategory("CogThief")
    DefaultSpeedWalkAnim = 4.
    CollisionRadius = 1.25
    MaxFriendsVisible = 4
    Infinity = 100000.0  # just a really big number
    SeparationDistance = 6.0
    MinUrgency = 0.5
    MaxUrgency = 0.75

    def __init__(self, cogIndex, suitType, game, cogSpeed):
        self.cogIndex = cogIndex
        self.suitType = suitType
        self.game = game
        self.cogSpeed = cogSpeed
        suit = Suit.Suit()
        d = SuitDNA.SuitDNA()
        d.newSuit(suitType)
        suit.setDNA(d)
        # cache the walk anim
        suit.pose('walk', 0)
        self.suit = suit
        self.goal = CTGG.NoGoal
        self.goalId = CTGG.InvalidGoalId
        self.lastLocalTimeStampFromAI = 0
        self.lastPosFromAI = Point3(0, 0, 0)
        self.lastThinkTime = 0
        self.doneAdjust = False
        self.barrel = CTGG.NoBarrelCarried
        self.signalledAtReturnPos = False
        self.defaultPlayRate = 1.0
        self.netTimeSentToStartByHit = 0

        # steering loosely based on boid code game programming gems #1
        # "Portions Copyright (C) Steven Woodcock, 2000"
        self.velocity = Vec3(0, 0, 0)
        self.oldVelocity = Vec3(0, 0, 0)
        self.acceleration = Vec3(0, 0, 0)
        self.bodyLength = self.CollisionRadius * 2
        # Desired distance from closest neighbor when flying.
        self.cruiseDistance = 2 * self.bodyLength
        self.maxVelocity = self.cogSpeed
        # Maximum magnitude of acceleration as a fraction of maxSpeed.
        self.maxAcceleration = 5.0
        self.perceptionRange = 6
        self.notify.debug('cogSpeed=%s' % self.cogSpeed)

        self.kaboomSound = loader.loadSfx(
            "phase_4/audio/sfx/MG_cannon_fire_alt.mp3")
        self.kaboom = loader.loadModel(
            'phase_4/models/minigames/ice_game_kaboom')
        self.kaboom.setScale(2.0)
        self.kaboom.setBillboardPointEye()
        self.kaboom.hide()
        self.kaboomTrack = None

        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splatType = globalPropPool.getPropType(splatName)

        self.pieHitSound = globalBattleSoundCache.getSound(
            'AA_wholepie_only.mp3')

    def destroy(self):
        self.ignoreAll()
        self.suit.delete()
        self.game = None

    def uniqueName(self, baseStr):
        return baseStr + '-' + str(self.game.doId)

    def handleEnterSphere(self, collEntry):
        """Handle the suit colliding with localToon."""
        #assert self.notify.debugStateCall(self)
        intoNp = collEntry.getIntoNodePath()
        self.notify.debug('handleEnterSphere suit %d hit %s' %
                          (self.cogIndex, intoNp))
        if self.game:
            self.game.handleEnterSphere(collEntry)

    def gameStart(self, gameStartTime):
        self.gameStartTime = gameStartTime

        self.initCollisions()
        self.startWalkAnim()

    def gameEnd(self):
        self.moveIval.pause()
        del self.moveIval

        self.shutdownCollisions()

        # keep the suits from walking in place
        self.suit.loop('neutral')

    def initCollisions(self):
        # Make a sphere, give it a unique name, and parent it
        # to the suit.
        self.collSphere = CollisionSphere(0, 0, 0, 1.25)
        # Make he sphere intangible
        self.collSphere.setTangible(1)
        name = "CogThiefSphere-%d" % self.cogIndex
        self.collSphereName = self.uniqueName(name)
        self.collNode = CollisionNode(self.collSphereName)
        self.collNode.setIntoCollideMask(CTGG.BarrelBitmask
                                         | ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.suit.attachNewNode(self.collNode)
        #self.collNodePath.hide()

        # Add a hook looking for collisions with localToon
        self.accept('enter' + self.collSphereName, self.handleEnterSphere)

        # we need a taller collision tube to collide against for pie
        self.pieCollSphere = CollisionTube(0, 0, 0, 0, 0, 4,
                                           self.CollisionRadius)
        # Make he sphere intangible
        self.pieCollSphere.setTangible(1)
        name = "CogThiefPieSphere-%d" % self.cogIndex
        self.pieCollSphereName = self.uniqueName(name)
        self.pieCollNode = CollisionNode(self.pieCollSphereName)
        self.pieCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask)
        self.pieCollNode.addSolid(self.pieCollSphere)
        self.pieCollNodePath = self.suit.attachNewNode(self.pieCollNode)
        #self.pieCollNodePath.show()

        # Add a hook looking for collisions with localToon
        #self.accept('enter' + self.pieCollSphereName,
        #            self.handleEnter)

    def shutdownCollisions(self):
        self.ignore(self.uniqueName('enter' + self.collSphereName))

        del self.collSphere
        self.collNodePath.removeNode()
        del self.collNodePath
        del self.collNode

    def updateGoal(self, timestamp, inResponseClientStamp, goalType, goalId,
                   pos):
        """Update our goal and position."""
        assert self.notify.debugStateCall(self)
        self.notify.debug('self.netTimeSentToStartByHit =%s' %
                          self.netTimeSentToStartByHit)
        if not self.game:
            self.notify.debug('updateGoal self.game is None, just returning')
            return
        if not self.suit:
            self.notify.debug('updateGoal self.suit is None, just returning')
            return
        if self.goal == CTGG.NoGoal:
            self.startWalkAnim()

        if goalType == CTGG.NoGoal:
            self.notify.debug('updateGoal setting position to %s' % pos)
            self.suit.setPos(pos)

        self.lastThinkTime = 0
        self.velocity = Vec3(0, 0, 0)
        self.oldVelocity = Vec3(0, 0, 0)
        self.acceleration = Vec3(0, 0, 0)

        if goalType == CTGG.RunAwayGoal:
            #import pdb; pdb.set_trace()
            pass

        if inResponseClientStamp < self.netTimeSentToStartByHit and \
           self.goal == CTGG.NoGoal and \
           goalType == CTGG.RunAwayGoal:
            #import pdb; pdb.set_trace()
            self.notify.warning(
                'ignoring newGoal %s as cog %d was recently hit responsetime=%s hitTime=%s'
                % (CTGG.GoalStr[goalType], self.cogIndex,
                   inResponseClientStamp, self.netTimeSentToStartByHit))
        else:
            self.lastLocalTimeStampFromAI = globalClockDelta.networkToLocalTime(
                timestamp, bits=32)
            self.goal = goalType
            self.goalId = goalId
            self.lastPosFromAI = pos
            self.doneAdjust = False
        self.signalledAtReturnPos = False
        # TODO move the suit to where we expect him to be given the time difference

    def startWalkAnim(self):
        if self.suit:
            self.suit.loop('walk')
            speed = self.cogSpeed  # float(MazeData.CELL_WIDTH) / self.cellWalkDuration
            self.defaultPlayRate = float(self.cogSpeed /
                                         self.DefaultSpeedWalkAnim)
            self.suit.setPlayRate(self.defaultPlayRate, 'walk')

    def think(self):
        """Calculate where we should go."""
        if self.goal == CTGG.ToonGoal:
            self.thinkAboutCatchingToon()
        elif self.goal == CTGG.BarrelGoal:
            self.thinkAboutGettingBarrel()
        elif self.goal == CTGG.RunAwayGoal:
            self.thinkAboutRunAway()

    def thinkAboutCatchingToon(self):
        if not self.game:
            return

        av = self.game.getAvatar(self.goalId)
        if av:
            if not self.lastThinkTime:
                self.lastThinkTime = globalClock.getFrameTime()
            diffTime = globalClock.getFrameTime() - self.lastThinkTime
            avPos = av.getPos()
            myPos = self.suit.getPos()

            if not self.doneAdjust:
                myPos = self.lastPosFromAI
                self.notify.debug(
                    'thinkAboutCatchingToon not doneAdjust setting pos %s' %
                    myPos)
                self.doneAdjust = True

            self.suit.setPos(myPos)

            if self.game.isToonPlayingHitTrack(self.goalId):
                # do nothing, just look at toon
                self.suit.headsUp(av)
                self.velocity = Vec3(0, 0, 0)
                self.oldVelocity = Vec3(0, 0, 0)
                self.acceleration = Vec3(0, 0, 0)
            else:
                self.commonMove()

            newPos = self.suit.getPos()
            self.adjustPlayRate(newPos, myPos, diffTime)

        self.lastThinkTime = globalClock.getFrameTime()

    def convertNetworkStampToGameTime(self, timestamp):
        """Convert a network timestamp to game time."""
        localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32)
        gameTime = self.game.local2GameTime(localStamp)
        return gameTime

    def respondToToonHit(self, timestamp):
        """The toon hit us, react appropriately."""
        assert self.notify.debugStateCall(self)
        localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32)
        # using 1.0 sec as fudge
        #if localStamp > self.lastLocalTimeStampFromAI:
        if self.netTimeSentToStartByHit < timestamp:
            self.clearGoal()
            self.showKaboom()
            # move him to his starting postion
            startPos = CTGG.CogStartingPositions[self.cogIndex]
            oldPos = self.suit.getPos()
            self.suit.setPos(startPos)
            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            self.notify.debug(
                'localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToToonHit'
                % (localStamp, self.lastLocalTimeStampFromAI))
        self.notify.debug(
            'respondToToonHit self.netTimeSentToStartByHit = %s' %
            self.netTimeSentToStartByHit)

    def clearGoal(self):
        """Clear goal and goal id."""
        self.goal = CTGG.NoGoal
        self.goalId = CTGG.InvalidGoalId

    def thinkAboutGettingBarrel(self):
        """Go for  a barrel."""
        if not self.game:
            return

        if not hasattr(self.game, 'barrels'):
            return
        if not self.goalId in range(len(self.game.barrels)):
            return

        if not self.lastThinkTime:
            self.lastThinkTime = globalClock.getFrameTime()
        diffTime = globalClock.getFrameTime() - self.lastThinkTime
        barrel = self.game.barrels[self.goalId]
        barrelPos = barrel.getPos()
        myPos = self.suit.getPos()
        if not self.doneAdjust:
            myPos = self.lastPosFromAI
            self.notify.debug(
                'thinkAboutGettingBarrel not doneAdjust setting position to %s'
                % myPos)
            self.suit.setPos(myPos)
            """
            diffTime = globalClock.getFrameTime()- self.lastLocalTimeStampFromAI
            self.notify.debug('doing adjust, diffTime = %s' % diffTime)
            if diffTime < 0:
                # it just looks really weird when it moves backwards
                diffTime = 0
                self.notify.debug('forcing diffTime to %s' % diffTime)
            """
            self.doneAdjust = True
        displacement = barrelPos - myPos
        distanceToToon = displacement.length()
        #self.notify.debug('diffTime = %s' % diffTime)
        self.suit.headsUp(barrel)
        lengthTravelled = diffTime * self.cogSpeed
        #self.notify.debug('lengthTravelled = %s' % lengthTravelled)
        # don't overshoot our target
        if lengthTravelled > distanceToToon:
            lengthTravelled = distanceToToon
            #self.notify.debug('overshooting lengthTravelled = %s' % lengthTravelled)
        displacement.normalize()
        dirVector = displacement
        dirVector *= lengthTravelled
        newPos = myPos + dirVector
        # always keep them grounded
        newPos.setZ(0)
        self.suit.setPos(newPos)
        self.adjustPlayRate(newPos, myPos, diffTime)

        self.lastThinkTime = globalClock.getFrameTime()

    def stopWalking(self, timestamp):
        """Stop the cog from walking."""
        localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32)
        if localStamp > self.lastLocalTimeStampFromAI:
            self.suit.loop('neutral')
            self.clearGoal()

    def thinkAboutRunAway(self):
        """Go for  a barrel."""
        if not self.game:
            return
        if not self.lastThinkTime:
            self.lastThinkTime = globalClock.getFrameTime()
        diffTime = globalClock.getFrameTime() - self.lastThinkTime

        returnPos = CTGG.CogReturnPositions[self.goalId]
        myPos = self.suit.getPos()
        if not self.doneAdjust:
            myPos = self.lastPosFromAI
            self.suit.setPos(myPos)
            """
            diffTime = globalClock.getFrameTime()- self.lastLocalTimeStampFromAI
            self.notify.debug('run away doing adjust, diffTime = %s' % diffTime)
            if diffTime < 0:
                # it just looks really weird when it moves backwards
                diffTime = 0
                self.notify.debug('forcing diffTime to %s' % diffTime)
            """
            self.doneAdjust = True
        displacement = returnPos - myPos
        distanceToToon = displacement.length()
        #self.notify.debug('diffTime = %s' % diffTime)
        tempNp = render.attachNewNode('tempRet')
        tempNp.setPos(returnPos)
        self.suit.headsUp(tempNp)
        tempNp.removeNode()
        lengthTravelled = diffTime * self.cogSpeed
        #self.notify.debug('lengthTravelled = %s' % lengthTravelled)
        # don't overshoot our target
        if lengthTravelled > distanceToToon:
            lengthTravelled = distanceToToon
            #self.notify.debug('overshooting lengthTravelled = %s' % lengthTravelled)
        displacement.normalize()
        dirVector = displacement
        dirVector *= lengthTravelled
        newPos = myPos + dirVector
        # always keep them grounded
        newPos.setZ(0)
        self.suit.setPos(newPos)
        self.adjustPlayRate(newPos, myPos, diffTime)

        if (self.suit.getPos() - returnPos).length() < 0.0001:
            if not self.signalledAtReturnPos and self.barrel >= 0:
                # tell the AI we're at return Pos
                self.game.sendCogAtReturnPos(self.cogIndex, self.barrel)
                self.signalledAtReturnPos = True

        self.lastThinkTime = globalClock.getFrameTime()

    def makeCogCarryBarrel(self, timestamp, inResponseClientStamp, barrelModel,
                           barrelIndex, cogPos):
        """Handle the AI telling us the barrel is attached to a cog."""
        #assert self.notify.debugStateCall(self)
        if not self.game:
            return
        localTimeStamp = globalClockDelta.networkToLocalTime(timestamp,
                                                             bits=32)
        # TODO validate time?
        self.lastLocalTimeStampFromAI = localTimeStamp
        inResponseGameTime = self.convertNetworkStampToGameTime(
            inResponseClientStamp)

        self.notify.debug('inResponseGameTime =%s timeSentToStart=%s' %
                          (inResponseGameTime, self.netTimeSentToStartByHit))
        if inResponseClientStamp  < self.netTimeSentToStartByHit and \
           self.goal == CTGG.NoGoal:
            self.notify.warning('ignoring makeCogCarrybarrel')
        else:
            barrelModel.setPos(0, -1.0, 1.5)
            barrelModel.reparentTo(self.suit)
            self.suit.setPos(cogPos)
            self.barrel = barrelIndex

    def makeCogDropBarrel(self, timestamp, inResponseClientStamp, barrelModel,
                          barrelIndex, barrelPos):
        """Handle the AI telling us the barrel is attached to a cog."""
        #assert self.notify.debugStateCall(self)
        localTimeStamp = globalClockDelta.networkToLocalTime(timestamp,
                                                             bits=32)
        # TODO validate time?
        self.lastLocalTimeStampFromAI = localTimeStamp

        barrelModel.reparentTo(render)
        barrelModel.setPos(barrelPos)

        self.barrel = CTGG.NoBarrelCarried

        #
        #self.suit.setPos(cogPos)

    def respondToPieHit(self, timestamp):
        """The toon hit us, react appropriately."""
        assert self.notify.debugStateCall(self)
        localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32)
        # argh using 1.0 sec as fudge
        #if localStamp  > self.lastLocalTimeStampFromAI:
        if self.netTimeSentToStartByHit < timestamp:
            self.clearGoal()
            self.showSplat()
            # move him to his starting postion
            startPos = CTGG.CogStartingPositions[self.cogIndex]
            oldPos = self.suit.getPos()
            self.suit.setPos(startPos)
            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            self.notify.debug(
                'localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit'
                % (localStamp, self.lastLocalTimeStampFromAI))
            self.notify.debug(
                'respondToPieHit self.netTimeSentToStartByHit = %s' %
                self.netTimeSentToStartByHit)

    def cleanup(self):
        """Do whatever is necessary to cleanup properly."""
        self.clearGoal()
        self.ignoreAll()
        self.suit.delete()
        if self.kaboomTrack and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.suit = None
        self.game = None

    def adjustPlayRate(self, newPos, oldPos, diffTime):
        """Adjust animation rate based on how far he's moved."""
        # lets slowdown playrate if they're not moving much
        lengthTravelled = (newPos - oldPos).length()
        if diffTime:
            speed = lengthTravelled / diffTime
        else:
            speed = self.cogSpeed
        rateMult = speed / self.cogSpeed
        newRate = rateMult * self.defaultPlayRate
        self.suit.setPlayRate(newRate, 'walk')

    def commonMove(self):
        """Move the cog thief. Common for all 3 behaviors """
        if not self.lastThinkTime:
            self.lastThinkTime = globalClock.getFrameTime()
        dt = globalClock.getFrameTime() - self.lastThinkTime

        # Step 1:  Update our position.
        # Update our position based on the velocity
        # vector we computed last time around.

        self.oldpos = self.suit.getPos()
        # save off our previous position

        pos = self.suit.getPos()
        pos += self.velocity * dt
        # apply velocities.
        self.suit.setPos(pos)

        # Step 2:  SeeFriends.
        # Determine if we can see any of our flockmates.

        self.seeFriends()

        acc = Vec3(0, 0, 0)

        # well first off we want to move to our target
        self.accumulate(acc, self.getTargetVector())

        # Step 3:  Flocking behavior.
        # Do we see any of our flockmates?  If yes, it's time to implement
        # the first Three Rules (they don't matter if we can't see anybody)

        if self.numFlockmatesSeen > 0:
            #if hasattr(base,'doDebug') and base.doDebug:
            #    import pdb; pdb.set_trace()
            keepDistanceVector = self.keepDistance()
            oldAcc = Vec3(acc)
            self.accumulate(acc, keepDistanceVector)
            if self.cogIndex == 0:
                #self.notify.debug('oldAcc=%s, keepDist=%s newAcc=%s' %
                #                  (oldAcc,keepDistanceVector, acc))
                pass

        # Step 8:  Constrain acceleration
        # If our acceleration change is more than we allow, constrain it

        if (acc.length() > self.maxAcceleration):
            # definitely too much...constrain to maximum change
            acc.normalize()
            acc *= self.maxAcceleration

        # Step 9:  Implementation.
        # Here's where we apply our newly computed acceleration vector
        # to create a new velocity vector to use next update cycle.

        self.oldVelocity = self.velocity
        # save off our previous velocity

        # now add in the acceleration

        self.velocity += acc

        # Step 10:  constraint Y velocity changes.
        # Attempt to restrict flight straight up/down by damping out Y axis velocity.
        # This isn't strictly necessary, but does lead to more realistic looking flight.

        # Step 11:  Constrain our speed.
        # If we're moving faster than we're allowed to move, constrain our velocity.
        if self.velocity.length() > self.maxVelocity:
            self.velocity.normalize()
            self.velocity *= self.maxVelocity

        # Step 12:  Compute roll/pitch/yaw.
        # Compute our orientation after all this speed adjustment nonsense.
        # bah no need, we turn on a dime towards our velocity
        forwardVec = Vec3(1, 0, 0)
        heading = rad2Deg(math.atan2(self.velocity[1], self.velocity[0]))
        heading -= 90
        self.suit.setH(heading)

    def getTargetVector(self):
        """Return a vector to my goal."""
        targetPos = Point3(0, 0, 0)
        if self.goal == CTGG.ToonGoal:
            av = self.game.getAvatar(self.goalId)
            if av:
                targetPos = av.getPos()
        elif self.goal == CTGG.BarrelGoal:
            barrel = self.game.barrels[self.goalId]
            targetPos = barrel.getPos()
        elif self.goal == CTGG.RunAwayGoal:
            targetPos = CTGG.CogReturnPositions[self.goalId]
        targetPos.setZ(0)
        myPos = self.suit.getPos()
        diff = targetPos - myPos
        if diff.length() > 1.0:
            diff.normalize()
            diff *= 1.0

        return diff

    def accumulate(self, accumulator, valueToAdd):
        """Return the magnitude of the accumulated vector."""

        accumulator += valueToAdd

        return accumulator.length()

    def seeFriends(self):
        """Determines which flockmates a given flock boid can see."""
        # clear the existing visibility list of any holdover from last round

        self.clearVisibleList()

        for cogIndex in list(self.game.cogInfo.keys()):
            if cogIndex == self.cogIndex:
                continue

            if self.sameGoal(cogIndex):
                dist = self.canISee(cogIndex)
                if dist != self.Infinity:
                    self.addToVisibleList(cogIndex)
                    if dist < self.distToNearestFlockmate:
                        self.nearestFlockmate = cogIndex
                        self.distToNearestFlockmate = dist

        return self.numFlockmatesSeen

    def clearVisibleList(self):
        """Clears the visibility list and associated fields."""
        self.visibleFriendsList = []
        self.numFlockmatesSeen = 0
        self.nearestFlockmate = None
        self.distToNearestFlockmate = self.Infinity

    def addToVisibleList(self, cogIndex):
        """Add the cog to the visible list."""
        # test:  do we see enough buddies already?
        if self.numFlockmatesSeen < self.MaxFriendsVisible:
            #nope--we can add to this one to the list
            self.visibleFriendsList.append(cogIndex)
            self.numFlockmatesSeen += 1
            if self.cogIndex == 0:
                #self.notify.debug('self.numFlockmatesSeen = %s' % self.numFlockmatesSeen)
                pass

    def canISee(self, cogIndex):
        """Return distance if I can see the other cog, infinity otherwise"""

        if self.cogIndex == cogIndex:
            # well we should never see ourself
            return self.Infinity

        cogThief = self.game.getCogThief(cogIndex)
        distance = self.suit.getDistance(cogThief.suit)

        if distance < self.perceptionRange:
            #self.notify.debug('%s can see %s' % (self.cogIndex, cogIndex))
            return distance

        # fell through; can not see it
        return self.Infinity

    def sameGoal(self, cogIndex):
        """Return true if we have the same goal."""
        cogThief = self.game.getCogThief(cogIndex)
        result = (cogThief.goalId == self.goalId) and (cogThief.goal
                                                       == self.goal)
        return result

    def keepDistance(self):
        """Generates a vector for a flock boid to maintain his
        desired separation distance from the nearest flockmate he sees.
        """
        ratio = self.distToNearestFlockmate / self.SeparationDistance
        nearestThief = self.game.getCogThief(self.nearestFlockmate)
        change = nearestThief.suit.getPos() - self.suit.getPos()

        if ratio < self.MinUrgency:
            ratio = self.MinUrgency
        if ratio > self.MaxUrgency:
            ratio = self.MaxUrgency

        # test:  are we too close to our nearest flockmate?
        if self.distToNearestFlockmate < self.SeparationDistance:
            #self.notify.debug('%d is too close to %d' % (self.cogIndex, self.nearestFlockmate))

            # too close...move away from our neighbor
            change.normalize()
            change *= -(1 - ratio
                        )  # the close we are the more we are pushed away
        elif self.distToNearestFlockmate > self.SeparationDistance:
            # too far away move towards our neighbor
            change.normalize()
            change *= ratio
        else:
            # in the UNLIKELY event we're exactly the right distance away, do nothing
            change = Vec3(0, 0, 0)

        return change

    def showKaboom(self):
        """Show the kaboom graphic and sound."""
        if self.kaboomTrack and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.kaboom.reparentTo(render)
        self.kaboom.setPos(self.suit.getPos())
        self.kaboom.setZ(3)

        self.kaboomTrack = Parallel(
            SoundInterval(self.kaboomSound, volume=0.5),
            Sequence(
                Func(self.kaboom.showThrough),
                LerpScaleInterval(self.kaboom,
                                  duration=0.5,
                                  scale=Point3(10, 10, 10),
                                  startScale=Point3(1, 1, 1),
                                  blendType='easeOut'),
                Func(self.kaboom.hide),
            ))
        self.kaboomTrack.start()

    def showSplat(self):
        """Show the splat graphic and sound."""
        if self.kaboomTrack and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.splat.reparentTo(render)
        self.splat.setPos(self.suit.getPos())
        self.splat.setZ(3)

        self.kaboomTrack = Parallel(
            SoundInterval(self.pieHitSound, volume=1.0),
            Sequence(
                Func(self.splat.showThrough),
                LerpScaleInterval(self.splat,
                                  duration=0.5,
                                  scale=1.75,
                                  startScale=Point3(0.1, 0.1, 0.1),
                                  blendType='easeOut'),
                Func(self.splat.hide),
            ))
        self.kaboomTrack.start()
Exemplo n.º 16
0
class PartyCog(FSM):
    notify = directNotify.newCategory("PartyCog")

    HpTextGenerator = TextNode("HpTextGenerator")
    hpText = None
    height = 7

    def __init__(self,
                 parentNode,
                 id,
                 bounceSpeed=3,
                 bounceHeight=1,
                 rotateSpeed=1,
                 heightShift=1,
                 xMoveSpeed=0,
                 xMoveDistance=0,
                 bounceOffset=0):
        self.id = id

        FSM.__init__(self, "PartyCogFSM-%d" % self.id)

        self.showFacingStatus = False
        self.xMoveSpeed = xMoveSpeed
        self.xMoveDistance = xMoveDistance
        self.heightShift = heightShift
        self.bounceSpeed = bounceSpeed
        self.bounceHeight = bounceHeight
        self.rotateSpeed = rotateSpeed
        self.parentNode = parentNode
        self.bounceOffset = bounceOffset
        self.hitInterval = None
        self.kaboomTrack = None
        self.resetRollIval = None
        self.netTimeSentToStartByHit = 0

        self.load()
        self.request("Down")

    def load(self):
        self.root = NodePath("PartyCog-%d" % self.id)
        self.root.reparentTo(self.parentNode)

        path = "phase_13/models/parties/cogPinata_"
        self.actor = Actor(
            path + "actor", {
                "idle": path + "idle_anim",
                "down": path + "down_anim",
                "up": path + "up_anim",
                "bodyHitBack": path + "bodyHitBack_anim",
                "bodyHitFront": path + "bodyHitFront_anim",
                "headHitBack": path + "headHitBack_anim",
                "headHitFront": path + "headHitFront_anim",
            })
        self.actor.reparentTo(self.root)

        self.temp_transform = Mat4()
        self.head_locator = self.actor.attachNewNode("temphead")

        self.bodyColl = CollisionTube(0, 0, 1, 0, 0, 5.75, 0.75)
        self.bodyColl.setTangible(1)
        self.bodyCollNode = CollisionNode("PartyCog-%d-Body-Collision" %
                                          self.id)
        self.bodyCollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.bodyCollNode.addSolid(self.bodyColl)
        self.bodyCollNodePath = self.root.attachNewNode(self.bodyCollNode)

        self.headColl = CollisionTube(0, 0, 3, 0, 0, 3.0, 1.5)
        self.headColl.setTangible(1)
        self.headCollNode = CollisionNode("PartyCog-%d-Head-Collision" %
                                          self.id)
        self.headCollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.headCollNode.addSolid(self.headColl)
        self.headCollNodePath = self.root.attachNewNode(self.headCollNode)

        # Cog's Left Arm
        self.arm1Coll = CollisionSphere(1.65, 0, 3.95, 1.0)
        self.arm1Coll.setTangible(1)
        self.arm1CollNode = CollisionNode("PartyCog-%d-Arm1-Collision" %
                                          self.id)
        self.arm1CollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.arm1CollNode.addSolid(self.arm1Coll)
        self.arm1CollNodePath = self.root.attachNewNode(self.arm1CollNode)

        # Cog's Right Arm
        self.arm2Coll = CollisionSphere(-1.65, 0, 3.45, 1.0)
        self.arm2Coll.setTangible(1)
        self.arm2CollNode = CollisionNode("PartyCog-%d-Arm2-Collision" %
                                          self.id)
        self.arm2CollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.arm2CollNode.addSolid(self.arm2Coll)
        self.arm2CollNodePath = self.root.attachNewNode(self.arm2CollNode)

        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splatType = globalPropPool.getPropType(splatName)

        self.pieHitSound = globalBattleSoundCache.getSound(
            'AA_wholepie_only.mp3')
        self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.mp3')

        self.hole = loader.loadModel("phase_13/models/parties/cogPinataHole")
        self.hole.setTransparency(True)
        self.hole.setP(-90.0)
        self.hole.setScale(3)
        self.hole.setBin("ground", 3)
        self.hole.reparentTo(self.parentNode)

    def unload(self):
        self.request("Off")
        self.clearHitInterval()

        if self.hole is not None:
            self.hole.removeNode()
            self.hole = None

        if self.actor is not None:
            self.actor.cleanup()
            self.actor.removeNode()
            self.actor = None

        if self.root is not None:
            self.root.removeNode()
            self.root = None

        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.kaboomTrack = None

        if self.resetRollIval is not None and self.resetRollIval.isPlaying():
            self.resetRollIval.finish()
        self.resetRollIval = None

        if self.hitInterval is not None and self.hitInterval.isPlaying():
            self.hitInterval.finish()
        self.hitInterval = None

        del self.upSound
        del self.pieHitSound

#===============================================================================
# FSM States
#===============================================================================

    def enterStatic(self):
        pass

    def exitStatic(self):
        pass

    def enterActive(self, startTime):
        self.root.setR(0.0)

        updateTask = Task.Task(self.updateTask)
        updateTask.startTime = startTime

        taskMgr.add(updateTask, "PartyCog.update-%d" % self.id)

    def exitActive(self):
        taskMgr.remove("PartyCog.update-%d" % self.id)
        taskMgr.remove("PartyCog.bounceTask-%d" % self.id)

        self.clearHitInterval()
        self.resetRollIval = self.root.hprInterval(0.5,
                                                   Point3(
                                                       self.root.getH(), 0.0,
                                                       0.0),
                                                   blendType="easeInOut")
        self.resetRollIval.start()

        self.actor.stop()

    def enterDown(self):
        if self.oldState == "Off":
            self.actor.pose("down", self.actor.getNumFrames("down") - 1)
            return

        self.clearHitInterval()
        startScale = self.hole.getScale()
        endScale = Point3(5, 5, 5)
        self.hitInterval = Sequence(
            LerpFunc(self.setAlongSpline,
                     duration=1.0,
                     fromData=self.currentT,
                     toData=0.0),
            LerpScaleInterval(self.hole,
                              duration=0.175,
                              scale=endScale,
                              startScale=startScale,
                              blendType="easeIn"),
            Parallel(
                SoundInterval(self.upSound,
                              volume=0.6,
                              node=self.actor,
                              cutOff=PartyGlobals.PARTY_COG_CUTOFF),
                ActorInterval(self.actor, "down", loop=0),
            ),
            LerpScaleInterval(self.hole,
                              duration=0.175,
                              scale=Point3(3, 3, 3),
                              startScale=endScale,
                              blendType="easeOut"),
        )
        self.hitInterval.start()

    def exitDown(self):
        self.root.setR(0.0)
        self.root.setH(0.0)
        self.targetDistance = 0.0
        self.targetFacing = 0.0
        self.currentT = 0.0
        self.setAlongSpline(0.0)
        self.clearHitInterval()
        startScale = self.hole.getScale()
        endScale = Point3(5, 5, 5)
        self.hitInterval = Sequence(
            LerpScaleInterval(self.hole,
                              duration=0.175,
                              scale=endScale,
                              startScale=startScale,
                              blendType="easeIn"),
            Parallel(
                SoundInterval(self.upSound,
                              volume=0.6,
                              node=self.actor,
                              cutOff=PartyGlobals.PARTY_COG_CUTOFF),
                ActorInterval(self.actor, "up", loop=0),
            ),
            Func(self.actor.loop, "idle"),
            LerpScaleInterval(self.hole,
                              duration=0.175,
                              scale=Point3(3, 3, 3),
                              startScale=endScale,
                              blendType="easeOut"),
        )
        self.hitInterval.start()

    def filterDown(self, request, args):
        if request == "Down":
            return None
        else:
            return self.defaultFilter(request, args)

#------------------------------------------------------------------------------

    def setEndPoints(self, start, end, amplitude=1.7):
        self.sinAmplitude = amplitude
        self.sinPeriod = (end.getX() - start.getX()) / 2
        self.sinDisplacement = start.getY()
        self.startPoint = start
        self.endPoint = end
        self.currentT = 0.0
        self.targetDistance = 0.0
        self.currentFacing = 0.0
        self.targetFacing = 0.0
        self.setAlongSpline(self.currentT)
        self.hole.setPos(self.root.getPos())
        self.hole.setZ(0.02)

    def rockBackAndForth(self, task):
        t = task.startTime + task.time
        angle = math.sin(t) * 20.0

        self.root.setR(angle)

        #        if self.id == 0:
        #            print angle

        return task.cont

    def updateDistance(self, distance):
        self.targetDistance = clamp(distance, -1.0, 1.0)

    def updateTask(self, task):
        self.rockBackAndForth(task)

        if self.targetDistance > self.currentT:
            self.currentT += min(0.01, self.targetDistance - self.currentT)
            self.setAlongSpline(self.currentT)
        elif self.targetDistance < self.currentT:
            self.currentT += max(-0.01, self.targetDistance - self.currentT)
            self.setAlongSpline(self.currentT)

        if self.currentT < 0.0:
            self.targetFacing = -90.0
        elif self.currentT > 0.0:
            self.targetFacing = 90.0
        else:
            self.targetFacing = 0.0

        if self.targetFacing > self.currentFacing:
            self.currentFacing += min(10,
                                      self.targetFacing - self.currentFacing)
        elif self.targetFacing < self.currentFacing:
            self.currentFacing += max(-10,
                                      self.targetFacing - self.currentFacing)

        self.root.setH(self.currentFacing)

        return task.cont

    def setAlongSpline(self, t):
        t = t + 1.0
        dist = (self.endPoint.getX() - self.startPoint.getX()) / 2.0
        x = self.startPoint.getX() + t * dist
        y = self.startPoint.getY() - math.sin(
            t * 2 * math.pi) * self.sinAmplitude
        self.root.setPos(x, y, 0)

    def startBounce(self):
        taskMgr.add(self.bounce, "PartyCog.bounceTask-%d" % self.id)

    def bounce(self, task):
        #self.root.setH(self.root.getH() - self.rotateSpeed)
        self.root.setZ((math.sin((self.bounceOffset + task.time) *
                                 self.bounceSpeed) * self.bounceHeight) +
                       self.heightShift)

        return task.cont

    def setPos(self, position):
        self.root.setPos(position)

    def respondToPieHit(self, timestamp, position, hot=False, direction=1.0):
        """The toon hit us, react appropriately."""
        assert (self.notify.debugStateCall(self))

        if self.netTimeSentToStartByHit < timestamp:
            self.__showSplat(position, direction, hot)

            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            #self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit' % (localStamp, self.lastLocalTimeStampFromAI))
            self.notify.debug(
                'respondToPieHit self.netTimeSentToStartByHit = %s' %
                self.netTimeSentToStartByHit)

    def clearHitInterval(self):
        if self.hitInterval is not None and self.hitInterval.isPlaying():
            self.hitInterval.clearToInitial()

    def __showSplat(self, position, direction, hot=False):
        """Show the splat graphic and sound."""
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()

        self.clearHitInterval()
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()

        self.splat.reparentTo(render)
        self.splat.setPos(self.root, position)
        self.splat.setAlphaScale(1.0)

        if not direction == 1.0:
            #self.splat.setColorScale(Vec4(0.0, 0.0, 50.0, 1.0))
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0])
            if self.currentFacing > 0.0:
                facing = "HitFront"
            else:
                facing = "HitBack"
        else:
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1])
            #self.splat.setColorScale(Vec4(1.0, 0.6, 0.08, 1.0))
            if self.currentFacing > 0.0:
                facing = "HitBack"
            else:
                facing = "HitFront"

        if hot:
            targetscale = 0.75
            part = "head"
        else:
            targetscale = 0.5
            part = "body"

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.hitInterval = Sequence(
            ActorInterval(self.actor, part + facing, loop=0),
            Func(self.actor.loop, "idle"),
        )
        self.hitInterval.start()

        self.kaboomTrack = Parallel(
            SoundInterval(self.pieHitSound,
                          volume=1.0,
                          node=self.actor,
                          cutOff=PartyGlobals.PARTY_COG_CUTOFF),
            Sequence(
                Func(self.splat.showThrough),
                Parallel(
                    Sequence(
                        LerpScaleInterval(self.splat,
                                          duration=0.175,
                                          scale=targetscale,
                                          startScale=Point3(0.1, 0.1, 0.1),
                                          blendType="easeOut"),
                        Wait(0.175),
                    ),
                    Sequence(
                        Wait(0.1),
                        LerpFunc(
                            setSplatAlpha,
                            duration=1.0,  #0.4,
                            fromData=1.0,
                            toData=0.0,
                            blendType="easeOut"))),
                Func(self.splat.cleanup),
                Func(self.splat.removeNode),
            ))
        self.kaboomTrack.start()

    def showHitScore(self, number, scale=1):
        """
        Shows the hit score.
        Borrowed from otp.avatar.DistributedAvatar.showHpText
        """
        if number <= 0:
            return

        # Get rid of the number if it is already there.
        if self.hpText:
            self.hideHitScore()

        # Set the font
        self.HpTextGenerator.setFont(ToontownGlobals.getSignFont())

        # Show both negative and positive signs
        if number < 0:
            self.HpTextGenerator.setText(str(number))
        else:
            self.HpTextGenerator.setText("+" + str(number))

        # No shadow
        self.HpTextGenerator.clearShadow()

        # Center the number
        self.HpTextGenerator.setAlign(TextNode.ACenter)

        # Red, always
        #if number < 0:
        r = 1  #0.9
        g = 1  #0
        b = 0
        a = 1

        self.HpTextGenerator.setTextColor(r, g, b, a)

        self.hpTextNode = self.HpTextGenerator.generate()

        # Put the hpText over the head of the avatar
        self.hpText = render.attachNewNode(self.hpTextNode)
        self.hpText.setScale(scale)
        # Make sure it is a billboard
        self.hpText.setBillboardPointEye()
        # Render it after other things in the scene.
        self.hpText.setBin('fixed', 100)

        # Initial position ... Center of the body... the "tan tien"
        self.hpText.setPos(self.root, 0, 0, self.height / 2)

        # Black magic from the early days of Panda3D, later replaced by a Sequence
        seq = Task.sequence(
            # Fly the number out of the character
            self.hpText.lerpPos(Point3(
                self.root.getX(render), self.root.getY(render),
                self.root.getZ(render) + self.height + 1.0),
                                0.25,
                                blendType='easeOut'),
            Task.pause(0.25),
            # Fade the number
            self.hpText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.1),
            # Get rid of the number
            Task.Task(self.__hideHitScoreTask))

        taskMgr.add(seq, "PartyCogHpText" + str(self.id))

    def __hideHitScoreTask(self, task):
        self.hideHitScore()

        return Task.done

    def hideHitScore(self):
        if self.hpText:
            taskMgr.remove("PartyCogHpText" + str(self.id))
            self.hpText.removeNode()
            self.hpText = None

    def getHeadLocation(self):
        (self.actor.getJoints(jointName="head")[0]).getNetTransform(
            self.temp_transform)
        self.head_locator.setMat(self.temp_transform)
        #print self.head_locator.getZ()

        return self.head_locator.getZ(self.root)
Exemplo n.º 17
0
class DistributedFireworksCannon(DistributedFireworkShow.DistributedFireworkShow):

    notify = directNotify.newCategory("DistributedFireworksCannon")

    def __init__(self, cr):
        DistributedFireworkShow.DistributedFireworkShow.__init__(self,cr)
        self.fireworksGui = None
        self.load()

    def generateInit(self):
        DistributedFireworkShow.DistributedFireworkShow.generateInit(self)
        """generateInit(self)
        This method is called when the DistributedObject is first introduced
        to the world... Not when it is pulled from the cache.
        """
        self.fireworksSphereEvent = self.uniqueName("fireworksSphere")
        self.fireworksSphereEnterEvent = "enter" + self.fireworksSphereEvent
        self.fireworksGuiDoneEvent = "fireworksGuiDone"
        self.shootEvent = "fireworkShootEvent"

        self.collSphere = CollisionSphere(0, 0, 0, 2.5)
        # Make the sphere intangible
        self.collSphere.setTangible(1)
        self.collNode = CollisionNode(self.fireworksSphereEvent)
        self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.geom.attachNewNode(self.collNode)

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

    def announceGenerate(self):
        self.notify.debug("announceGenerate")
        self.accept(self.fireworksSphereEnterEvent,  self.__handleEnterSphere)

    def disable(self):
        self.notify.debug("disable")
        self.ignore(self.fireworksSphereEnterEvent)
        self.ignore(self.shootEvent)
        self.ignore(self.fireworksGuiDoneEvent)
        if self.fireworksGui:
            self.fireworksGui.destroy()
            self.fireworksGui = None
        DistributedFireworkShow.DistributedFireworkShow.disable(self)

    def delete(self):
        self.notify.debug("delete")
        self.geom.removeNode()
        DistributedFireworkShow.DistributedFireworkShow.delete(self)

    def load(self):
        self.geom = loader.loadModel("phase_5/models/props/trashcan_TT.bam")
        self.geom.reparentTo(base.cr.playGame.hood.loader.geom)
        self.geom.setScale(.5)

    def __handleEnterSphere(self, collEntry):
        self.notify.debug("handleEnterSphere()")
        # don't let other toons access the fireworks now
        self.ignore(self.fireworksSphereEnterEvent)
        self.sendUpdate("avatarEnter", [])

    def __handleFireworksDone(self):
        self.ignore(self.fireworksGuiDoneEvent)
        self.ignore(self.shootEvent)
        self.sendUpdate("avatarExit")
        self.fireworksGui.destroy()
        self.fireworksGui = None

    def freeAvatar(self):
        """
        This is a message from the AI used to free the avatar from movie mode
        """
        # If we are the local toon and we have simply taken too long,
        # just free us
        base.localAvatar.posCamera(0,0)
        base.cr.playGame.getPlace().setState("walk")
        # Start accepting the fireworks collision sphere event again
        self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere)


    def setMovie(self, mode, avId, timestamp):
        """
        This is a message from the AI describing a movie between this fireworks
        cannon and a Toon that has approached us.
        """
        timeStamp = globalClockDelta.localElapsedTime(timestamp)
        # See if this is the local toon
        isLocalToon = (avId == base.localAvatar.doId)

        if (mode == FIREWORKS_MOVIE_CLEAR):
            self.notify.debug("setMovie: clear")
            return
        elif (mode == FIREWORKS_MOVIE_GUI):
            self.notify.debug("setMovie: gui")
            if isLocalToon:
                self.fireworksGui = FireworksGui.FireworksGui(self.fireworksGuiDoneEvent, self.shootEvent)
                self.accept(self.fireworksGuiDoneEvent, self.__handleFireworksDone)
                self.accept(self.shootEvent, self.localShootFirework)
            return
        else:
            self.notify.warning("unknown mode in setMovie: %s" % (mode))

    def setPosition(self, x, y, z):
        self.pos = [x,y,z]
        self.geom.setPos(x,y,z)

    def localShootFirework(self, index):
        style = index
        col1,col2 = self.fireworksGui.getCurColor()
        amp = 30

        # attach a dummy node to the toon that always in front of him.
        # This is where the fireworks will shoot from.
        dummy = base.localAvatar.attachNewNode("dummy")
        dummy.setPos(0,100,60)
        pos = dummy.getPos(render)
        dummy.removeNode()

        print(("lauFirework: %s, col=%s" % (index,col1)))
        self.d_requestFirework(pos[0],pos[1],pos[2],style,col1,col2)
Exemplo n.º 18
0
class CogdoGameGatherable(NodePath, DirectObject):
    EnterEventName = 'CogdoGameGatherable_Enter'

    def __init__(self,
                 serialNum,
                 model,
                 triggerRadius,
                 triggerOffset=(0, 0, 0),
                 animate=True,
                 animDuration=0.2,
                 instanceModel=True,
                 name='CogdoGameGatherable'):
        NodePath.__init__(self, '%s-%d' % (name, serialNum))
        self.serialNum = serialNum
        self._animate = animate
        if instanceModel:
            model.instanceTo(self)
            self._model = self
        else:
            self._model = model
            self._model.reparentTo(self)
            self._model.setPosHpr(0, 0, 0, 0, 0, 0)
        self._animDuration = animDuration
        self._animSeq = None
        self._initCollisions(triggerRadius, triggerOffset)
        self._update = None
        self._wasPickedUp = False
        return

    def _initCollisions(self, triggerRadius, triggerOffset):
        self.collSphere = CollisionSphere(triggerOffset[0], triggerOffset[1],
                                          triggerOffset[2], triggerRadius)
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode(self.getName())
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.attachNewNode(self.collNode)

    def destroy(self):
        self.disable()
        del self._model
        if self._animSeq is not None:
            self._animSeq.finish()
            self._animSeq = None
        self.collNodePath.removeNode()
        self.removeNode()
        return

    def enable(self):
        self.accept('enter' + self.getName(), self._handleEnterCollision)
        self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)

    def disable(self):
        self.ignoreAll()
        self.collNode.setIntoCollideMask(BitMask32(0))

    def show(self):
        if not self.wasPickedUp():
            NodePath.show(self)
            self.enable()

    def hide(self):
        self.disable()
        NodePath.hide(self)

    def _handleEnterCollision(self, collEntry):
        messenger.send(CogdoGameGatherable.EnterEventName, [self])

    def wasPickedUp(self):
        return self._wasPickedUp

    def wasPickedUpByToon(self):
        pass

    def update(self, dt):
        pass

    def getModel(self):
        return self._model

    def pickUp(self, toon, elapsedSeconds=0.0):
        self._wasPickedUp = True
        if self._animSeq is not None:
            self._animSeq.finish()
            self._animSeq = None
        if self._animate:

            def lerpFlyToToon(t):
                vec = toon.getPos(render) - self.getPos(render)
                vec[2] += toon.getHeight()
                self.setPos(self.getPos() + vec * t)
                self.setScale(1.0 - t * 0.8)

            self._animSeq = Sequence(
                LerpFunc(lerpFlyToToon,
                         fromData=0.0,
                         toData=1.0,
                         duration=self._animDuration), Wait(0.1),
                Func(self.hide))
            self._animSeq.start(elapsedSeconds)
        else:
            self.hide()
        return
Exemplo n.º 19
0
class CogdoFlyingLegalEagle(FSM, DirectObject):
    CollSphereName = 'CogdoFlyingLegalEagleSphere'
    CollisionEventName = 'CogdoFlyingLegalEagleCollision'
    InterestCollName = 'CogdoFlyingLegalEagleInterestCollision'
    RequestAddTargetEventName = 'CogdoFlyingLegalEagleRequestTargetEvent'
    RequestAddTargetAgainEventName = 'CogdoFlyingLegalEagleRequestTargetAgainEvent'
    RequestRemoveTargetEventName = 'CogdoFlyingLegalEagleRemoveTargetEvent'
    ForceRemoveTargetEventName = 'CogdoFlyingLegalEagleForceRemoveTargetEvent'
    EnterLegalEagle = 'CogdoFlyingLegalEagleDamageToon'
    ChargingToAttackEventName = 'LegalEagleChargingToAttack'
    LockOnToonEventName = 'LegalEagleLockOnToon'
    CooldownEventName = 'LegalEagleCooldown'
    notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLegalEagle')

    def __init__(self, nest, index, suitDnaName = 'le'):
        FSM.__init__(self, 'CogdoFlyingLegalEagle')
        self.defaultTransitions = {'Off' : ['Roost'],
                                   'Roost' : ['TakeOff', 'Off'],
                                   'TakeOff' : ['LockOnToon', 'LandOnNest', 'Off'],
                                   'LockOnToon' : ['RetreatToNest', 'ChargeUpAttack', 'Off'],
                                   'ChargeUpAttack' : ['RetreatToNest', 'Attack', 'Off'],
                                   'Attack' : ['RetreatToSky', 'Off'],
                                   'RetreatToSky' : ['Cooldown', 'Off'],
                                   'Cooldown' : ['LockOnToon', 'LandOnNest', 'Off'],
                                   'RetreatToNest' : ['LandOnNest', 'Off'],
                                   'LandOnNest' : ['Roost', 'Off'],
                                  }
        self.index = index
        self.nest = nest
        self.target = None
        self.isEagleInterested = False
        self.collSphere = None
        self.suit = Suit.Suit()
        d = SuitDNA.SuitDNA()
        d.newSuit(suitDnaName)
        self.suit.setDNA(d)
        self.suit.reparentTo(render)
        swapAvatarShadowPlacer(self.suit, 'legalEagle-%sShadowPlacer' % index)
        self.suit.setPos(self.nest.getPos(render))
        self.suit.setHpr(-180, 0, 0)
        self.suit.stash()
        self.prop = None
        self.attachPropeller()
        head = self.suit.find('**/joint_head')
        self.interestConeOrigin = self.nest.attachNewNode('fakeHeadNodePath')
        self.interestConeOrigin.setPos(render, head.getPos(render) + Vec3(0, Globals.LegalEagle.InterestConeOffset, 0))
        self.attackTargetPos = None
        self.startOfRetreatToSkyPos = None
        pathModel = CogdoUtil.loadFlyingModel('legalEaglePaths')
        self.chargeUpMotionPath = Mopath.Mopath(name='chargeUpMotionPath-%i' % self.index)
        self.chargeUpMotionPath.loadNodePath(pathModel.find('**/charge_path'))
        self.retreatToSkyMotionPath = Mopath.Mopath(name='retreatToSkyMotionPath-%i' % self.index)
        self.retreatToSkyMotionPath.loadNodePath(pathModel.find('**/retreat_path'))
        audioMgr = base.cogdoGameAudioMgr
        self._screamSfx = audioMgr.createSfx('legalEagleScream', self.suit)
        self.initIntervals()
        return

    def attachPropeller(self):
        if self.prop == None:
            self.prop = BattleProps.globalPropPool.getProp('propeller')
            head = self.suit.find('**/joint_head')
            self.prop.reparentTo(head)
        return

    def detachPropeller(self):
        if self.prop:
            self.prop.cleanup()
            self.prop.removeNode()
            self.prop = None
        return

    def _getAnimationIval(self, animName, startFrame = 0, endFrame = None, duration = 1):
        if endFrame == None:
            self.suit.getNumFrames(animName) - 1
        frames = endFrame - startFrame
        frameRate = self.suit.getFrameRate(animName)
        newRate = frames / duration
        playRate = newRate / frameRate
        ival = Sequence(ActorInterval(self.suit, animName, playRate=playRate))
        return ival

    def initIntervals(self):
        dur = Globals.LegalEagle.LiftOffTime
        nestPos = self.nest.getPos(render)
        airPos = nestPos + Vec3(0.0, 0.0, Globals.LegalEagle.LiftOffHeight)
        self.takeOffSeq = Sequence(Parallel(Sequence(Wait(dur * 0.6), LerpPosInterval(self.suit, dur * 0.4, startPos=nestPos, pos=airPos, blendType='easeInOut'))), Wait(1.5), Func(self.request, 'next'), name='%s.takeOffSeq-%i' % (self.__class__.__name__, self.index))
        self.landOnNestPosLerp = LerpPosInterval(self.suit, 1.0, startPos=airPos, pos=nestPos, blendType='easeInOut')
        self.landingSeq = Sequence(Func(self.updateLandOnNestPosLerp), Parallel(self.landOnNestPosLerp), Func(self.request, 'next'), name='%s.landingSeq-%i' % (self.__class__.__name__, self.index))
        dur = Globals.LegalEagle.ChargeUpTime
        self.chargeUpPosLerp = LerpFunc(self.moveAlongChargeUpMopathFunc, fromData=0.0, toData=self.chargeUpMotionPath.getMaxT(), duration=dur, blendType='easeInOut')
        self.chargeUpAttackSeq = Sequence(Func(self.updateChargeUpPosLerp), self.chargeUpPosLerp, Func(self.request, 'next'), name='%s.chargeUpAttackSeq-%i' % (self.__class__.__name__, self.index))
        dur = Globals.LegalEagle.RetreatToNestTime
        self.retreatToNestPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=airPos, blendType='easeInOut')
        self.retreatToNestSeq = Sequence(Func(self.updateRetreatToNestPosLerp), self.retreatToNestPosLerp, Func(self.request, 'next'), name='%s.retreatToNestSeq-%i' % (self.__class__.__name__, self.index))
        dur = Globals.LegalEagle.RetreatToSkyTime
        self.retreatToSkyPosLerp = LerpFunc(self.moveAlongRetreatMopathFunc, fromData=0.0, toData=self.retreatToSkyMotionPath.getMaxT(), duration=dur, blendType='easeOut')
        self.retreatToSkySeq = Sequence(Func(self.updateRetreatToSkyPosLerp), self.retreatToSkyPosLerp, Func(self.request, 'next'), name='%s.retreatToSkySeq-%i' % (self.__class__.__name__, self.index))
        dur = Globals.LegalEagle.PreAttackTime
        self.preAttackLerpXY = LerpFunc(self.updateAttackXY, fromData=0.0, toData=1.0, duration=dur)
        self.preAttackLerpZ = LerpFunc(self.updateAttackZ, fromData=0.0, toData=1.0, duration=dur, blendType='easeOut')
        dur = Globals.LegalEagle.PostAttackTime
        self.postAttackPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=Vec3(0, 0, 0))
        self.attackSeq = Sequence(Parallel(self.preAttackLerpXY, self.preAttackLerpZ), Func(self.updatePostAttackPosLerp), self.postAttackPosLerp, Func(self.request, 'next'), name='%s.attackSeq-%i' % (self.__class__.__name__, self.index))
        dur = Globals.LegalEagle.CooldownTime
        self.cooldownSeq = Sequence(Wait(dur), Func(self.request, 'next'), name='%s.cooldownSeq-%i' % (self.__class__.__name__, self.index))
        self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14))
        self.hoverOverNestSeq = Sequence(ActorInterval(self.suit, 'landing', startFrame=10, endFrame=20, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=20, endFrame=10, playRate=0.5))

    def initCollision(self):
        self.collSphere = CollisionSphere(0, 0, 0, 0)
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode('%s-%s' % (self.CollSphereName, self.index))
        self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.suit.attachNewNode(self.collNode)
        self.collNodePath.hide()
        self.accept('enter%s-%s' % (self.CollSphereName, self.index), self.handleEnterSphere)
        self.setCollSphereToNest()

    def getInterestConeLength(self):
        return Globals.LegalEagle.InterestConeLength + Globals.LegalEagle.InterestConeOffset

    def isToonInView(self, toon):
        distanceThreshold = self.getInterestConeLength()
        angleThreshold = Globals.LegalEagle.InterestConeAngle
        toonPos = toon.getPos(render)
        nestPos = self.nest.getPos(render)
        distance = toon.getDistance(self.interestConeOrigin)
        if distance > distanceThreshold:
            return False
        if toonPos[1] > nestPos[1]:
            return False
        a = toon.getPos(render) - self.interestConeOrigin.getPos(render)
        a.normalize()
        b = Vec3(0, -1, 0)
        dotProduct = a.dot(b)
        angle = math.degrees(math.acos(dotProduct))
        if angle <= angleThreshold / 2.0:
            return True
        else:
            return False

    def update(self, dt, localPlayer):
        if Globals.Dev.NoLegalEagleAttacks:
            return
        inView = self.isToonInView(localPlayer.toon)
        if inView and not self.isEagleInterested:
            self.handleEnterInterest()
        elif inView and self.isEagleInterested:
            self.handleAgainInterest()
        elif not inView and self.isEagleInterested:
            self.handleExitInterest()

    def updateLockOnTask(self):
        dt = globalClock.getDt()
        targetPos = self.target.getPos(render)
        suitPos = self.suit.getPos(render)
        nestPos = self.nest.getPos(render)
        attackPos = Vec3(targetPos)
        attackPos[1] = nestPos[1] + Globals.LegalEagle.LockOnDistanceFromNest
        attackPos[2] += Globals.LegalEagle.VerticalOffset
        if attackPos[2] < nestPos[2]:
            attackPos[2] = nestPos[2]
        attackChangeVec = (attackPos - suitPos) * Globals.LegalEagle.LockOnSpeed
        self.suit.setPos(suitPos + attackChangeVec * dt)
        return Task.cont

    def updateAttackXY(self, value):
        if Globals.LegalEagle.EagleAttackShouldXCorrect:
            x = self.readyToAttackPos.getX() + (self.attackTargetPos.getX() - self.readyToAttackPos.getX()) * value
            self.suit.setX(x)
        y = self.readyToAttackPos.getY() + (self.attackTargetPos.getY() - self.readyToAttackPos.getY()) * value
        self.suit.setY(y)

    def updateAttackZ(self, value):
        z = self.readyToAttackPos.getZ() + (self.attackTargetPos.getZ() - self.readyToAttackPos.getZ()) * value
        self.suit.setZ(z)

    def moveAlongChargeUpMopathFunc(self, value):
        self.chargeUpMotionPath.goTo(self.suit, value)
        self.suit.setPos(self.suit.getPos() + self.startOfChargeUpPos)

    def moveAlongRetreatMopathFunc(self, value):
        self.retreatToSkyMotionPath.goTo(self.suit, value)
        self.suit.setPos(self.suit.getPos() + self.startOfRetreatToSkyPos)

    def updateChargeUpPosLerp(self):
        self.startOfChargeUpPos = self.suit.getPos(render)

    def updateLandOnNestPosLerp(self):
        self.landOnNestPosLerp.setStartPos(self.suit.getPos())

    def updateRetreatToNestPosLerp(self):
        self.retreatToNestPosLerp.setStartPos(self.suit.getPos())

    def updateRetreatToSkyPosLerp(self):
        self.startOfRetreatToSkyPos = self.suit.getPos(render)

    def updatePostAttackPosLerp(self):
        suitPos = self.suit.getPos(render)
        finalPos = suitPos + Vec3(0, -Globals.LegalEagle.PostAttackLength, 0)
        self.postAttackPosLerp.setStartPos(suitPos)
        self.postAttackPosLerp.setEndPos(finalPos)

    def handleEnterSphere(self, collEntry):
        self.notify.debug('handleEnterSphere:%i' % self.index)
        messenger.send(CogdoFlyingLegalEagle.EnterLegalEagle, [self, collEntry])

    def handleEnterInterest(self):
        self.notify.debug('handleEnterInterestColl:%i' % self.index)
        self.isEagleInterested = True
        messenger.send(CogdoFlyingLegalEagle.RequestAddTargetEventName, [self.index])

    def handleAgainInterest(self):
        self.isEagleInterested = True
        messenger.send(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName, [self.index])

    def handleExitInterest(self):
        self.notify.debug('handleExitInterestSphere:%i' % self.index)
        self.isEagleInterested = False
        messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [self.index])

    def hasTarget(self):
        if self.target != None:
            return True
        else:
            return False
        return

    def setTarget(self, toon, elapsedTime = 0.0):
        self.notify.debug('Setting eagle %i to target: %s, elapsed time: %s' % (self.index, toon.getName(), elapsedTime))
        self.target = toon
        if self.state == 'Roost':
            self.request('next', elapsedTime)
        if self.state == 'ChargeUpAttack':
            messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId])

    def clearTarget(self, elapsedTime = 0.0):
        self.notify.debug('Clearing target from eagle %i, elapsed time: %s' % (self.index, elapsedTime))
        messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId])
        self.target = None
        if self.state in ['LockOnToon']:
            self.request('next', elapsedTime)
        return

    def leaveCooldown(self, elapsedTime = 0.0):
        if self.state in ['Cooldown']:
            self.request('next', elapsedTime)

    def shouldBeInFrame(self):
        if self.state in ['TakeOff', 'LockOnToon', 'ChargeUpAttack']:
            return True
        elif self.state == 'Attack':
            distance = self.suit.getDistance(self.target)
            threshold = Globals.LegalEagle.EagleAndTargetDistCameraTrackThreshold
            suitPos = self.suit.getPos(render)
            targetPos = self.target.getPos(render)
            if distance > threshold and suitPos[1] > targetPos[1]:
                return True
        return False

    def getTarget(self):
        return self.target

    def onstage(self):
        self.suit.unstash()
        self.request('Roost')

    def offstage(self):
        self.suit.stash()
        self.request('Off')

    def gameStart(self, gameStartTime):
        self.gameStartTime = gameStartTime
        self.initCollision()

    def gameEnd(self):
        self.shutdownCollisions()

    def shutdownCollisions(self):
        self.ignoreAll()
        if self.collSphere != None:
            del self.collSphere
            self.collSphere = None
        if self.collNodePath != None:
            self.collNodePath.removeNode()
            del self.collNodePath
            self.collNodePath = None
        if self.collNode != None:
            del self.collNode
            self.collNode = None
        return

    def destroy(self):
        self.request('Off')
        self.detachPropeller()
        del self._screamSfx
        self.suit.cleanup()
        self.suit.removeNode()
        self.suit.delete()
        self.interestConeOrigin.removeNode()
        del self.interestConeOrigin
        self.nest = None
        self.target = None
        taskMgr.remove('updateLockOnTask-%i' % self.index)
        taskMgr.remove('exitLockOnToon-%i' % self.index)
        self.propTrack.clearToInitial()
        del self.propTrack
        del self.chargeUpMotionPath
        del self.retreatToSkyMotionPath
        self.takeOffSeq.clearToInitial()
        del self.takeOffSeq
        del self.landOnNestPosLerp
        self.landingSeq.clearToInitial()
        del self.landingSeq
        del self.chargeUpPosLerp
        self.chargeUpAttackSeq.clearToInitial()
        del self.chargeUpAttackSeq
        del self.retreatToNestPosLerp
        self.retreatToNestSeq.clearToInitial()
        del self.retreatToNestSeq
        del self.retreatToSkyPosLerp
        self.retreatToSkySeq.clearToInitial()
        del self.retreatToSkySeq
        del self.postAttackPosLerp
        self.attackSeq.clearToInitial()
        del self.attackSeq
        self.cooldownSeq.clearToInitial()
        del self.cooldownSeq
        self.hoverOverNestSeq.clearToInitial()
        del self.hoverOverNestSeq
        del self.preAttackLerpXY
        del self.preAttackLerpZ
        return

    def requestNext(self):
        self.request('next')

    def setCollSphereToNest(self):
        if hasattr(self, 'collSphere') and self.collSphere is not None:
            radius = Globals.LegalEagle.OnNestDamageSphereRadius
            self.collSphere.setCenter(Point3(0.0, -Globals.Level.LaffPowerupNestOffset[1], self.suit.getHeight() / 2.0))
            self.collSphere.setRadius(radius)
        return

    def setCollSphereToTargeting(self):
        if hasattr(self, 'collSphere') and self.collSphere is not None:
            radius = Globals.LegalEagle.DamageSphereRadius
            self.collSphere.setCenter(Point3(0, 0, radius * 2))
            self.collSphere.setRadius(radius)
        return

    def enterRoost(self):
        self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.hoverOverNestSeq.loop()
        self.propTrack.loop()
        self.setCollSphereToNest()

    def filterRoost(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            return 'TakeOff'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitRoost(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.hoverOverNestSeq.pause()
        self.setCollSphereToTargeting()

    def enterTakeOff(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        self.takeOffSeq.start(elapsedTime)
        self.hoverOverNestSeq.loop()

    def filterTakeOff(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            if self.hasTarget():
                return 'LockOnToon'
            else:
                return 'LandOnNest'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitTakeOff(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.takeOffSeq.clearToInitial()
        self.hoverOverNestSeq.pause()

    def enterLockOnToon(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        taskName = 'updateLockOnTask-%i' % self.index
        taskMgr.add(self.updateLockOnTask, taskName, 45, extraArgs=[])
        messenger.send(CogdoFlyingLegalEagle.LockOnToonEventName, [self.target.doId])
        range = self.target.getDistance(self.interestConeOrigin) / self.getInterestConeLength()
        range = clamp(range, 0.0, 1.0)
        dur = Globals.LegalEagle.LockOnTime
        if self.oldState == 'TakeOff':
            dur *= range
        else:
            dur += Globals.LegalEagle.ExtraPostCooldownTime
        taskName = 'exitLockOnToon-%i' % self.index
        taskMgr.doMethodLater(dur, self.requestNext, taskName, extraArgs=[])

    def filterLockOnToon(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            if self.hasTarget():
                return 'ChargeUpAttack'
            else:
                return 'RetreatToNest'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitLockOnToon(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        taskMgr.remove('updateLockOnTask-%i' % self.index)
        taskMgr.remove('exitLockOnToon-%i' % self.index)

    def enterChargeUpAttack(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        self.chargeUpAttackSeq.start(elapsedTime)
        messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId])

    def filterChargeUpAttack(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            if self.hasTarget():
                return 'Attack'
            else:
                return 'RetreatToNest'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitChargeUpAttack(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.chargeUpAttackSeq.clearToInitial()

    def enterAttack(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        self.attackTargetPos = self.target.getPos(render)
        targetState = self.target.animFSM.getCurrentState().getName()
        self._screamSfx.play()
        if targetState == 'jumpAirborne':
            self.attackTargetPos[2] += Globals.LegalEagle.VerticalOffset
        else:
            self.attackTargetPos[2] += Globals.LegalEagle.PlatformVerticalOffset
        self.readyToAttackPos = self.suit.getPos(render)
        self.attackSeq.start(elapsedTime)

    def filterAttack(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            return 'RetreatToSky'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitAttack(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.attackSeq.clearToInitial()
        taskMgr.remove('updateAttackPosTask-%i' % self.index)

    def enterRetreatToSky(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        self.retreatToSkySeq.start(elapsedTime)

    def filterRetreatToSky(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            return 'Cooldown'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitRetreatToSky(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.retreatToSkySeq.clearToInitial()

    def enterCooldown(self):
        if self.target != None:
            messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId])
        self.suit.stash()
        self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        return

    def filterCooldown(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            if self.hasTarget():
                return 'LockOnToon'
            else:
                return 'LandOnNest'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitCooldown(self):
        self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.suit.unstash()
        self.cooldownSeq.clearToInitial()
        if self.newState != 'Off':
            heightOffNest = Globals.LegalEagle.PostCooldownHeightOffNest
            nestPos = self.nest.getPos(render)
            if self.newState in ['LandOnNest']:
                self.suit.setPos(nestPos + Vec3(0, 0, heightOffNest))
            else:
                targetPos = self.target.getPos(render)
                attackPos = Vec3(targetPos)
                attackPos[1] = nestPos[1]
                attackPos[2] = nestPos[2] + heightOffNest
                self.suit.setPos(attackPos)

    def enterRetreatToNest(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        self.retreatToNestSeq.start(elapsedTime)

    def filterRetreatToNest(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            return 'LandOnNest'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitRetreatToNest(self):
        self.retreatToNestSeq.clearToInitial()

    def enterLandOnNest(self, elapsedTime = 0.0):
        self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState,
         self.oldState,
         self.newState,
         elapsedTime))
        self.landingSeq.start(elapsedTime)

    def filterLandOnNest(self, request, args):
        self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args))
        if request == self.state:
            return None
        elif request == 'next':
            if self.hasTarget():
                return 'TakeOff'
            else:
                return 'Roost'
        else:
            return self.defaultFilter(request, args)
        return None

    def exitLandOnNest(self):
        self.landingSeq.clearToInitial()