예제 #1
0
class Creature(object):
    def __init__(self):
        self.nodepath = NodePath(repr(self))
        node = self.nodepath.attachNewNode(CARD_MAKER.generate())
        node.setTexture(self.texture)
        node.setTwoSided(True)
        node.setTransparency(True)
        self.nodepath.setBillboardPointEye()
        self.pos = None
        self.cur_path = []
        #TODO: how to handle creatures actions?
        self.speed = 4
        self.action = random.randint(0, self.speed)

    def goto(self, pos, asteroid):
        self.cur_path = find_path(self.pos, pos, asteroid)
class FishLure(NodePath):
    def __init__(self, gameObject, type):
        NodePath.__init__(self, "FishingLure_%s" % type)
        self.gameObject = gameObject
        self.type = type
        self.initLureModel()
        if FishingGlobals.wantDebugCollisionVisuals:
            self.initCollision()

        self.initLureHelpText()
        self.setLightOff()

    def initLureModel(self):
        self.lureModel = NodePath("lure")
        self.currentLureType = "regular"
        self.lureAttractRadius = FishingGlobals.lureTypeToAttractRadius[self.currentLureType]
        self.lureModel = loader.loadModel(FishingGlobals.lureTypeToModel[self.currentLureType])
        self.lureModel.setScale(2.0)
        self.lureModel.reparentTo(self)
        self.lureModel.setDepthWrite(False)
        self.lureModel.setDepthTest(True)
        self.lureModel.setBin("ground", 25)
        self.mainGui = loader.loadModel("models/gui/gui_main")

    def initCollision(self):
        self.lureCollisionVisual = loader.loadModel("models/ammunition/cannonball")
        self.lureCollisionVisual.setTransparency(1)
        self.lureCollisionVisual.setColor(0.0, 0.0, 1.0, 0.29999999999999999)
        self.lureCollisionVisual.setScale(self.lureAttractRadius)
        self.lureCollisionVisual.reparentTo(self)
        self.lureCollisionVisual.hide()

    def initLureHelpText(self):
        self.helpTextNode = TextNode("fishBitingIcon")
        self.helpTextNodePath = NodePath(self.helpTextNode)
        self.helpTextNodePath.setPos(0.0, 0.0, 0.69999999999999996)
        self.helpTextNode.setText(" ")
        self.helpTextNode.setAlign(TextNode.ACenter)
        self.helpTextNode.setFont(PiratesGlobals.getPirateFont())
        self.helpTextNode.setTextColor(1.0, 1.0, 1.0, 1.0)
        self.helpTextNodePath.reparentTo(self)
        self.helpTextNodePath.setBillboardPointEye()
        self.helpTextNodePath.setBin("fishingGame", 10)
        self.helpTextNodePath.hide()

    def enableLureGlow(self, glowType):
        self.lureGlow = LureGlow.getEffect()
        if self.lureGlow:
            if self.gameObject.fishManager.activeFish is not None:
                self.lureGlow.reparentTo(self.gameObject.fishManager.activeFish.mouthJoint)
            else:
                self.lureGlow.reparentTo(self)
            self.lureGlow.effectColor = _glowColors[glowType]
            self.lureGlow.setShaderOff()
            self.lureGlow.setBin("fishingGame", 5)
            self.lureGlow.play()

    def showHelpText(self, textToShow):
        taskMgr.remove(self.gameObject.distributedFishingSpot.uniqueName("ClearLureText"))
        if textToShow is None:
            self.helpTextNode.setText(" ")
            self.helpTextNodePath.hide()
        elif self.gameObject.fishManager.activeFish is not None:
            self.helpTextNodePath.setScale(
                FishingGlobals.fishSizeToHelpTextScale[self.gameObject.fishManager.activeFish.myData["size"]]
            )
        else:
            self.helpTextNodePath.setScale(1.0)
        self.helpTextNode.setText(textToShow)
        self.helpTextNodePath.show()
        taskMgr.doMethodLater(
            FishingGlobals.lureHelpTextDuration,
            self.showHelpText,
            name=self.gameObject.distributedFishingSpot.uniqueName("ClearLureText"),
            extraArgs=[None],
        )

    def setLureType(self, type):
        self.currentLureType = type
        if type == None:
            self.lureModel.hide()
            self.gameObject.gui.setTackleBoxPulse(True)
        elif type == "regular":
            self.gameObject.gui.setTackleBoxPulse(False)
            self.currentLureType = type
            self.lureModel.removeNode()
            self.lureAttractRadius = FishingGlobals.lureTypeToAttractRadius[type]
            self.lureModel = loader.loadModel(FishingGlobals.lureTypeToModel[type])
            self.lureModel.setScale(2.0)
            self.lureModel.reparentTo(self)
            self.lureModel.setDepthWrite(False)
            self.lureModel.setDepthTest(True)
            self.lureModel.setBin("ground", 25)
        elif type == "legendary":
            self.gameObject.gui.setTackleBoxPulse(False)
            self.currentLureType = type
            self.lureModel.removeNode()
            self.lureAttractRadius = FishingGlobals.lureTypeToAttractRadius[type]
            self.lureModel = loader.loadModel(FishingGlobals.lureTypeToModel[type])
            self.lureModel.setScale(2.0)
            self.lureModel.reparentTo(self)
            self.lureModel.setDepthWrite(False)
            self.lureModel.setDepthTest(True)
            self.lureModel.setBin("ground", 25)

    def showCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.lureCollisionVisual.show()

    def hideCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.lureCollisionVisual.hide()

    def requestPitch(self, fish):
        offset = fish.getPos() - self.getPos()
        if fish.getX() < self.getX():
            return math.degrees(math.atan2(offset.getZ(), -offset.getX()))
        else:
            return math.degrees(math.atan2(-offset.getZ(), offset.getX()))

    def resetLureModel(self):
        self.lureModel.reparentTo(self)
        self.lureModel.show()
        self.lureModel.setPosHpr(0, 0, 0, 0, 0, 0.0)
        if self.currentLureType == None:
            self.lureModel.hide()
            self.gameObject.gui.setTackleBoxPulse(True)
        else:
            self.gameObject.gui.setTackleBoxPulse(False)

    def destroy(self):
        self.lureModel.removeNode()
        self.removeNode()
