Exemplo n.º 1
0
 def loadModel(self):
     """Load the necessary assets."""
     self.hill = loader.loadModel("phase_12/models/bossbotHQ/mole_hole")
     self.hill.setZ(0.0)
     self.hill.reparentTo(self)
     #self.hill.setScale(20.0)
     # find the collision node of the hill and rename it
     #hillCollision = self.hill.find('**/dirt_collision')
     self.hillColName = 'moleHillCol-%d-%d' % (self.moleField.doId, self.index)
     #hillCollision.setName(self.hillColName)
     self.moleField.accept('enter' + self.hillColName, self.moleField.handleEnterHill)
     self.mole = self.attachNewNode("mole")#loader.loadModel("phase_12/models/bossbotHQ/mole_norm")
     self.mole.reparentTo(self)
     self.mole.setScale(0.75)
     self.mole.setZ(-2.5)
     self.moleHead = loader.loadModel("phase_12/models/bossbotHQ/mole_norm")
     self.moleHead.reparentTo(self.mole)
     # setup mole collision
     moleColName = "moleCol-%d-%s" % (self.moleField.doId,self.index)
     moleSphere = CollisionSphere(0, 0, 0, 1.0)
     collNode = CollisionNode(moleColName)
     collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
     collNode.addSolid(moleSphere)
     self.moleColNodePath = self.mole.attachNewNode(collNode)
     self.moleColNodePath.stash()
     self.moleColNodePath.setScale(1.0)
     self.moleField.accept('enter' + moleColName, self.moleField.handleEnterMole)
Exemplo n.º 2
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')
Exemplo n.º 3
0
 def setupFloorEventSphere(self, avatarNodePath, bitmask, avatarRadius):
     cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.75)
     cSphereNode = CollisionNode('Flyer.cFloorEventSphere')
     cSphereNode.addSolid(cSphere)
     cSphereNodePath = avatarNodePath.attachNewNode(cSphereNode)
     cSphereNode.setFromCollideMask(bitmask)
     cSphereNode.setIntoCollideMask(BitMask32.allOff())
     self.floorCollisionEvent = CollisionHandlerEvent()
     self.floorCollisionEvent.addInPattern('%fn-enter-%in')
     self.floorCollisionEvent.addAgainPattern('%fn-again-%in')
     self.floorCollisionEvent.addOutPattern('%fn-exit-%in')
     base.cTrav.addCollider(cSphereNodePath, self.floorCollisionEvent)
     self.cFloorEventSphereNodePath = cSphereNodePath
Exemplo n.º 4
0
 def getFlyBallBubble(self):
     if self.__flyBallBubble == None:
         bubble = CollisionSphere(0, 0, 0, GolfGlobals.GOLF_BALL_RADIUS)
         node = CollisionNode('flyBallBubble')
         node.addSolid(bubble)
         node.setFromCollideMask(ToontownGlobals.PieBitmask
                                 | ToontownGlobals.CameraBitmask
                                 | ToontownGlobals.FloorBitmask)
         node.setIntoCollideMask(BitMask32.allOff())
         self.__flyBallBubble = NodePath(node)
         self.flyBallHandler = CollisionHandlerEvent()
         self.flyBallHandler.addInPattern('flyBallHit-%d' % self.index)
         #self.flyBallHandler.addInPattern('flyBallHit-%d--%in')
     return self.__flyBallBubble
Exemplo n.º 5
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.º 6
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.º 7
0
 def setupWallSphere(self, bitmask, avatarRadius):
     self.avatarRadius = avatarRadius
     cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75,
                               self.avatarRadius)
     cSphereNode = CollisionNode('Flyer.cWallSphereNode')
     cSphereNode.addSolid(cSphere)
     cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode)
     cSphereNode.setFromCollideMask(bitmask)
     cSphereNode.setIntoCollideMask(BitMask32.allOff())
     if ConfigVariableBool('want-fluid-pusher', 0).getValue():
         self.pusher = CollisionHandlerFluidPusher()
     else:
         self.pusher = CollisionHandlerPusher()
     self.pusher.addCollider(cSphereNodePath, self.avatarNodePath)
     self.cWallSphereNodePath = cSphereNodePath
Exemplo n.º 8
0
 def setupRay(self, bitmask, floorOffset, reach):
     cRay = CollisionRay(0.0, 0.0, 3.0, 0.0, 0.0, -1.0)
     cRayNode = CollisionNode('Flyer.cRayNode')
     cRayNode.addSolid(cRay)
     self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
     cRayNode.setFromCollideMask(bitmask)
     cRayNode.setIntoCollideMask(BitMask32.allOff())
     self.lifter = CollisionHandlerGravity()
     self.lifter.setLegacyMode(self._legacyLifter)
     self.lifter.setGravity(self.getGravity(0))
     self.lifter.addInPattern('%fn-enter-%in')
     self.lifter.addAgainPattern('%fn-again-%in')
     self.lifter.addOutPattern('%fn-exit-%in')
     self.lifter.setOffset(floorOffset)
     self.lifter.setReach(reach)
     self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath)
Exemplo n.º 9
0
class CogdoMazeSplattable:

    def __init__(self, object, name, collisionRadius):
        self.object = object
        self.splat = CogdoUtil.loadMazeModel('splash')
        self.splat.setBillboardPointEye()
        self.splat.setBin('fixed', 40)
        self.splat.setDepthTest(False)
        self.splat.setDepthWrite(False)
        self.splatTrack = None
        self._splatSfxIval = base.cogdoGameAudioMgr.createSfxIval('splat')
        self.initGagCollision(name, collisionRadius)
        return

    def destroy(self):
        self.disableGagCollision()
        if self._splatSfxIval.isPlaying():
            self._splatSfxIval.finish()
        del self._splatSfxIval

    def initGagCollision(self, name, radius):
        self.gagCollisionName = name
        collision = CollisionTube(0, 0, 0, 0, 0, 4, radius)
        collision.setTangible(1)
        self.gagCollNode = CollisionNode(self.gagCollisionName)
        self.gagCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask)
        self.gagCollNode.addSolid(collision)
        self.gagCollNodePath = self.object.attachNewNode(self.gagCollNode)

    def disableGagCollision(self):
        self.gagCollNodePath.removeNode()

    def doSplat(self):
        if self.splatTrack and self.splatTrack.isPlaying():
            self.splatTrack.finish()
        self.splat.reparentTo(render)
        self.splat.setPos(self.object, 0, 0, 3.0)
        self.splat.setY(self.splat.getY() - 1.0)
        self._splatSfxIval.node = self.splat
        self.splatTrack = Parallel(self._splatSfxIval, Sequence(Func(self.splat.showThrough), LerpScaleInterval(self.splat, duration=0.5, scale=6, startScale=1, blendType='easeOut'), Func(self.splat.hide)))
        self.splatTrack.start()