예제 #3
0
class Translation( Base ):
    
    def __init__( self, *args, **kwargs ):
        Base.__init__( self, *args, **kwargs )
        
        # Create x, y, z and camera normal axes
        self.axes.append( self.CreateArrow( Vec3(1, 0, 0), RED ) )
        self.axes.append( self.CreateArrow( Vec3(0, 1, 0), GREEN ) )
        self.axes.append( self.CreateArrow( Vec3(0, 0, 1), BLUE ) )
        self.axes.append( self.CreateSquare( Vec3(0, 0, 0), TEAL ) )
        
    def CreateArrow( self, vector, colour ):
        
        # Create the geometry and collision
        line = NodePath( Line( (0, 0, 0), vector ) )
        cone = NodePath( Cone( 0.05, 0.25, axis=vector, origin=vector * 0.125 ) )
        collTube = CollisionTube( (0,0,0), Point3( vector ) * 0.95, 0.05 )
        
        # Create the axis, add the geometry and collision
        axis = Axis( self.name, vector, colour )
        axis.AddGeometry( line, sizeStyle=SCALE )
        axis.AddGeometry( cone, vector, colour )
        axis.AddCollisionSolid( collTube, sizeStyle=TRANSLATE_POINT_B )
        axis.reparentTo( self )
        
        return axis
    
    def CreateSquare( self, vector, colour ):
        
        # Create the geometry and collision
        self.square = NodePath( Square( 0.2, 0.2, Vec3(0, 1, 0) ) )
        self.square.setBillboardPointEye()
        collSphere = CollisionSphere( 0, 0.125 )
        
        # Create the axis, add the geometry and collision
        axis = Axis( self.name, CAMERA_VECTOR, colour, planar=True, default=True )
        axis.AddGeometry( self.square, sizeStyle=NONE )
        axis.AddCollisionSolid( collSphere, sizeStyle=NONE )
        axis.reparentTo( self )
        
        return axis
    
    def Transform( self ):
        
        # Get the point where the mouse clicked the axis
        axis = self.GetSelectedAxis()
        axisPoint = self.GetAxisPoint( axis )
        
        # Get the gizmo's translation matrix and transform it
        newTransMat = Mat4().translateMat( self.initXform.getPos() - self.getPos() + axisPoint - self.initMousePoint )
        self.setMat( self.getMat() * newTransMat )
        
        # Get the attached node path's translation matrix
        transVec = axisPoint - self.initMousePoint
        if axis.vector != CAMERA_VECTOR:
            transVec = self.getRelativeVector( self.rootNp, transVec ) * self.getScale()[0]
        newTransMat = Mat4().translateMat( transVec )
        
        # Transform attached node paths
        for i, np in enumerate( self.attachedNps ):
            
            # Perform transforms in local or world space
            if self.local and axis.vector != CAMERA_VECTOR:
                transMat, rotMat, scaleMat = commonUtils.GetTrsMatrices( self.initNpXforms[i] )
                np.setMat( scaleMat * newTransMat * rotMat * transMat )
            else:
                np.setMat( self.initNpXforms[i].getMat() * newTransMat )
            
    def OnNodeMouse1Down( self, planar, collEntry ):
        Base.OnNodeMouse1Down( self, planar, collEntry )
        
        # Store the gizmo's initial transform
        self.initXform = self.getTransform()
        
        # If in planar mode, clear the billboard effect on the center square
        # and make it face the selected axis
        axis = self.GetSelectedAxis()
        if self.planar and not axis.planar:
            self.square.clearBillboard()
            self.square.lookAt( self, Point3( axis.vector ) )
        else:
            self.square.setHpr( Vec3(0, 0, 0) )
            self.square.setBillboardPointEye()
            
    def OnMouse2Down( self ):
        Base.OnMouse2Down( self )
        
        # Store the gizmo's initial transform
        self.initXform = self.getTransform()