Exemplo n.º 10
0
 def _initCollisions(self):
     self._camCollRay = CollisionRay()
     camCollNode = CollisionNode('CameraToonRay')
     camCollNode.addSolid(self._camCollRay)
     camCollNode.setFromCollideMask(OTPGlobals.WallBitmask
                                    | OTPGlobals.CameraBitmask
                                    | ToontownGlobals.FloorEventBitmask
                                    | ToontownGlobals.CeilingBitmask)
     camCollNode.setIntoCollideMask(0)
     self._camCollNP = self._camera.attachNewNode(camCollNode)
     self._camCollNP.show()
     self._collOffset = Vec3(0, 0, 0.5)
     self._collHandler = CollisionHandlerQueue()
     self._collTrav = CollisionTraverser()
     self._collTrav.addCollider(self._camCollNP, self._collHandler)
     self._betweenCamAndToon = {}
     self._transNP = NodePath('trans')
     self._transNP.reparentTo(render)
     self._transNP.setTransparency(True)
     self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon)
     self._transNP.setBin('fixed', 10000)
Exemplo n.º 11
0
class CogdoFlyingLevel(DirectObject):
    notify = directNotify.newCategory('CogdoFlyingLevel')

    def __init__(self, parent, frameModel, startPlatformModel,
                 endPlatformModel, quadLengthUnits, quadVisibilityAhead,
                 quadVisibiltyBehind):
        self.parent = parent
        self.quadLengthUnits = quadLengthUnits
        self._halfQuadLengthUnits = quadLengthUnits / 2.0
        self.quadVisibiltyAhead = quadVisibilityAhead
        self.quadVisibiltyBehind = quadVisibiltyBehind
        self._frameModel = frameModel
        self.root = NodePath('CogdoFlyingLevel')
        self.quadrantRoot = NodePath('QuadrantsRoot')
        self.quadrantRoot.reparentTo(self.root)
        self._startPlatformModel = startPlatformModel
        self._startPlatformModel.reparentTo(self.root)
        self._startPlatformModel.setZ(Globals.Level.StartPlatformHeight)
        self._endPlatformModel = endPlatformModel
        self._endPlatformModel.reparentTo(self.root)
        self._endPlatformModel.setZ(Globals.Level.EndPlatformHeight)
        self.wallR = self._frameModel.find('**/wallR')
        self.wallL = self._frameModel.find('**/wallL')
        self._exit = CogdoGameExit()
        self._exit.reparentTo(self._endPlatformModel)
        loc = self._endPlatformModel.find('**/exit_loc')
        offset = loc.getPos(render)
        self._exit.setPos(render, offset)
        self.quadrants = []
        self.visibleQuadIndices = []
        self._numQuads = 0
        self._currentQuadNum = -1
        self._camera = None
        self._initCollisions()
        self.upLimit = self._frameModel.find('**/limit_up').getZ(render)
        self.downLimit = self._frameModel.find('**/limit_down').getZ(render)
        self.leftLimit = self._frameModel.find('**/limit_left').getX(
            render) - 30.0
        self.rightLimit = self._frameModel.find('**/limit_right').getX(
            render) + 30.0
        self.backLimit = -self.quadLengthUnits
        self.forwardLimit = self.quadLengthUnits * 20
        self._frameModel.flattenStrong()
        self.gatherableFactory = CogdoFlyingGatherableFactory()
        self.obstacleFactory = CogdoFlyingObtacleFactory()
        return

    def getExit(self):
        return self._exit

    def getBounds(self):
        return ((self.leftLimit, self.rightLimit),
                (self.backLimit, self.forwardLimit), (self.downLimit,
                                                      self.upLimit))

    def getGatherable(self, serialNum):
        for quadrant in self.quadrants:
            for gatherable in quadrant.gatherables:
                if gatherable.serialNum == serialNum:
                    return gatherable

        return None

    def ready(self):
        self.gatherableFactory.destroy()
        del self.gatherableFactory
        self.obstacleFactory.destroy()
        del self.obstacleFactory
        self._initStartEndPlatforms()
        self._frameModel.reparentTo(self.root)
        self.root.reparentTo(self.parent)
        self.root.stash()

    def _initStartEndPlatforms(self):
        self.startPlatform = CogdoFlyingPlatform(
            self._startPlatformModel,
            Globals.Level.PlatformTypes.StartPlatform)
        self.endPlatform = CogdoFlyingPlatform(
            self._endPlatformModel, Globals.Level.PlatformTypes.EndPlatform)
        self._endPlatformModel.setY(self.convertQuadNumToY(self._numQuads))
        self.backLimit = self._startPlatformModel.getY(
            render) - Globals.Level.StartPlatformLength * 0.7
        self.forwardLimit = self._endPlatformModel.getY(
            render) + Globals.Level.EndPlatformLength * 0.7

    def _initCollisions(self):
        self.collPlane = CollisionPlane(
            Plane(Vec3(0, 0, 1.0), Point3(0, 0, 10)))
        self.collPlane.setTangible(0)
        self.collNode = CollisionNode('fogPlane')
        self.collNode.setIntoCollideMask(OTPGlobals.FloorBitmask)
        self.collNode.addSolid(self.collPlane)
        self.collNodePath = self.root.attachNewNode(self.collNode)
        self.collNodePath.hide()

    def destroy(self):
        del self.collPlane
        self.collNodePath.removeNode()
        del self.collNodePath
        del self.collNode
        for quadrant in self.quadrants:
            quadrant.destroy()

        self._exit.destroy()
        del self._exit
        self.root.removeNode()
        del self.root

    def onstage(self):
        self.root.unstash()
        self.update(0.0)

    def offstage(self):
        self.root.stash()

    def start(self, startTime=0.0):
        self._startTime = startTime

    def stop(self):
        pass

    def getLength(self):
        return self.quadLengthUnits * self.getNumQuadrants()

    def appendQuadrant(self, model):
        quadrant = CogdoFlyingLevelQuadrant(self._numQuads, model, self,
                                            self.root)
        if self._numQuads == 0:
            quadrant.generateGatherables(self._startPlatformModel)
        quadrant.offstage()
        self.quadrants.append(quadrant)
        self._numQuads = len(self.quadrants)

    def getNumQuadrants(self):
        return self._numQuads

    def setCamera(self, camera):
        self._camera = camera

    def getCameraActualQuadrant(self):
        camY = self._camera.getY(render)
        y = self.root.getY(render)
        return self.convertYToQuadNum(camY - y)

    def update(self, dt=0.0):
        if self._camera is None:
            return
        quadNum = clamp(self.getCameraActualQuadrant(), 0, self._numQuads - 1)
        if quadNum < self._numQuads:
            self.quadrants[quadNum].update(dt)
            if quadNum + 1 < self._numQuads:
                self.quadrants[quadNum + 1].update(dt)
            if quadNum != self._currentQuadNum:
                self._switchToQuadrant(quadNum)
        return

    def _switchToQuadrant(self, quadNum):
        self.visibleQuadIndices = []
        if quadNum >= 0:
            if quadNum > 0:
                self.quadrants[max(quadNum - self.quadVisibiltyBehind,
                                   0)].onstage()
            for i in range(
                    quadNum,
                    min(quadNum + self.quadVisibiltyAhead + 1,
                        self._numQuads)):
                self.quadrants[i].onstage()
                self.visibleQuadIndices.append(i)
                if i == 0:
                    self.startPlatform.onstage()
                elif i == self._numQuads - 1:
                    self.endPlatform.onstage()

        self._currentQuadNum = quadNum
        for i in list(
                range(0, max(self._currentQuadNum - self.quadVisibiltyBehind,
                             0))) + list(
                                 range(
                                     min(
                                         self._currentQuadNum +
                                         self.quadVisibiltyAhead + 1,
                                         self._numQuads), self._numQuads)):
            self.quadrants[i].offstage()
            if i == 0:
                self.startPlatform.offstage()
            elif i == self._numQuads - 1:
                self.endPlatform.offstage()

    def convertQuadNumToY(self, quadNum):
        return quadNum * self.quadLengthUnits

    def convertYToQuadNum(self, y):
        return int(y / self.quadLengthUnits)

    def convertCenterYToQuadNum(self, y):
        return self.convertYToQuadNum(y + self._halfQuadLengthUnits)
Exemplo n.º 12
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.º 13
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.º 14
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.º 15
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.º 16
0
class DistributedPartyTrampolineActivity(DistributedPartyActivity):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        "DistributedPartyTrampolineActivity")

    def __init__(self, cr, doJellyBeans=True, doTricks=False, texture=None):
        DistributedPartyTrampolineActivity.notify.debug("__init__")
        DistributedPartyActivity.__init__(
            self,
            cr,
            PartyGlobals.ActivityIds.PartyTrampoline,
            PartyGlobals.ActivityTypes.GuestInitiated,
            wantLever=False,
            wantRewardGui=True,
        )

        self.doJellyBeans = doJellyBeans
        self.doTricks = doTricks
        self.texture = texture

        self.toon = None
        self.trampHeight = 3.6
        self.trampK = 400.0  # spring constant
        self.normalTrampB = 2.5  # spring damping
        self.leavingTrampB = 8.0  # increase damping to slow toon faster when leaving
        self.trampB = self.normalTrampB
        self.g = -32.0  # acceleration due to gravity
        self.jumpBoost = 330.0
        self.beginningBoost = 500.0
        self.beginningBoostThreshold = self.trampHeight + 1.5
        self.earlyJumpThreshold = 75.0
        self.boingThreshold = 300.0
        self.turnFactor = 120.0
        self.stepDT = 0.001
        self.targetCameraPos = Point3(0.0, 40.0, 10.0)  # relative to toon
        self.cameraSpeed = 2.0
        self.hopOffPos = Point3(16.0, 0.0, 0.0)  # relative to tramp
        self.indicatorFactor = 0.0095
        self.dropShadowCutoff = 15.0
        self.minHeightForText = 15.0
        self.heightTextOffset = -0.065
        self.beanOffset = 0.5
        self.guiBeanOffset = -0.02

        self.jumpTextShown = False
        self.toonJumped = False
        self.turnLeft = False
        self.turnRight = False
        self.leavingTrampoline = False
        self.toonVelocity = 0.0
        self.topHeight = 0.0
        self.lastPeak = 0.0
        self.beginRoundInterval = None
        self.hopOnAnim = None
        self.hopOffAnim = None
        self.flashTextInterval = None

        # Jelly Beans
        self.numJellyBeans = PartyGlobals.TrampolineNumJellyBeans  # These are in PartyGlobals so they can be available to the AI.
        self.jellyBeanBonus = PartyGlobals.TrampolineJellyBeanBonus
        self.jellyBeanStartHeight = 20.0
        self.jellyBeanStopHeight = 90.0
        self.jellyBeanColors = [
            VBase4(1.0, 0.5, 0.5, 1.0),
            VBase4(0.5, 1.0, 0.5, 1.0),
            VBase4(0.5, 1.0, 1.0, 1.0),
            VBase4(1.0, 1.0, 0.4, 1.0),
            VBase4(0.4, 0.4, 1.0, 1.0),
            VBase4(1.0, 0.5, 1.0, 1.0),
        ]
        delta = (self.jellyBeanStopHeight -
                 self.jellyBeanStartHeight) / (self.numJellyBeans - 1)
        self.jellyBeanPositions = [
            self.jellyBeanStartHeight + n * delta
            for n in range(self.numJellyBeans)
        ]
        self.doSimulateStep = False

#        import sys
#        sys.path.append( "C:\\pratt\\SchellGames\\perforce\\depot\\Tools\\PandaMisc" )
#        from Reloader import Reloader
#        self.reloader = Reloader( "C:\\cygwin\\home\\pratt\\player_working\\toontown\\src\\parties" )