예제 #4
0
class Fish(NodePath):

    def __init__(self, fishManager, myData, index, trophy=0):
        NodePath.__init__(self, '%s_%d' % (myData['name'], index))
        self.trophy = trophy
        self.myData = myData
        if not self.trophy:
            self.fishManager = fishManager
            self.index = index
            self.fsm = FishFSM(self)
            self.weight = random.randint(self.myData['weightRange'][0], self.myData['weightRange'][1])
        else:
            self.weight = trophy
        self.adjustedScale = (self.myData['scaleRange'][1] - self.myData['scaleRange'][0]) * (self.weight - self.myData['weightRange'][0]) / (self.myData['weightRange'][1] - self.myData['weightRange'][0]) + self.myData['scaleRange'][0]
        self.initActor()
        if not self.trophy:
            self.initVariables()
            self.initFishStatusIcon()
            if FishingGlobals.wantDebugCollisionVisuals:
                self.initCollisions()
        self.avoidingFish = False
        self.biteBubbleEffect = None
        self.idleBubbleEffect = None
        self.fightBubbleEffect = None
        self.behaviorNameToFunction = {'straight': self.performStraightBehavior,'sineStraight': self.performSineStraightBehavior,'erratic': self.performErraticBehavior}
        self.sineDtAccumulator = 0.0
        self.erraticDtAccumulator = 0.0
        self.myZ = 0.0
        if not self.trophy:
            self.setLightOff()
        return

    def initActor(self):
        self.animDict = {}
        for anim in FishingGlobals.fishAnimations:
            self.animDict[anim] = 'models/char/pir_a_gam_fsh_%s_%s.bam' % (self.myData['model'], anim)

        self.actor = BlendActor('models/char/pir_r_gam_fsh_%s.bam' % self.myData['model'], self.animDict, FishingGlobals.defaultFishBlendTime, FishingGlobals.fishBlendTimeDict)
        self.actor.reparentTo(self)
        self.actor.setScale(self.adjustedScale)
        self.mouthJoint = self.actor.exposeJoint(None, 'modelRoot', 'hookAttach')
        self.attractionPoint = NodePath('AttractionPoint')
        self.attractionPoint.reparentTo(self.mouthJoint)
        self.attractionPoint.setPos(0.0, 0.0, 0.0)
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdle')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdleOpposite')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turn')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turnOpposite')
        if not self.trophy:
            self.setBin('fishingGame', 10)
        return

    def codeReload(self):
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdle')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdleOpposite')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turn')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turnOpposite')

    def initFishStatusIcon(self):
        self.fishStatusIconTextNode = TextNode('fishBitingIcon')
        self.fishStatusIconNodePath = NodePath(self.fishStatusIconTextNode)
        self.fishStatusIconNodePath.setPos(0.0, 0.0, self.myData['indicatorHeightOffset'])
        self.fishStatusIconTextNode.setText('?')
        self.fishStatusIconTextNode.setTextColor(1.0, 0.0, 0.0, 1.0)
        self.fishStatusIconNodePath.reparentTo(self.mouthJoint)
        self.fishStatusIconNodePath.setBillboardPointEye()
        self.fishStatusIconNodePath.hide()
        self.fishStatusIconNodePath.setShaderOff()

    def initVariables(self):
        self.attractionVisual = None
        self.collisionVisual = None
        self.movingRight = True
        self.turnSpeed = 160.0
        self.turnTowardLureInterval = None
        self.velocity = FishingGlobals.baseFishVelocity * self.myData['speed']
        self.accel = FishingGlobals.baseFishAccel * self.myData['speed']
        self.fishMoveSequence = None
        self.bubbleEffect = None
        return

    def initCollisions(self):
        self.collisionVisual = loader.loadModel('models/props/crate')
        self.collisionVisual.setTransparency(1)
        self.collisionVisual.setColor(1.0, 1.0, 1.0, 0.3)
        self.collisionVisual.setScale(*self.myData['collisionBoxSize'])
        self.collisionVisual.setPos(*self.myData['collisionBoxOffset'])
        self.collisionVisual.reparentTo(self)
        self.collisionVisual.hide()
        self.attractionVisual = loader.loadModel('models/ammunition/cannonball')
        self.attractionVisual.setTransparency(1)
        self.attractionVisual.setColor(0.0, 1.0, 0.0, 0.3)
        self.attractionVisual.setScale(self.myData['attractionRadius'])
        self.attractionVisual.reparentTo(self.attractionPoint)
        self.attractionVisual.hide()
        self.collisionVisualVisible = False

    def hide(self):
        NodePath.hide(self)
        if self.idleBubbleEffect:
            self.idleBubbleEffect.hide()

    def show(self):
        NodePath.show(self)
        if self.idleBubbleEffect:
            self.idleBubbleEffect.show()

    def reloadCollisions(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.collisionVisual.removeNode()
            self.attractionVisual.removeNode()
            self.initCollisions()

    def cleanFishData(self):
        pass

    def destroy(self):
        self.closeFish = []
        self.actor.destroy()
        self.stopIdleBubbleEffect()
        self.stopFightBubbleEffect()
        if self.fishMoveSequence:
            self.fishMoveSequence.pause()
            self.fishMoveSequence = None
        if self.fsm:
            del self.fsm
            self.fsm = None
        self.behaviorNameToFunction = {}
        self.removeNode()
        return

    def pickPositionAndSwim(self):
        self.initVariables()
        self.actor.clearControlEffectWeights()
        if self.myData['depth'] == 0:
            depth = random.uniform(FishingGlobals.fishingLevelBoundaries[self.myData['depth']], self.fishManager.gameObject.waterLevel + FishingGlobals.fishSpawnBelowWaterLevelHeight)
        else:
            depth = random.uniform(FishingGlobals.fishingLevelBoundaries[self.myData['depth']], FishingGlobals.fishingLevelBoundaries[self.myData['depth'] - 1])
        startX = random.uniform(FishingGlobals.leftFishBarrier + 5.0, FishingGlobals.rightFishBarrier - 5.0)
        self.setPos(startX, 0.0, depth)
        if random.randint(0, 1):
            self.fsm.request('TurnAround', 'Swimming', False)
        else:
            self.fsm.request('Swimming')

    def turnAround(self, nextState, shouldMoveRight):
        if self.velocity[0] < 0 and shouldMoveRight:
            self.velocity[0] = -self.velocity[0]
        elif self.velocity[0] > 0 and not shouldMoveRight:
            self.velocity[0] = -self.velocity[0]
        self.movingRight = self.velocity[0] > 0
        if self.fishMoveSequence:
            self.fishMoveSequence.pause()
            self.fishMoveSequence.clearToInitial()
        animationToTurn = 'turn'
        if self.movingRight:
            animationToTurn = 'turnOpposite'
        durationOfFishTurn = self.myData['durationOfFishTurn']
        self.fishMoveSequence = Parallel(Sequence(Func(self.actor.changeAnimationTo, animationToTurn, False), Wait(durationOfFishTurn), Func(self.fsm.request, nextState)), Sequence(Wait(durationOfFishTurn * 0.33), Func(self.setXVelocity, 0.0), Wait(durationOfFishTurn * 0.66), Func(self.setXVelocity, self.velocity[0])), name='%s_turnAroundInterval' % self.getName())
        self.velocity[0] = -self.velocity[0]
        self.fishMoveSequence.start()

    def setXVelocity(self, newVel):
        self.velocity[0] = newVel

    def checkForBiting(self):
        if self.fishManager.activeFish is not None:
            return
        if self.fishManager.gameObject.fsm.getCurrentOrNextState() not in ['Fishing', 'Reeling', 'LureStall', 'LegdFishShow']:
            return
        inv = localAvatar.getInventory()
        rodLvl = inv.getItemQuantity(InventoryType.FishingRod)
        if self.myData['depth'] + 1 > rodLvl:
            return
        self.fsm.request('Biting')
        return

    def checkForBoxOverlap(self, otherFish):
        pos = self.getPos(self.fishManager.gameObject.fishingSpot)
        size = self.myData['collisionBoxSize']
        offset = list(self.myData['collisionBoxOffset'])
        otherPos = otherFish.getPos()
        otherSize = otherFish.myData['collisionBoxSize']
        otherOffset = list(otherFish.myData['collisionBoxOffset'])
        if pos[0] + size[0] / 2.0 + offset[0] > otherPos[0] - otherSize[0] / 2.0 + otherOffset[0] and pos[0] - size[0] / 2.0 + offset[0] < otherPos[0] + otherSize[0] / 2.0 + otherOffset[0] and pos[2] + size[2] / 2.0 + offset[2] > otherPos[2] - otherSize[2] / 2.0 + otherOffset[2] and pos[2] - size[2] / 2.0 + offset[2] < otherPos[2] + otherSize[2] / 2.0 + otherOffset[2]:
            return True
        return False

    def checkForCloseFish(self, index):
        if index < len(self.fishManager.uncaughtFish) - 1:
            for i in range(index + 1, len(self.fishManager.uncaughtFish)):
                if self.fishManager.uncaughtFish[i].index != self.index:
                    if self.checkForBoxOverlap(self.fishManager.uncaughtFish[i]):
                        self.closeFish.append(self.fishManager.uncaughtFish[i])
                        if FishingGlobals.wantDebugCollisionVisuals:
                            self.collisionVisual.setColor(1, 0, 0, 0.3)

        if len(self.closeFish) == 0:
            if FishingGlobals.wantDebugCollisionVisuals:
                self.collisionVisual.setColor(1, 1, 1, 0.3)

    def checkForLures(self, currentState, lurePos):
        if self.getX() + FishingGlobals.fishAttractionOffset < lurePos[0] and self.movingRight or self.getX() - FishingGlobals.fishAttractionOffset > lurePos[0] and not self.movingRight:
            if self.attractionPoint.getDistance(self.fishManager.gameObject.lure) < self.myData['attractionRadius'] + self.fishManager.gameObject.lure.lureAttractRadius:
                self.checkForBiting()

    def update(self, dt, index, lurePos):
        currentState = self.fsm.getCurrentOrNextState()
        self.closeFish = []
        if currentState in ['ScareAway', 'Swimming', 'Flee', 'TurnAround']:
            self.checkForCloseFish(index)
            if currentState in ['Swimming']:
                self.checkForLures(currentState, lurePos)
            self.updateBasedOnBehavior(dt, lurePos)
        elif currentState in ['Hooked', 'AboutToFight', 'HookedFighting']:
            self.checkForCloseFish(-1)
            for fish in self.closeFish:
                self.makeFishRunFromMe(fish)

    def makeFishRunFromMe(self, otherFish):
        if otherFish.fsm.getCurrentOrNextState() == 'Flee' or otherFish.fsm.getCurrentOrNextState() == 'TurnAround':
            return
        if otherFish.getX() < self.getX(self.fishManager.gameObject.fishingSpot) and otherFish.movingRight:
            otherFish.fsm.request('TurnAround', 'Flee', False)
        elif otherFish.getX() > self.getX(self.fishManager.gameObject.fishingSpot) and not otherFish.movingRight:
            otherFish.fsm.request('TurnAround', 'Flee', True)
        else:
            otherFish.fsm.request('Flee')

    def updateBasedOnBehavior(self, dt, lurePos):
        currentState = self.fsm.getCurrentOrNextState()
        newX = self.getX()
        newY = self.getY()
        newZ = self.getZ()
        for fish in self.closeFish:
            if self.myData['size'] == 'small' and fish.myData['size'] == 'large':
                if self.checkForEating(fish):
                    return
            self.avoidingFish = True
            if fish.velocity[1] > 0.0 and fish.avoidingFish:
                self.velocity[1] = -FishingGlobals.fishAvoidYVelocity
            else:
                self.velocity[1] = FishingGlobals.fishAvoidYVelocity
            if abs(fish.getY() - self.getY()) > self.myData['collisionBoxSize'][1] + fish.myData['collisionBoxSize'][1]:
                self.velocity[1] = 0.0

        if len(self.closeFish) == 0 and abs(self.getY()) > FishingGlobals.fishYTolerance:
            self.avoidingFish = False
            if self.getY() > 0:
                self.velocity[1] = -FishingGlobals.fishAvoidYVelocity
            else:
                self.velocity[1] = FishingGlobals.fishAvoidYVelocity
        elif len(self.closeFish) == 0 and abs(self.getY()) < FishingGlobals.fishYTolerance:
            self.avoidingFish = False
            self.velocity[1] = 0.0
            self.setY(0.0)
        newY = self.getY() + self.velocity[1] * dt + self.accel[1] * dt * dt
        if currentState in ['Swimming', 'TurnAround', 'Flee', 'ScareAway']:
            if currentState == 'ScareAway':
                newX, newZ = self.performScareAwayBehavior(dt, self.velocity, self.accel)
            else:
                if currentState == 'Flee':
                    newX, newZ = self.performFleeBehavior(dt, self.velocity, self.accel)
                else:
                    newX, newZ = self.behaviorNameToFunction[self.myData['behaviorDict']['name']](dt, self.velocity, self.accel)
                currentState = self.fsm.getCurrentOrNextState()
                if newX < FishingGlobals.leftFishBarrier:
                    if currentState == 'ScareAway':
                        if newX < FishingGlobals.leftFishBarrier - FishingGlobals.fullyOffscreenXOffset:
                            self.fsm.request('Offscreen')
                            return
                    elif currentState != 'TurnAround' and not self.movingRight:
                        self.fsm.request('TurnAround', 'Swimming', True)
                elif newX > FishingGlobals.rightFishBarrier:
                    if currentState != 'TurnAround' and self.movingRight:
                        self.fsm.request('TurnAround', 'Swimming', False)
            newZ = min(max(FishingGlobals.fishingLevelBoundaries[len(FishingGlobals.fishingLevelBoundaries) - 1], newZ), self.fishManager.gameObject.waterLevel + FishingGlobals.fishSpawnBelowWaterLevelHeight)
        self.setPos(newX, newY, newZ)

    def checkForEating(self, fishThatWillEat):
        if (self.getX() < fishThatWillEat.getX() and not fishThatWillEat.movingRight or self.getX() > fishThatWillEat.getX() and fishThatWillEat.movingRight) and self.fsm.getCurrentOrNextState() == 'Swimming' and fishThatWillEat.fsm.getCurrentOrNextState() == 'Swimming' and random.random() < 1.0:
            self.fsm.request('BeingEaten', fishThatWillEat)
            fishThatWillEat.fsm.request('Eating', self.weight)
            return True
        return False

    def startIdleBubbleEffect(self):
        self.idleBubbleEffect = FishIdleBubbleEffect.getEffect(unlimited=True)
        if self.idleBubbleEffect:
            self.idleBubbleEffect.reparentTo(self.mouthJoint)
            self.idleBubbleEffect.setScale(1.0)
            self.idleBubbleEffect.setHpr(0, 0, 0)
            self.idleBubbleEffect.setLifespanBasedOnDepth(self.getPos(render))
            self.idleBubbleEffect.setBubbleSizeBasedOnWeight(self.weight)
            self.idleBubbleEffect.particleDummy.setBin('fishingGame', 5)
            self.idleBubbleEffect.startLoop()

    def stopIdleBubbleEffect(self):
        if self.idleBubbleEffect:
            self.idleBubbleEffect.stopLoop()
        self.idleBubbleEffect = None
        return

    def startBiteBubbleEffect(self):
        self.biteBubbleEffect = FishBitingBubbleEffect.getEffect(unlimited=True)
        if self.biteBubbleEffect:
            self.biteBubbleEffect.reparentTo(self.mouthJoint)
            self.biteBubbleEffect.setScale(1.0)
            self.biteBubbleEffect.setHpr(0, 0, 0)
            self.biteBubbleEffect.setLifespanBasedOnDepth(self.getPos(render))
            self.biteBubbleEffect.setBubbleSizeBasedOnWeight(self.weight)
            self.biteBubbleEffect.particleDummy.setBin('fishingGame', 5)
            self.biteBubbleEffect.play()

    def stopBiteBubbleEffect(self):
        if self.biteBubbleEffect:
            self.biteBubbleEffect.stopLoop()
        self.biteBubbleEffect = None
        return

    def startFightBubbleEffect(self):
        self.fightBubbleEffect = FishFightingHookedBubbleEffect.getEffect(unlimited=True)
        if self.fightBubbleEffect:
            self.fightBubbleEffect.reparentTo(self.mouthJoint)
            self.fightBubbleEffect.setScale(1.0)
            self.fightBubbleEffect.setHpr(0, 0, 0)
            self.fightBubbleEffect.setLifespanBasedOnDepth(self.getPos(render))
            self.fightBubbleEffect.setBubbleSizeBasedOnWeight(self.weight)
            self.fightBubbleEffect.particleDummy.setBin('fishingGame', 5)
            self.fightBubbleEffect.startLoop()

    def stopFightBubbleEffect(self):
        if self.fightBubbleEffect:
            self.fightBubbleEffect.stopLoop()
        self.fightBubbleEffect = None
        return

    def performStraightBehavior(self, dt, velocity, accel):
        newX = self.getX() + velocity[0] * dt + accel[0] * dt * dt
        newZ = self.getZ() + velocity[2] * dt + accel[2] * dt * dt
        return (
         newX, newZ)

    def performSineStraightBehavior(self, dt, velocity, accel):
        self.sineDtAccumulator += dt
        newX = self.getX() + velocity[0] * dt + accel[0] * dt * dt
        newZ = self.myZ + math.sin(self.sineDtAccumulator) * self.myData['behaviorDict']['sineMultiplier']
        return (
         newX, newZ)

    def performScareAwayBehavior(self, dt, velocity, accel):
        newX = self.getX() + velocity[0] * FishingGlobals.scareAwayVelocityMultiplier * dt + accel[0] * dt * dt
        newZ = self.getZ() + velocity[2] * FishingGlobals.scareAwayVelocityMultiplier * dt + accel[2] * dt * dt
        return (
         newX, newZ)

    def performFleeBehavior(self, dt, velocity, accel):
        newX = self.getX() + velocity[0] * FishingGlobals.fleeVelocityMultiplier * dt + accel[0] * dt * dt
        newZ = self.getZ() + velocity[2] * FishingGlobals.fleeVelocityMultiplier * dt + accel[2] * dt * dt
        return (
         newX, newZ)

    def performErraticBehavior(self, dt, velocity, accel):
        self.erraticDtAccumulator += dt
        self.sineDtAccumulator += dt
        newX = self.getX() + velocity[0] * dt + accel[0] * dt * dt
        newZ = self.myZ + math.sin(self.sineDtAccumulator) * self.myData['behaviorDict']['sineMultiplier']
        if self.erraticDtAccumulator > self.myData['behaviorDict']['secondsBetweenChanges']:
            self.erraticDtAccumulator = 0
            if random.random() < self.myData['behaviorDict']['chanceOfTurning']:
                if self.fsm.getCurrentOrNextState() != 'TurnAround':
                    self.fsm.request('TurnAround', 'Swimming', not self.movingRight)
        return (
         newX, newZ)

    def showAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.attractionVisual.show()

    def hideAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.attractionVisual.hide()

    def showAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.collisionVisual.show()

    def hideAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.collisionVisual.hide()
예제 #5
0
class FishLure(NodePath):
    def __init__(self, gameObject, type):
        NodePath.__init__(self, 'FishingLure_%s' % type)
        self.gameObject = gameObject
        self.type = type
        self.initLureModel()
        if FishingGlobals.wantDebugCollisionVisuals:
            self.initCollision()

        self.initLureHelpText()
        self.setLightOff()

    def initLureModel(self):
        self.lureModel = NodePath('lure')
        self.currentLureType = 'regular'
        self.lureAttractRadius = FishingGlobals.lureTypeToAttractRadius[
            self.currentLureType]
        self.lureModel = loader.loadModel(
            FishingGlobals.lureTypeToModel[self.currentLureType])
        self.lureModel.setScale(2.0)
        self.lureModel.reparentTo(self)
        self.lureModel.setDepthWrite(False)
        self.lureModel.setDepthTest(True)
        self.lureModel.setBin('ground', 25)
        self.mainGui = loader.loadModel('models/gui/gui_main')

    def initCollision(self):
        self.lureCollisionVisual = loader.loadModel(
            'models/ammunition/cannonball')
        self.lureCollisionVisual.setTransparency(1)
        self.lureCollisionVisual.setColor(0.0, 0.0, 1.0, 0.29999999999999999)
        self.lureCollisionVisual.setScale(self.lureAttractRadius)
        self.lureCollisionVisual.reparentTo(self)
        self.lureCollisionVisual.hide()

    def initLureHelpText(self):
        self.helpTextNode = TextNode('fishBitingIcon')
        self.helpTextNodePath = NodePath(self.helpTextNode)
        self.helpTextNodePath.setPos(0.0, 0.0, 0.69999999999999996)
        self.helpTextNode.setText(' ')
        self.helpTextNode.setAlign(TextNode.ACenter)
        self.helpTextNode.setFont(PiratesGlobals.getPirateFont())
        self.helpTextNode.setTextColor(1.0, 1.0, 1.0, 1.0)
        self.helpTextNodePath.reparentTo(self)
        self.helpTextNodePath.setBillboardPointEye()
        self.helpTextNodePath.setBin('fishingGame', 10)
        self.helpTextNodePath.hide()

    def enableLureGlow(self, glowType):
        self.lureGlow = LureGlow.getEffect()
        if self.lureGlow:
            if self.gameObject.fishManager.activeFish is not None:
                self.lureGlow.reparentTo(
                    self.gameObject.fishManager.activeFish.mouthJoint)
            else:
                self.lureGlow.reparentTo(self)
            self.lureGlow.effectColor = _glowColors[glowType]
            self.lureGlow.setShaderOff()
            self.lureGlow.setBin('fishingGame', 5)
            self.lureGlow.play()

    def showHelpText(self, textToShow):
        taskMgr.remove(
            self.gameObject.distributedFishingSpot.uniqueName('ClearLureText'))
        if textToShow is None:
            self.helpTextNode.setText(' ')
            self.helpTextNodePath.hide()
        elif self.gameObject.fishManager.activeFish is not None:
            self.helpTextNodePath.setScale(
                FishingGlobals.fishSizeToHelpTextScale[
                    self.gameObject.fishManager.activeFish.myData['size']])
        else:
            self.helpTextNodePath.setScale(1.0)
        self.helpTextNode.setText(textToShow)
        self.helpTextNodePath.show()
        taskMgr.doMethodLater(
            FishingGlobals.lureHelpTextDuration,
            self.showHelpText,
            name=self.gameObject.distributedFishingSpot.uniqueName(
                'ClearLureText'),
            extraArgs=[None])

    def setLureType(self, type):
        self.currentLureType = type
        if type == None:
            self.lureModel.hide()
            self.gameObject.gui.setTackleBoxPulse(True)
        elif type == 'regular':
            self.gameObject.gui.setTackleBoxPulse(False)
            self.currentLureType = type
            self.lureModel.removeNode()
            self.lureAttractRadius = FishingGlobals.lureTypeToAttractRadius[
                type]
            self.lureModel = loader.loadModel(
                FishingGlobals.lureTypeToModel[type])
            self.lureModel.setScale(2.0)
            self.lureModel.reparentTo(self)
            self.lureModel.setDepthWrite(False)
            self.lureModel.setDepthTest(True)
            self.lureModel.setBin('ground', 25)
        elif type == 'legendary':
            self.gameObject.gui.setTackleBoxPulse(False)
            self.currentLureType = type
            self.lureModel.removeNode()
            self.lureAttractRadius = FishingGlobals.lureTypeToAttractRadius[
                type]
            self.lureModel = loader.loadModel(
                FishingGlobals.lureTypeToModel[type])
            self.lureModel.setScale(2.0)
            self.lureModel.reparentTo(self)
            self.lureModel.setDepthWrite(False)
            self.lureModel.setDepthTest(True)
            self.lureModel.setBin('ground', 25)

    def showCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.lureCollisionVisual.show()

    def hideCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.lureCollisionVisual.hide()

    def requestPitch(self, fish):
        offset = fish.getPos() - self.getPos()
        if fish.getX() < self.getX():
            return math.degrees(math.atan2(offset.getZ(), -offset.getX()))
        else:
            return math.degrees(math.atan2(-offset.getZ(), offset.getX()))

    def resetLureModel(self):
        self.lureModel.reparentTo(self)
        self.lureModel.show()
        self.lureModel.setPosHpr(0, 0, 0, 0, 0, 0.0)
        if self.currentLureType == None:
            self.lureModel.hide()
            self.gameObject.gui.setTackleBoxPulse(True)
        else:
            self.gameObject.gui.setTackleBoxPulse(False)

    def destroy(self):
        self.lureModel.removeNode()
        self.removeNode()
class Fish(NodePath):
    
    def __init__(self, fishManager, myData, index, trophy = 0):
        NodePath.__init__(self, '%s_%d' % (myData['name'], index))
        self.trophy = trophy
        self.myData = myData
        if not self.trophy:
            self.fishManager = fishManager
            self.index = index
            self.fsm = FishFSM(self)
            self.weight = random.randint(self.myData['weightRange'][0], self.myData['weightRange'][1])
        else:
            self.weight = trophy
        self.adjustedScale = (self.myData['scaleRange'][1] - self.myData['scaleRange'][0]) * (self.weight - self.myData['weightRange'][0]) / (self.myData['weightRange'][1] - self.myData['weightRange'][0]) + self.myData['scaleRange'][0]
        self.initActor()
        if not self.trophy:
            self.initVariables()
            self.initFishStatusIcon()
            if FishingGlobals.wantDebugCollisionVisuals:
                self.initCollisions()
            
        
        self.avoidingFish = False
        self.biteBubbleEffect = None
        self.idleBubbleEffect = None
        self.fightBubbleEffect = None
        self.behaviorNameToFunction = {
            'straight': self.performStraightBehavior,
            'sineStraight': self.performSineStraightBehavior,
            'erratic': self.performErraticBehavior }
        self.sineDtAccumulator = 0.0
        self.erraticDtAccumulator = 0.0
        self.myZ = 0.0
        if not self.trophy:
            self.setLightOff()
        

    
    def initActor(self):
        self.animDict = { }
        for anim in FishingGlobals.fishAnimations:
            self.animDict[anim] = 'models/char/pir_a_gam_fsh_%s_%s.bam' % (self.myData['model'], anim)
        
        self.actor = BlendActor('models/char/pir_r_gam_fsh_%s.bam' % self.myData['model'], self.animDict, FishingGlobals.defaultFishBlendTime, FishingGlobals.fishBlendTimeDict)
        self.actor.reparentTo(self)
        self.actor.setScale(self.adjustedScale)
        self.mouthJoint = self.actor.exposeJoint(None, 'modelRoot', 'hookAttach')
        self.attractionPoint = NodePath('AttractionPoint')
        self.attractionPoint.reparentTo(self.mouthJoint)
        self.attractionPoint.setPos(0.0, 0.0, 0.0)
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdle')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdleOpposite')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turn')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turnOpposite')
        if not self.trophy:
            self.setBin('fishingGame', 10)
        

    
    def codeReload(self):
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdle')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['swimAnimationMultiplier'], 'swimIdleOpposite')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turn')
        self.actor.setPlayRate(self.myData['speed'] * self.myData['turnAnimationMultiplier'], 'turnOpposite')

    
    def initFishStatusIcon(self):
        self.fishStatusIconTextNode = TextNode('fishBitingIcon')
        self.fishStatusIconNodePath = NodePath(self.fishStatusIconTextNode)
        self.fishStatusIconNodePath.setPos(0.0, 0.0, self.myData['indicatorHeightOffset'])
        self.fishStatusIconTextNode.setText('?')
        self.fishStatusIconTextNode.setTextColor(1.0, 0.0, 0.0, 1.0)
        self.fishStatusIconNodePath.reparentTo(self.mouthJoint)
        self.fishStatusIconNodePath.setBillboardPointEye()
        self.fishStatusIconNodePath.hide()
        self.fishStatusIconNodePath.setShaderOff()

    
    def initVariables(self):
        self.attractionVisual = None
        self.collisionVisual = None
        self.movingRight = True
        self.turnSpeed = 160.0
        self.turnTowardLureInterval = None
        self.velocity = FishingGlobals.baseFishVelocity * self.myData['speed']
        self.accel = FishingGlobals.baseFishAccel * self.myData['speed']
        self.fishMoveSequence = None
        self.bubbleEffect = None

    
    def initCollisions(self):
        self.collisionVisual = loader.loadModel('models/props/crate')
        self.collisionVisual.setTransparency(1)
        self.collisionVisual.setColor(1.0, 1.0, 1.0, 0.29999999999999999)
        self.collisionVisual.setScale(*self.myData['collisionBoxSize'])
        self.collisionVisual.setPos(*self.myData['collisionBoxOffset'])
        self.collisionVisual.reparentTo(self)
        self.collisionVisual.hide()
        self.attractionVisual = loader.loadModel('models/ammunition/cannonball')
        self.attractionVisual.setTransparency(1)
        self.attractionVisual.setColor(0.0, 1.0, 0.0, 0.29999999999999999)
        self.attractionVisual.setScale(self.myData['attractionRadius'])
        self.attractionVisual.reparentTo(self.attractionPoint)
        self.attractionVisual.hide()
        self.collisionVisualVisible = False

    
    def hide(self):
        NodePath.hide(self)
        if self.idleBubbleEffect:
            self.idleBubbleEffect.hide()
        

    
    def show(self):
        NodePath.show(self)
        if self.idleBubbleEffect:
            self.idleBubbleEffect.show()
        

    
    def reloadCollisions(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.collisionVisual.removeNode()
            self.attractionVisual.removeNode()
            self.initCollisions()
        

    
    def cleanFishData(self):
        pass

    
    def destroy(self):
        self.closeFish = []
        self.actor.destroy()
        self.stopIdleBubbleEffect()
        self.stopFightBubbleEffect()
        if self.fishMoveSequence:
            self.fishMoveSequence.pause()
            self.fishMoveSequence = None
        
        if self.fsm:
            del self.fsm
            self.fsm = None
        
        self.behaviorNameToFunction = { }
        self.removeNode()

    
    def pickPositionAndSwim(self):
        self.initVariables()
        self.actor.clearControlEffectWeights()
        if self.myData['depth'] == 0:
            depth = random.uniform(FishingGlobals.fishingLevelBoundaries[self.myData['depth']], self.fishManager.gameObject.waterLevel + FishingGlobals.fishSpawnBelowWaterLevelHeight)
        else:
            depth = random.uniform(FishingGlobals.fishingLevelBoundaries[self.myData['depth']], FishingGlobals.fishingLevelBoundaries[self.myData['depth'] - 1])
        startX = random.uniform(FishingGlobals.leftFishBarrier + 5.0, FishingGlobals.rightFishBarrier - 5.0)
        self.setPos(startX, 0.0, depth)
        if random.randint(0, 1):
            self.fsm.request('TurnAround', 'Swimming', False)
        else:
            self.fsm.request('Swimming')

    
    def turnAround(self, nextState, shouldMoveRight):
        if self.velocity[0] < 0 and shouldMoveRight:
            self.velocity[0] = -self.velocity[0]
        elif self.velocity[0] > 0 and not shouldMoveRight:
            self.velocity[0] = -self.velocity[0]
        
        self.movingRight = self.velocity[0] > 0
        if self.fishMoveSequence:
            self.fishMoveSequence.pause()
            self.fishMoveSequence.clearToInitial()
        
        animationToTurn = 'turn'
        if self.movingRight:
            animationToTurn = 'turnOpposite'
        
        durationOfFishTurn = self.myData['durationOfFishTurn']
        self.fishMoveSequence = Parallel(Sequence(Func(self.actor.changeAnimationTo, animationToTurn, False), Wait(durationOfFishTurn), Func(self.fsm.request, nextState)), Sequence(Wait(durationOfFishTurn * 0.33000000000000002), Func(self.setXVelocity, 0.0), Wait(durationOfFishTurn * 0.66000000000000003), Func(self.setXVelocity, self.velocity[0])), name = '%s_turnAroundInterval' % self.getName())
        self.velocity[0] = -self.velocity[0]
        self.fishMoveSequence.start()

    
    def setXVelocity(self, newVel):
        self.velocity[0] = newVel

    
    def checkForBiting(self):
        if self.fishManager.activeFish is not None:
            return None
        
        if self.fishManager.gameObject.fsm.getCurrentOrNextState() not in [
            'Fishing',
            'Reeling',
            'LureStall',
            'LegdFishShow']:
            return None
        
        inv = localAvatar.getInventory()
        rodLvl = inv.getItemQuantity(InventoryType.FishingRod)
        if self.myData['depth'] + 1 > rodLvl:
            return None
        
        self.fsm.request('Biting')

    
    def checkForBoxOverlap(self, otherFish):
        pos = self.getPos(self.fishManager.gameObject.fishingSpot)
        size = self.myData['collisionBoxSize']
        offset = list(self.myData['collisionBoxOffset'])
        otherPos = otherFish.getPos()
        otherSize = otherFish.myData['collisionBoxSize']
        otherOffset = list(otherFish.myData['collisionBoxOffset'])
        if pos[0] + size[0] / 2.0 + offset[0] > (otherPos[0] - otherSize[0] / 2.0) + otherOffset[0] and (pos[0] - size[0] / 2.0) + offset[0] < otherPos[0] + otherSize[0] / 2.0 + otherOffset[0] and pos[2] + size[2] / 2.0 + offset[2] > (otherPos[2] - otherSize[2] / 2.0) + otherOffset[2] and (pos[2] - size[2] / 2.0) + offset[2] < otherPos[2] + otherSize[2] / 2.0 + otherOffset[2]:
            return True
        
        return False

    
    def checkForCloseFish(self, index):
        if index < len(self.fishManager.uncaughtFish) - 1:
            for i in range(index + 1, len(self.fishManager.uncaughtFish)):
                if self.fishManager.uncaughtFish[i].index != self.index:
                    if self.checkForBoxOverlap(self.fishManager.uncaughtFish[i]):
                        self.closeFish.append(self.fishManager.uncaughtFish[i])
                        if FishingGlobals.wantDebugCollisionVisuals:
                            self.collisionVisual.setColor(1, 0, 0, 0.29999999999999999)
                        
                    
                self.checkForBoxOverlap(self.fishManager.uncaughtFish[i])
            
        
        if len(self.closeFish) == 0:
            if FishingGlobals.wantDebugCollisionVisuals:
                self.collisionVisual.setColor(1, 1, 1, 0.29999999999999999)
            
        

    
    def checkForLures(self, currentState, lurePos):
        if (self.getX() + FishingGlobals.fishAttractionOffset < lurePos[0] or self.movingRight or self.getX() - FishingGlobals.fishAttractionOffset > lurePos[0]) and not (self.movingRight):
            if self.attractionPoint.getDistance(self.fishManager.gameObject.lure) < self.myData['attractionRadius'] + self.fishManager.gameObject.lure.lureAttractRadius:
                self.checkForBiting()
            
        

    
    def update(self, dt, index, lurePos):
        currentState = self.fsm.getCurrentOrNextState()
        self.closeFish = []
        if currentState in [
            'ScareAway',
            'Swimming',
            'Flee',
            'TurnAround']:
            self.checkForCloseFish(index)
            if currentState in [
                'Swimming']:
                self.checkForLures(currentState, lurePos)
            
            self.updateBasedOnBehavior(dt, lurePos)
        elif currentState in [
            'Hooked',
            'AboutToFight',
            'HookedFighting']:
            self.checkForCloseFish(-1)
            for fish in self.closeFish:
                self.makeFishRunFromMe(fish)
            
        

    
    def makeFishRunFromMe(self, otherFish):
        if otherFish.fsm.getCurrentOrNextState() == 'Flee' or otherFish.fsm.getCurrentOrNextState() == 'TurnAround':
            return None
        
        if otherFish.getX() < self.getX(self.fishManager.gameObject.fishingSpot) and otherFish.movingRight:
            otherFish.fsm.request('TurnAround', 'Flee', False)
        elif otherFish.getX() > self.getX(self.fishManager.gameObject.fishingSpot) and not (otherFish.movingRight):
            otherFish.fsm.request('TurnAround', 'Flee', True)
        else:
            otherFish.fsm.request('Flee')

    
    def updateBasedOnBehavior(self, dt, lurePos):
        currentState = self.fsm.getCurrentOrNextState()
        newX = self.getX()
        newY = self.getY()
        newZ = self.getZ()
        for fish in self.closeFish:
            if self.myData['size'] == 'small' and fish.myData['size'] == 'large':
                if self.checkForEating(fish):
                    return None
                
            
            self.avoidingFish = True
            if fish.velocity[1] > 0.0 and fish.avoidingFish:
                self.velocity[1] = -(FishingGlobals.fishAvoidYVelocity)
            else:
                self.velocity[1] = FishingGlobals.fishAvoidYVelocity
            if abs(fish.getY() - self.getY()) > self.myData['collisionBoxSize'][1] + fish.myData['collisionBoxSize'][1]:
                self.velocity[1] = 0.0
                continue
        
        if len(self.closeFish) == 0 and abs(self.getY()) > FishingGlobals.fishYTolerance:
            self.avoidingFish = False
            if self.getY() > 0:
                self.velocity[1] = -(FishingGlobals.fishAvoidYVelocity)
            else:
                self.velocity[1] = FishingGlobals.fishAvoidYVelocity
        elif len(self.closeFish) == 0 and abs(self.getY()) < FishingGlobals.fishYTolerance:
            self.avoidingFish = False
            self.velocity[1] = 0.0
            self.setY(0.0)
        
        newY = self.getY() + self.velocity[1] * dt + self.accel[1] * dt * dt
        if currentState in [
            'Swimming',
            'TurnAround',
            'Flee',
            'ScareAway']:
            if currentState == 'ScareAway':
                (newX, newZ) = self.performScareAwayBehavior(dt, self.velocity, self.accel)
            elif currentState == 'Flee':
                (newX, newZ) = self.performFleeBehavior(dt, self.velocity, self.accel)
            else:
                (newX, newZ) = self.behaviorNameToFunction[self.myData['behaviorDict']['name']](dt, self.velocity, self.accel)
            currentState = self.fsm.getCurrentOrNextState()
            if newX < FishingGlobals.leftFishBarrier:
                if currentState == 'ScareAway':
                    if newX < FishingGlobals.leftFishBarrier - FishingGlobals.fullyOffscreenXOffset:
                        self.fsm.request('Offscreen')
                        return None
                    
                elif currentState != 'TurnAround' and not (self.movingRight):
                    self.fsm.request('TurnAround', 'Swimming', True)
                
            elif newX > FishingGlobals.rightFishBarrier:
                if currentState != 'TurnAround' and self.movingRight:
                    self.fsm.request('TurnAround', 'Swimming', False)
                
            
            newZ = min(max(FishingGlobals.fishingLevelBoundaries[len(FishingGlobals.fishingLevelBoundaries) - 1], newZ), self.fishManager.gameObject.waterLevel + FishingGlobals.fishSpawnBelowWaterLevelHeight)
        
        self.setPos(newX, newY, newZ)

    
    def checkForEating(self, fishThatWillEat):
        if (self.getX() < fishThatWillEat.getX() or not (fishThatWillEat.movingRight) or self.getX() > fishThatWillEat.getX() or fishThatWillEat.movingRight) and self.fsm.getCurrentOrNextState() == 'Swimming' and fishThatWillEat.fsm.getCurrentOrNextState() == 'Swimming' and random.random() < 1.0:
            self.fsm.request('BeingEaten', fishThatWillEat)
            fishThatWillEat.fsm.request('Eating', self.weight)
            return True
        
        return False

    
    def startIdleBubbleEffect(self):
        self.idleBubbleEffect = FishIdleBubbleEffect.getEffect(unlimited = True)
        if self.idleBubbleEffect:
            self.idleBubbleEffect.reparentTo(self.mouthJoint)
            self.idleBubbleEffect.setScale(1.0)
            self.idleBubbleEffect.setHpr(0, 0, 0)
            self.idleBubbleEffect.setLifespanBasedOnDepth(self.getPos(render))
            self.idleBubbleEffect.setBubbleSizeBasedOnWeight(self.weight)
            self.idleBubbleEffect.particleDummy.setBin('fishingGame', 5)
            self.idleBubbleEffect.startLoop()
        

    
    def stopIdleBubbleEffect(self):
        if self.idleBubbleEffect:
            self.idleBubbleEffect.stopLoop()
        
        self.idleBubbleEffect = None

    
    def startBiteBubbleEffect(self):
        self.biteBubbleEffect = FishBitingBubbleEffect.getEffect(unlimited = True)
        if self.biteBubbleEffect:
            self.biteBubbleEffect.reparentTo(self.mouthJoint)
            self.biteBubbleEffect.setScale(1.0)
            self.biteBubbleEffect.setHpr(0, 0, 0)
            self.biteBubbleEffect.setLifespanBasedOnDepth(self.getPos(render))
            self.biteBubbleEffect.setBubbleSizeBasedOnWeight(self.weight)
            self.biteBubbleEffect.particleDummy.setBin('fishingGame', 5)
            self.biteBubbleEffect.play()
        

    
    def stopBiteBubbleEffect(self):
        if self.biteBubbleEffect:
            self.biteBubbleEffect.stopLoop()
        
        self.biteBubbleEffect = None

    
    def startFightBubbleEffect(self):
        self.fightBubbleEffect = FishFightingHookedBubbleEffect.getEffect(unlimited = True)
        if self.fightBubbleEffect:
            self.fightBubbleEffect.reparentTo(self.mouthJoint)
            self.fightBubbleEffect.setScale(1.0)
            self.fightBubbleEffect.setHpr(0, 0, 0)
            self.fightBubbleEffect.setLifespanBasedOnDepth(self.getPos(render))
            self.fightBubbleEffect.setBubbleSizeBasedOnWeight(self.weight)
            self.fightBubbleEffect.particleDummy.setBin('fishingGame', 5)
            self.fightBubbleEffect.startLoop()
        

    
    def stopFightBubbleEffect(self):
        if self.fightBubbleEffect:
            self.fightBubbleEffect.stopLoop()
        
        self.fightBubbleEffect = None

    
    def performStraightBehavior(self, dt, velocity, accel):
        newX = self.getX() + velocity[0] * dt + accel[0] * dt * dt
        newZ = self.getZ() + velocity[2] * dt + accel[2] * dt * dt
        return (newX, newZ)

    
    def performSineStraightBehavior(self, dt, velocity, accel):
        self.sineDtAccumulator += dt
        newX = self.getX() + velocity[0] * dt + accel[0] * dt * dt
        newZ = self.myZ + math.sin(self.sineDtAccumulator) * self.myData['behaviorDict']['sineMultiplier']
        return (newX, newZ)

    
    def performScareAwayBehavior(self, dt, velocity, accel):
        newX = self.getX() + velocity[0] * FishingGlobals.scareAwayVelocityMultiplier * dt + accel[0] * dt * dt
        newZ = self.getZ() + velocity[2] * FishingGlobals.scareAwayVelocityMultiplier * dt + accel[2] * dt * dt
        return (newX, newZ)

    
    def performFleeBehavior(self, dt, velocity, accel):
        newX = self.getX() + velocity[0] * FishingGlobals.fleeVelocityMultiplier * dt + accel[0] * dt * dt
        newZ = self.getZ() + velocity[2] * FishingGlobals.fleeVelocityMultiplier * dt + accel[2] * dt * dt
        return (newX, newZ)

    
    def performErraticBehavior(self, dt, velocity, accel):
        self.erraticDtAccumulator += dt
        self.sineDtAccumulator += dt
        newX = self.getX() + velocity[0] * dt + accel[0] * dt * dt
        newZ = self.myZ + math.sin(self.sineDtAccumulator) * self.myData['behaviorDict']['sineMultiplier']
        if self.erraticDtAccumulator > self.myData['behaviorDict']['secondsBetweenChanges']:
            self.erraticDtAccumulator = 0
            if random.random() < self.myData['behaviorDict']['chanceOfTurning']:
                if self.fsm.getCurrentOrNextState() != 'TurnAround':
                    self.fsm.request('TurnAround', 'Swimming', not (self.movingRight))
                
            
        
        return (newX, newZ)

    
    def showAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.attractionVisual.show()
        

    
    def hideAttractionCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.attractionVisual.hide()
        

    
    def showAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.collisionVisual.show()
        

    
    def hideAvoidanceCollisionVisuals(self):
        if FishingGlobals.wantDebugCollisionVisuals:
            self.collisionVisual.hide()