#---------------------------------------------------
# Loading
#---------------------------------------------------

    def load(self):
        DistributedPartyTrampolineActivity.notify.debug("load")
        DistributedPartyActivity.load(self)

        self.loadModels()
        self.loadCollision()
        self.loadGUI()
        self.loadSounds()
        self.loadIntervals()

        self.activityFSM = TrampolineActivityFSM(self)
        self.activityFSM.request("Idle")

        self.animFSM = TrampolineAnimFSM(self)

        self.setBestHeightInfo("", 0)

    def loadModels(self):
        self.tramp = self.root.attachNewNode(self.uniqueName("tramp"))

        self.screenPlaneElements = NodePath(self.uniqueName("screenPlane"))

        self.trampActor = Actor(
            "phase_13/models/parties/trampoline_model",
            {"emptyAnim": "phase_13/models/parties/trampoline_anim"},
        )
        self.trampActor.reparentTo(self.tramp)

        # Allow reskinning.
        if self.texture:
            reskinNode = self.tramp.find(
                "**/trampoline/__Actor_modelRoot/-GeomNode")
            reskinNode.setTexture(loader.loadTexture(self.texture), 100)

        self.surface = NodePath(self.uniqueName("trampSurface"))
        self.surface.reparentTo(self.tramp)
        self.surface.setZ(self.trampHeight)
        self.trampActor.controlJoint(self.surface, "modelRoot",
                                     "trampoline_joint1")

        self.sign.setPos(PartyGlobals.TrampolineSignOffset)

        self.beans = [
            loader.loadModelCopy("phase_4/models/props/jellybean4")
            for i in range(self.numJellyBeans)
        ]
        for bean in self.beans:
            bean.find("**/jellybean").setP(-35.0)
            bean.setScale(3.0)
            bean.setTransparency(True)
            bean.reparentTo(self.tramp)
            bean.stash()
        self.beans[-1].setScale(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)

    def loadGUI(self):
        self.gui = loader.loadModel("phase_13/models/parties/trampolineGUI")
        self.gui.reparentTo(base.a2dTopLeft)
        self.gui.setPos(0.115, 0, -1)
        self.gui.hide()

        self.toonIndicator = self.gui.find("**/trampolineGUI_MovingBar")
        jumpLineLocator = self.gui.find("**/jumpLine_locator")
        guiBean = self.gui.find("**/trampolineGUI_GreenJellyBean")

        self.gui.find("**/trampolineGUI_GreenJellyBean").stash(
        )  # sadly, the white jelly bean is named GreenJellyBean

        self.guiBeans = [
            guiBean.instanceUnderNode(jumpLineLocator,
                                      self.uniqueName("guiBean%d" % i))
            for i in range(self.numJellyBeans)
        ]
        self.guiBeans[-1].setScale(1.5)

        heightTextNode = TextNode(
            self.uniqueName("TrampolineActivity.heightTextNode"))
        heightTextNode.setFont(ToontownGlobals.getSignFont())
        heightTextNode.setAlign(TextNode.ALeft)
        heightTextNode.setText("0.0")
        heightTextNode.setShadow(0.05, 0.05)
        heightTextNode.setShadowColor(0.0, 0.0, 0.0, 1.0)
        heightTextNode.setTextColor(1.0, 1.0, 1.0, 1.0)
        self.heightText = jumpLineLocator.attachNewNode(heightTextNode)
        self.heightText.setX(0.15)
        self.heightText.setScale(0.1)
        self.heightText.setAlphaScale(0.0)

        self.quitEarlyButtonModels = loader.loadModel(
            "phase_3.5/models/gui/inventory_gui")
        quitEarlyUp = self.quitEarlyButtonModels.find("**//InventoryButtonUp")
        quitEarlyDown = self.quitEarlyButtonModels.find(
            "**/InventoryButtonDown")
        quitEarlyRollover = self.quitEarlyButtonModels.find(
            "**/InventoryButtonRollover")
        self.quitEarlyButton = DirectButton(
            parent=base.a2dTopRight,
            relief=None,
            text=TTLocalizer.PartyTrampolineQuitEarlyButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -0.23),
            text_scale=0.7,
            image=(quitEarlyUp, quitEarlyDown, quitEarlyRollover),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(-0.183, 0, -0.4),
            scale=0.09,
            command=self.leaveTrampoline,
        )
        self.quitEarlyButton.stash()

        self.flashText = OnscreenText(
            text="",
            pos=(0.0, -0.45),
            scale=0.2,
            fg=(1.0, 1.0, 0.65, 1.0),
            align=TextNode.ACenter,
            font=ToontownGlobals.getSignFont(),
            mayChange=True,
        )

        self.timer = PartyUtils.getNewToontownTimer()
        self.timer.reparentTo(self.screenPlaneElements)

    def loadSounds(self):
        self.jellyBeanSound = base.loader.loadSfx(
            "phase_4/audio/sfx/sparkly.mp3")
        self.boingSound = base.loader.loadSfx(
            "phase_4/audio/sfx/target_trampoline_2.mp3")
        self.whistleSound = base.loader.loadSfx(
            "phase_4/audio/sfx/AA_sound_whistle.mp3")

    def loadIntervals(self):
        def prepareHeightText():
            self.heightText.node().setText(
                TTLocalizer.PartyTrampolineGetHeight % int(self.toon.getZ()))
            self.heightText.setZ(self.indicatorFactor * self.toon.getZ() +
                                 self.heightTextOffset)

        self.heightTextInterval = Sequence(
            Func(prepareHeightText),
            LerpFunc(self.heightText.setAlphaScale,
                     fromData=1.0,
                     toData=0.0,
                     duration=1.0),
        )

    def unload(self):
        DistributedPartyTrampolineActivity.notify.debug("unload")
        if self.hopOnAnim and self.hopOnAnim.isPlaying():
            self.hopOnAnim.finish()
        if self.hopOffAnim and self.hopOffAnim.isPlaying():
            self.hopOffAnim.finish()
        if self.beginRoundInterval and self.beginRoundInterval.isPlaying():
            self.beginRoundInterval.finish()
        if self.flashTextInterval and self.flashTextInterval.isPlaying():
            self.flashTextInterval.finish()
        if self.heightTextInterval and self.heightTextInterval.isPlaying():
            self.heightTextInterval.finish()
        self.timer.stop()
        DistributedPartyActivity.unload(self)

        # Unload tasks
        taskMgr.remove(self.uniqueName("TrampolineActivity.updateTask"))
        taskMgr.remove(self.uniqueName("TrampolineActivity.remoteUpdateTask"))

        # Unload events
        self.ignoreAll()

        # Unload intervals
        del self.heightTextInterval
        del self.beginRoundInterval
        del self.hopOnAnim
        del self.hopOffAnim
        del self.flashTextInterval

        if hasattr(self, 'beanAnims'):
            # we need to cleanup the jelly bean interval
            self.cleanupJellyBeans()

        # Unload gui stuff
        self.quitEarlyButton.destroy()
        del self.quitEarlyButton
        if self.screenPlaneElements:
            self.screenPlaneElements.removeNode()
            self.screenPlaneElements = None

        # Unload fsms
        del self.activityFSM
        del self.animFSM

    #---------------------------------------------------
    # Distributed
    #---------------------------------------------------
    def setBestHeightInfo(self, toonName, height):

        if GMUtils.testGMIdentity(toonName):
            toonName = GMUtils.handleGMName(toonName)

        self.bestHeightInfo = (toonName, height)
        DistributedPartyTrampolineActivity.notify.debug(
            "%s has the best height of %d" % (toonName, height))

        if height > 0:
            self.setSignNote(TTLocalizer.PartyTrampolineBestHeight %
                             self.bestHeightInfo)
        else:
            self.setSignNote(TTLocalizer.PartyTrampolineNoHeightYet)

    def leaveTrampoline(self):
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            self._showFlashMessage(TTLocalizer.PartyTrampolineTimesUp)
            self.leavingTrampoline = True
            self.timer.reset()
            self.trampB = self.leavingTrampB
            self.ignore("control")
            self.quitEarlyButton.stash()
            self.gui.hide()

    def requestAnim(self, request):
        self.animFSM.request(request)

    def b_requestAnim(self, request):
        self.requestAnim(request)
        self.sendUpdate("requestAnim", [request])

    def requestAnimEcho(self, request):
        if self.toon != None and self.toon.doId != base.localAvatar.doId:
            self.requestAnim(request)

    def removeBeans(self, beansToRemove):
        for i in beansToRemove:
            height, bean, guiBean, beanAnim = self.beanDetails[i]
            guiBean.stash()
            if i in self.beansToCollect:
                self.beansToCollect.remove(i)
            else:
                self.notify.warning(
                    "removeBeans avoided a crash, %d not in self.beansToCollect"
                    % i)
            self.poofBean(bean, beanAnim)
#            bean.stash()
#            beanAnim.finish()

    def b_removeBeans(self, beansToRemove):
        self.removeBeans(beansToRemove)
        self.sendUpdate("removeBeans", [beansToRemove])

    def removeBeansEcho(self, beansToRemove):
        if self.toon != None and self.toon.doId != base.localAvatar.doId:
            self.removeBeans(beansToRemove)

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny)
        base.cr.playGame.getPlace().fsm.request("walk")

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny)

    def setState(self, newState, timestamp):
        DistributedPartyTrampolineActivity.notify.debug(
            "setState( newState=%s, ... )" % newState)
        DistributedPartyActivity.setState(self, newState, timestamp)
        self.activityFSM.request(newState)

    #---------------------------------------------------
    # FSM handling
    #---------------------------------------------------
    def startIdle(self):
        DistributedPartyTrampolineActivity.notify.debug("startIdle")

    def finishIdle(self):
        DistributedPartyTrampolineActivity.notify.debug("finishIdle")

    def startRules(self):
        DistributedPartyTrampolineActivity.notify.debug("startRules")
        if self.doJellyBeans:
            self.setupJellyBeans()
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            self.acquireToon()

    def startActive(self):
        DistributedPartyTrampolineActivity.notify.debug("startActive")
        if self.toon != None and self.toon.doId == base.localAvatar.doId:
            base.setCellsAvailable(base.bottomCells, True)

            self.accept("arrow_left", self.onLeft)
            self.accept("arrow_left-up", self.onLeftUp)
            self.accept("arrow_right", self.onRight)
            self.accept("arrow_right-up", self.onRightUp)

            self.beginRoundInterval = Sequence(
                Func(self._showFlashMessage, TTLocalizer.PartyTrampolineReady),
                Wait(1.2),
                Func(self.flashMessage, TTLocalizer.PartyTrampolineGo),
                Func(self.beginRound))
            self.beginRoundInterval.start()

    def finishActive(self):
        DistributedPartyTrampolineActivity.notify.debug("finishActive")
        if self.doJellyBeans:
            self.cleanupJellyBeans()

    #---------------------------------------------------
    # FSM extras
    #---------------------------------------------------
    def setupJellyBeans(self):
        self.beanAnims = []
        self.beansToCollect = []
        self.beanDetails = []
        self.numBeansCollected = 0
        for i in range(self.numJellyBeans):
            bean = self.beans[i]
            guiBean = self.guiBeans[i]
            height = self.jellyBeanPositions[i]
            color = random.choice(self.jellyBeanColors)
            bean.find("**/jellybean").setColor(color)
            if self.toon.doId == base.localAvatar.doId:
                bean.setAlphaScale(1.0)
            else:
                bean.setAlphaScale(0.5)
            guiBean.setColor(color)
            bean.setZ(height + self.toon.getHeight() + self.beanOffset)
            guiBean.setZ(height * self.indicatorFactor + self.guiBeanOffset)
            bean.setH(0.0)
            bean.unstash()
            guiBean.unstash()
            beanAnim = bean.hprInterval(
                1.5, VBase3((((i % 2) * 2) - 1) * 360.0, 0.0, 0.0)
            )  # the (((i % 2)*2) - 1) makes adjacent beans spin opposite directions
            beanAnim.loop()
            self.beanAnims.append(beanAnim)
            self.beanDetails.append((height, bean, guiBean, beanAnim))
        self.beansToCollect = list(range(self.numJellyBeans))

    def cleanupJellyBeans(self):
        for bean in self.beans:
            bean.stash()
        for guiBean in self.guiBeans:
            guiBean.stash()
        # If handleToonJoined hasn't been sent toonId on some clients
        if hasattr(self, 'beanAnims'):
            for beanAnim in self.beanAnims:
                beanAnim.finish()
            del self.beanAnims
            del self.beansToCollect

    def beginRound(self):
        base.playSfx(self.whistleSound)

        self.timer.setTime(PartyGlobals.TrampolineDuration)
        self.timer.countdown(PartyGlobals.TrampolineDuration)
        self.timer.show()

        self.gui.show()

        self.quitEarlyButton.unstash()

        self.notify.debug("Accepting contorl")
        self.accept("control", self.onJump)
        self.notify.debug("setting simulate step to true")
        self.doSimulateStep = True

    def acquireToon(self):
        #        self.dataLog = open( "dataLog.txt", "w" )

        self.toon.disableSmartCameraViews()
        self.toon.stopUpdateSmartCamera()
        camera.wrtReparentTo(render)

        self.toon.dropShadow.reparentTo(hidden)
        self.toon.startPosHprBroadcast(period=0.2)

        self.toonAcceleration = 0.0
        self.toonVelocity = 0.0
        self.topHeight = 0.0
        self.trampB = self.normalTrampB
        self.leavingTrampoline = False
        self.hopOnAnim = Sequence(
            Func(self.toon.b_setAnimState, "jump", 1.0),
            Wait(0.4),
            PartyUtils.arcPosInterval(0.75, self.toon,
                                      Point3(0.0, 0.0, self.trampHeight), 5.0,
                                      self.tramp),
            Func(self.postHopOn),
        )
        self.hopOnAnim.start()

    def postHopOn(self):
        self.toon.setH(self.toon.getH() +
                       90.0)  # makes camera adjustment less jarring
        self.toon.dropShadow.reparentTo(self.surface)
        self.screenPlaneElements.reparentTo(aspect2d)
        self.timeLeftToSimulate = 0.0
        self.doSimulateStep = False
        taskMgr.add(self.updateTask,
                    self.uniqueName("TrampolineActivity.updateTask"))
        base.setCellsAvailable(base.leftCells, False)
        base.setCellsAvailable(base.bottomCells, False)
        DistributedPartyActivity.startRules(self)

    def releaseToon(self):
        self._hideFlashMessage()

        self.ignore("arrow_left")
        self.ignore("arrow_left-up")
        self.ignore("arrow_right")
        self.ignore("arrow_right-up")

        taskMgr.remove(self.uniqueName("TrampolineActivity.updateTask"))
        self.hopOffAnim = Sequence(
            self.toon.hprInterval(0.5,
                                  VBase3(-90.0, 0.0, 0.0),
                                  other=self.tramp),
            Func(self.toon.b_setAnimState, "jump", 1.0),
            Func(self.toon.dropShadow.reparentTo, hidden),
            Wait(0.4),
            PartyUtils.arcPosInterval(0.75, self.toon, self.hopOffPos, 5.0,
                                      self.tramp),
            Func(self.postHopOff),
        )
        self.hopOffAnim.start()

    def postHopOff(self):
        self.screenPlaneElements.reparentTo(hidden)
        base.setCellsAvailable(base.leftCells, True)
        self.timer.stop()
        self.timer.hide()

        self.toon.dropShadow.reparentTo(self.toon.getShadowJoint())
        self.toon.dropShadow.setAlphaScale(1.0)
        self.toon.dropShadow.setScale(1.0)

        # Continue broadcasting so remote toons see us reach the final hop off position.
        #self.toon.stopPosHprBroadcast()
        self.b_requestAnim("Off")

        camera.reparentTo(base.localAvatar)
        base.localAvatar.startUpdateSmartCamera()
        base.localAvatar.enableSmartCameraViews()
        base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
        place = base.cr.playGame.getPlace()

        if self.doJellyBeans:
            self.sendUpdate("awardBeans",
                            [self.numBeansCollected,
                             int(self.topHeight)])
            if int(self.topHeight) > self.bestHeightInfo[1]:
                self.sendUpdate("reportHeightInformation",
                                [int(self.topHeight)])

        self.d_toonExitDemand()

    #---------------------------------------------------
    # Event Handling
    #---------------------------------------------------
    def onTrampolineTrigger(self, collEntry):
        if (self.activityFSM._state == "Idle") and (self.toon == None) and (
                base.cr.playGame.getPlace().fsm.getCurrentState().getName()
                == "walk"):
            base.cr.playGame.getPlace().fsm.request("activity")
            self.d_toonJoinRequest()
        else:
            self.flashMessage(TTLocalizer.PartyTrampolineActivityOccupied,
                              duration=2.0)

    def onJump(self):
        self.notify.debug("got onJump")
        if self.toon != None and self.toon.getZ() < self.trampHeight:
            # Have to be on the trampoline
            self.toonJumped = True
            self.b_requestAnim("Jump")
        else:
            self.notify.debug("z is less than tramp height")

    def onLeft(self):
        self.turnLeft = True

    def onLeftUp(self):
        self.turnLeft = False

    def onRight(self):
        self.turnRight = True

    def onRightUp(self):
        self.turnRight = False

    #---------------------------------------------------
    # Super class functionality
    #---------------------------------------------------
    def handleToonJoined(self, toonId):
        DistributedPartyTrampolineActivity.notify.debug("handleToonJoined")
        self.toon = self.getAvatar(toonId)
        if self.toon != None and not self.toon.isEmpty():
            self.oldJumpSquatPlayRate = self.toon.getPlayRate("jump-squat")
            self.oldJumpLandPlayRate = self.toon.getPlayRate("jump-land")
            self.toon.setPlayRate(2.5, "jump-squat")
            self.toon.setPlayRate(2.0, "jump-land")

            self.turnLeft = False
            self.turnRight = False

            self.activityFSM.request("Rules")

            if self.toon.doId != base.localAvatar.doId:
                taskMgr.add(
                    self.remoteUpdateTask,
                    self.uniqueName("TrampolineActivity.remoteUpdateTask"))
        else:
            self.notify.warning("handleToonJoined could not get toon %d" %
                                toonId)

    def handleToonExited(self, toonId):
        DistributedPartyTrampolineActivity.notify.debug("handleToonExited")

        if self.toon != None:
            if self.toon.doId != base.localAvatar.doId:
                taskMgr.remove(
                    self.uniqueName("TrampolineActivity.remoteUpdateTask"))

            self.surface.setZ(self.trampHeight)

            self.toon.setPlayRate(self.oldJumpSquatPlayRate, "jump-squat")
            self.toon.setPlayRate(self.oldJumpLandPlayRate, "jump-land")

            self.toon = None

    def handleToonDisabled(self, toonId):
        """
        A toon dropped unexpectedly from the game. Handle it!
        """
        DistributedPartyTrampolineActivity.notify.debug("handleToonDisabled")
        DistributedPartyTrampolineActivity.notify.debug("avatar " +
                                                        str(toonId) +
                                                        " disabled")

        if base.localAvatar.doId == toonId:
            self.releaseToon()

    def handleRulesDone(self):
        self.sendUpdate("toonReady")
        self.finishRules()

    def getTitle(self):
        if self.doJellyBeans:
            return TTLocalizer.PartyTrampolineJellyBeanTitle
        elif self.doTricks:
            return TTLocalizer.PartyTrampolineTricksTitle
        else:
            return DistributedPartyActivity.getTitle(self)

    def getInstructions(self):
        return TTLocalizer.PartyTrampolineActivityInstructions

    #---------------------------------------------------
    # Simulation
    #---------------------------------------------------
    def updateTask(self, task):
        # Only run on local client
        z = self.toon.getZ()
        dt = globalClock.getDt()
        if self.doSimulateStep:
            self.timeLeftToSimulate += dt
            while self.timeLeftToSimulate >= self.stepDT:
                z, a = self.simulateStep(z)
                self.timeLeftToSimulate -= self.stepDT

        self.toon.setZ(z)

        # Move tramp surface
        if z <= self.trampHeight:
            self.surface.setZ(z)
        else:
            self.surface.setZ(self.trampHeight)

        # Move toon indicator
        self.toonIndicator.setZ((z - self.trampHeight) * self.indicatorFactor)

        # Turn left or right
        if self.turnLeft:
            self.toon.setH(self.toon.getH() + self.turnFactor * dt)
        if self.turnRight:
            self.toon.setH(self.toon.getH() - self.turnFactor * dt)

        # Update camera
        currentPos = base.camera.getPos(self.toon)
        vec = self.targetCameraPos - currentPos
        newPos = currentPos + vec * (dt * self.cameraSpeed)
        base.camera.setPos(self.toon, newPos)
        base.camera.lookAt(self.toon)

        # Fade and scale drop shadow
        if z > self.trampHeight:
            heightFactor = 1.0 - min(
                1.0, (z - self.trampHeight) / self.dropShadowCutoff)
            self.toon.dropShadow.setAlphaScale(heightFactor)
            self.toon.dropShadow.setScale(max(0.1, heightFactor))
        else:
            self.toon.dropShadow.setAlphaScale(1.0)
            self.toon.dropShadow.setScale(1.0)

        # Leave trampoline if necessary
        if self.leavingTrampoline and (z < self.trampHeight) and (abs(a) <
                                                                  0.1):
            self.releaseToon()

# Simulate poor framerate
#        time.sleep( 0.03 )

        return Task.cont

    def simulateStep(self, z):
        # Calculate acceleration
        if z >= self.trampHeight:
            # Above the trampoline; only use gravity.
            a = self.g
            # Clear jumped flag
            self.toonJumped = False
        else:
            # On the trampoline; use gravity + spring + damping
            a = self.g + self.trampK * (self.trampHeight -
                                        z) - self.trampB * self.toonVelocity
            # Add jump acceleration if necessary
            if self.toonJumped:
                # Don't penalize early jumps.
                # If we're above the earlyJumpThreshold, go ahead and add jump
                # acceleration, even if the toon hasn't bottomed out (which
                # will reduce the effectiveness of the jump).
                # Otherwise, only add jump acceleration, if the toon has
                # bottomed out (toonVelocity >= 0.0).
                #self.notify.debug("self.lastPeak=%s earlyJumpThreshold=%s toonVelocity=%s" %(self.lastPeak, self.earlyJumpThreshold, self.toonVelocity ))
                if (self.lastPeak > self.earlyJumpThreshold) or (
                        self.toonVelocity >= -3E5):
                    a += self.jumpBoost
                    # Add beginning boost if necessary.
                    if self.lastPeak < self.beginningBoostThreshold:
                        a += self.beginningBoost

        # Calculate velocity
        #import pdb; pdb.set_trace()
        lastVelocity = self.toonVelocity
        self.toonVelocity += a * self.stepDT

        if (lastVelocity > 0.0) and (self.toonVelocity <= 0.0):
            topOfJump = True
            bottomOfJump = False
        elif (lastVelocity < 0.0) and (self.toonVelocity >= 0.0):
            topOfJump = False
            bottomOfJump = True
        else:
            topOfJump = False
            bottomOfJump = False

        # optimal jumping


#        if bottomOfJump and self.isAccepting( "control" ):
#            self.toonJumped = True
#            print z

# Calculate position
        newZ = z + self.toonVelocity * self.stepDT
        if newZ > self.topHeight:
            self.topHeight = newZ
            if self.doJellyBeans:
                self.collectJellyBeans(newZ)

        if topOfJump:
            # Set lastPeak
            self.lastPeak = newZ

            # Show height text if necessary
            if newZ >= self.minHeightForText:
                self.heightTextInterval.start()

        # Set anim state
        if topOfJump:
            if newZ > (self.trampHeight + 20.0):
                self.b_requestAnim("Falling")
            elif self.animFSM._state == "Jump":
                self.b_requestAnim("Falling")
        if (newZ <= self.trampHeight) and (z > self.trampHeight):
            if self.animFSM._state == "Falling":
                self.b_requestAnim("Land")
            elif self.animFSM._state != "Neutral":
                self.b_requestAnim("Neutral")

        # Play "boing" sound.
        if bottomOfJump and (a > self.boingThreshold):
            base.playSfx(self.boingSound)

        return newZ, a

    def collectJellyBeans(self, z):
        beansToRemove = []
        for i in self.beansToCollect:
            height = self.beanDetails[i][0]
            if height <= z:
                beansToRemove.append(i)

        if len(beansToRemove) > 0:
            base.playSfx(self.jellyBeanSound)
            self.numBeansCollected += len(beansToRemove)
            self.b_removeBeans(beansToRemove)

    def remoteUpdateTask(self, task):
        # Only run on remote clients

        # Move tramp surface
        if self.toon != None and not self.toon.isEmpty():
            z = self.toon.getZ()
            if z <= self.trampHeight:
                self.surface.setZ(z)
            else:
                self.surface.setZ(self.trampHeight)

        return Task.cont

    #---------------------------------------------------
    # Misc
    #---------------------------------------------------
    def poofBean(self, bean, beanAnim):
        if bean == None:
            self.notify.warning(
                "poofBean, returning immediately as bean is None")
            return
        if bean.isEmpty():
            self.notify.warning(
                "poofBean, returning immediately as bean is empty")
            return
        currentAlpha = bean.getColorScale()[3]
        currentScale = bean.getScale()
        poofAnim = Sequence(
            Parallel(
                LerpFunc(bean.setAlphaScale,
                         fromData=currentAlpha,
                         toData=0.0,
                         duration=0.25),
                LerpFunc(bean.setScale,
                         fromData=currentScale,
                         toData=currentScale * 5.0,
                         duration=0.25),
            ),
            Func(bean.stash),
            Func(beanAnim.finish),
            Func(bean.setAlphaScale, currentAlpha),
            Func(bean.setScale, currentScale),
        )
        poofAnim.start()

    def _showFlashMessage(self, message):
        if self.isDisabled():
            assert self.notify.debug(
                "_showFlasMessage disabled, not showing %s" % message)
            return

        if (self.flashTextInterval
                is not None) and self.flashTextInterval.isPlaying():
            self.flashTextInterval.finish()
        self.flashText.setText(message)
        self.flashText.setAlphaScale(1.0)
        self.flashText.unstash()

    def _hideFlashMessage(self, duration=0.0):
        if self.isDisabled():
            assert self.notify.debug(
                "_hideFlashMessage we're disabled, but still hiding self.flashText"
            )
            # return

        self.flashTextInterval = Sequence(
            Wait(duration),
            LerpFunc(self.flashText.setAlphaScale,
                     fromData=1.0,
                     toData=0.0,
                     duration=1.0),
            Func(self.flashText.stash),
        )
        self.flashTextInterval.start()

    def flashMessage(self, message, duration=0.5):
        self._showFlashMessage(message)
        self._hideFlashMessage(duration)
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 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.º 19
0
class CogdoMazeDrop(NodePath, DirectObject):

    def __init__(self, game, id, x, y):
        NodePath.__init__(self, 'dropNode%s' % id)
        self.game = game
        self.id = id
        self.reparentTo(hidden)
        self.setPos(x, y, 0)
        shadow = loader.loadModel('phase_3/models/props/square_drop_shadow')
        shadow.setZ(0.2)
        shadow.setBin('ground', 10)
        shadow.setColor(1, 1, 1, 1)
        shadow.reparentTo(self)
        self.shadow = shadow
        drop = CogdoUtil.loadMazeModel('cabinetSmFalling')
        roll = random.randint(-15, 15)
        drop.setHpr(0, 0, roll)
        drop.setZ(Globals.DropHeight)
        self.collTube = CollisionTube(0, 0, 0, 0, 0, 4, Globals.DropCollisionRadius)
        self.collTube.setTangible(0)
        name = Globals.DropCollisionName
        self.collNode = CollisionNode(name)
        self.collNode.addSolid(self.collTube)
        self.collNodePath = drop.attachNewNode(self.collNode)
        self.collNodePath.hide()
        self.collNodePath.setTag('isFalling', str('True'))
        drop.reparentTo(self)
        self.drop = drop
        self._dropSfx = base.cogdoGameAudioMgr.createSfxIval('drop', volume=0.6)

    def disableCollisionDamage(self):
        self.collTube.setTangible(1)
        self.collTube.setRadius(Globals.DroppedCollisionRadius)
        self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        self.collNodePath.setTag('isFalling', str('False'))

    def getDropIval(self):
        shadow = self.shadow
        drop = self.drop
        id = self.id
        hangTime = Globals.ShadowTime
        dropTime = Globals.DropTime
        dropHeight = Globals.DropHeight
        targetShadowScale = 0.5
        targetShadowAlpha = 0.4
        shadowScaleIval = LerpScaleInterval(shadow, dropTime, targetShadowScale, startScale=0)
        shadowAlphaIval = LerpColorScaleInterval(shadow, hangTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0))
        shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
        startPos = Point3(0, 0, dropHeight)
        drop.setPos(startPos)
        dropIval = LerpPosInterval(drop, dropTime, Point3(0, 0, 0), startPos=startPos, blendType='easeIn')
        dropSoundIval = self._dropSfx
        dropSoundIval.node = self
        self.drop.setTransparency(1)

        def _setRandScale(t):
            self.drop.setScale(self, 1 - random.random() / 16, 1 - random.random() / 16, 1 - random.random() / 4)

        scaleChange = 0.4 + random.random() / 4
        dropShakeSeq = Sequence(LerpScaleInterval(self.drop, 0.25, Vec3(1.0 + scaleChange, 1.0 + scaleChange / 2, 1.0 - scaleChange), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.25, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), Func(self.disableCollisionDamage), LerpScaleInterval(self.drop, 0.2, Vec3(1.0 + scaleChange / 8, 1.0 + scaleChange / 8, 1.0 - scaleChange / 8), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.2, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.15, Vec3(1.0 + scaleChange / 16, 1.0 + scaleChange / 16, 1.0 - scaleChange / 16), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.15, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), LerpScaleInterval(self.drop, 0.1, Vec3(1.0 + scaleChange / 16, 1.0 + scaleChange / 8, 1.0 - scaleChange / 16), blendType='easeInOut'), LerpColorScaleInterval(self.drop, Globals.DropFadeTime, Vec4(1.0, 1.0, 1.0, 0.0)))
        ival = Sequence(Func(self.reparentTo, render), Parallel(Sequence(WaitInterval(hangTime), dropIval), shadowIval), Parallel(Func(self.game.dropHit, self, id), dropSoundIval, dropShakeSeq), Func(self.game.cleanupDrop, id), name='drop%s' % id)
        self.ival = ival
        return ival

    def destroy(self):
        self.ival.pause()
        self.ival = None
        self._dropSfx.pause()
        self._dropSfx = None
        self.collTube = None
        self.collNode = None
        self.collNodePath.removeNode()
        self.collNodePath = None
        self.removeNode()
        return
Exemplo n.º 20
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()