import math import random import time from panda3d.core import TextNode, BitMask32, Point3, Vec3, Vec4, deg2Rad, Mat3, NodePath, VBase4, CollisionTraverser, CollisionSegment, CollisionNode, CollisionHandlerQueue from panda3d.ode import OdeRayGeom from direct.distributed import DistributedObject from direct.directnotify import DirectNotifyGlobal from otp.otpbase import OTPGlobals from toontown.toonbase import ToontownGlobals from toontown.toonbase import TTLocalizer from toontown.toonbase import ToontownTimer from direct.gui.DirectGui import DirectWaitBar, DGG, DirectLabel from direct.task import Task from direct.fsm.FSM import FSM from toontown.minigame import ArrowKeys from direct.showbase import PythonUtil from toontown.golf import BuildGeometry from toontown.golf import DistributedPhysicsWorld from toontown.golf import GolfGlobals from direct.interval.IntervalGlobal import Sequence, Parallel, LerpScaleInterval, LerpFunctionInterval, Func, Wait, SoundInterval, ParallelEndTogether, LerpPosInterval, ActorInterval, LerpPosHprInterval, LerpColorScaleInterval, WaitInterval from direct.actor import Actor from toontown.golf import GolfHoleBase from toontown.distributed import DelayDelete class DistributedGolfHole(DistributedPhysicsWorld.DistributedPhysicsWorld, FSM, GolfHoleBase.GolfHoleBase): defaultTransitions = {'Off': ['Cleanup', 'ChooseTee', 'WatchTee'], 'ChooseTee': ['Aim', 'Cleanup'], 'WatchTee': ['WatchAim', 'Cleanup', 'WatchTee', 'ChooseTee', 'Aim'], 'Wait': ['Aim', 'WatchAim', 'Playback', 'Cleanup', 'ChooseTee', 'WatchTee'], 'Aim': ['Shoot', 'Playback', 'Cleanup', 'Aim', 'WatchAim'], 'WatchAim': ['WatchAim', 'WatchShoot', 'Playback', 'Cleanup', 'Aim', 'ChooseTee', 'WatchTee'], 'Playback': ['Wait', 'Aim', 'WatchAim', 'Cleanup', 'ChooseTee', 'WatchTee'], 'Cleanup': ['Off']} id = 0 notify = directNotify.newCategory('DistributedGolfHole') unlimitedAimTime = base.config.GetBool('unlimited-aim-time', 0) unlimitedTeeTime = base.config.GetBool('unlimited-tee-time', 0) golfPowerSpeed = base.config.GetDouble('golf-power-speed', 3) golfPowerExponent = base.config.GetDouble('golf-power-exponent', 0.75) DefaultCamP = -16 MaxCamP = -90 def __init__(self, cr): self.notify.debug('Hole Init') DistributedPhysicsWorld.DistributedPhysicsWorld.__init__(self, base.cr) GolfHoleBase.GolfHoleBase.__init__(self, 1) FSM.__init__(self, 'Golf_%s_FSM' % self.id) self.currentGolfer = 0 self.ballDict = {} self.ballShadowDict = {} self.holeNodes = [] self.golfCourse = None self.golfCourseRequest = None self.holePositions = [] self.timer = None self.teeTimer = None self.aimStart = None self.titleLabel = None self.teeInstructions = None self.aimInstructions = None self.powerReminder = None self.lastTimeHeadingSent = 0 self.lastTempHeadingSent = 0 self.holdCycleTime = 0.0 self.inPlayBack = 0 self.swingInterval = None self.sfxInterval = None self.isLookingAtPutt = False self.clubs = {} self.camInterval = None self.flyOverInterval = None self.needToDoFlyOver = True self.translucentLastFrame = [] self.translucentCurFrame = [] self.localMissedSwings = 0 self.localToonHitControl = False self.warningInterval = None self.playBackDelayDelete = None self.aimMomentum = 0.0 self.lastBumpSfxPos = Point3(0, 0, 0) self.__textGen = TextNode('golfHoleText') self.__textGen.setFont(ToontownGlobals.getSignFont()) self.__textGen.setAlign(TextNode.ACenter) if TTLocalizer.getLanguage() in ['castillian', 'japanese', 'german', 'portuguese', 'french']: self.__textGen.setGlyphScale(0.7) self.avIdList = [] self.enterAimStart = 0 return def generate(self): self.notify.debug('Hole Generate') DistributedPhysicsWorld.DistributedPhysicsWorld.generate(self) self.golfPowerTaskName = self.uniqueName('updateGolfPower') def announceGenerate(self): DistributedPhysicsWorld.DistributedPhysicsWorld.announceGenerate(self) self.setup() self.sendReady() self.request('Off') index = 1 for avId in self.avIdList: self.createBall(avId, index) self.createClub(avId) index += 1 if self.avIdList: avId = self.avIdList[0] self.currentGolfer = avId self.currentGolferActive = False def delete(self): self.removePlayBackDelayDelete() self.request('Cleanup') taskMgr.remove(self.golfPowerTaskName) DistributedPhysicsWorld.DistributedPhysicsWorld.delete(self) GolfHoleBase.GolfHoleBase.delete(self) if hasattr(self, 'perfectIval'): self.perfectIval.pause() del self.perfectIval self.golfCourse = None if self.teeInstructions: self.teeInstructions.destroy() self.teeInstructions = None if self.aimInstructions: self.aimInstructions.destory() self.aimInstructions = None if self.powerReminder: self.powerReminder.destroy() self.powerReminder = None if self.swingInterval: self.swingInterval.pause() self.swingInterval = None if self.sfxInterval: self.sfxInterval.pause() self.sfxInterval = None if self.camInterval: self.camInterval.pause() self.camInterval = None for club in self.clubs: self.clubs[club].removeNode() del self.clubs if hasattr(self, 'scoreBoard'): if hasattr(self.scoreBoard, 'maximizeB'): if self.scoreBoard.maximizeB: self.scoreBoard.maximizeB.hide() if not self.titleLabel == None: self.titleLabel.destroy() self.notify.debug('Deleted title label') self.notify.debug('Delete function') if self.flyOverInterval: self.flyOverInterval.pause() self.flyOverInterval = None for key in self.ballShadowDict: self.ballShadowDict[key].removeNode() self.dropShadowModel.removeNode() return def sendReady(self): self.sendUpdate('setAvatarReadyHole', []) def createClub(self, avId): club = NodePath('club-%s' % avId) clubModel = loader.loadModel('phase_6/models/golf/putter') clubModel.reparentTo(club) clubModel.setR(clubModel, 45) self.clubs[avId] = club def attachClub(self, avId, pointToBall = False): club = self.clubs[avId] if club: av = base.cr.doId2do.get(avId) if av: av.useLOD(1000) lHand = av.getLeftHands()[0] club.setPos(0, 0, 0) club.reparentTo(lHand) netScale = club.getNetTransform().getScale()[1] counterActToonScale = lHand.find('**/counteractToonScale') if counterActToonScale.isEmpty(): counterActToonScale = lHand.attachNewNode('counteractToonScale') counterActToonScale.setScale(1 / netScale) self.notify.debug('creating counterActToonScale for %s' % av.getName()) club.reparentTo(counterActToonScale) club.setX(-0.25 * netScale) if pointToBall: club.lookAt(self.clubLookatSpot) def createToonRay(self): self.toonRay = OdeRayGeom(self.space, 10.0) self.toonRay.setCollideBits(BitMask32(16777215)) self.toonRay.setCategoryBits(BitMask32(0)) self.toonRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) self.space.setCollideId(self.toonRay, GolfGlobals.TOON_RAY_COLLIDE_ID) self.rayList.append(self.toonRay) def createSkyRay(self): self.skyRay = OdeRayGeom(self.space, 100.0) self.skyRay.setCollideBits(BitMask32(240)) self.skyRay.setCategoryBits(BitMask32(0)) self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) self.space.setCollideId(self.skyRay, 78) self.rayList.append(self.skyRay) def createCameraRay(self): self.cameraRay = OdeRayGeom(self.space, 30.0) self.cameraRay.setCollideBits(BitMask32(8388608)) self.cameraRay.setCategoryBits(BitMask32(0)) self.space.setCollideId(self.cameraRay, GolfGlobals.CAMERA_RAY_COLLIDE_ID) self.cameraRayNodePath = self.terrainModel.attachNewNode('cameraRayNodePath') self.rayList.append(self.cameraRay) def loadLevel(self): GolfHoleBase.GolfHoleBase.loadLevel(self) self.teeNodePath = self.terrainModel.find('**/tee0') if self.teeNodePath.isEmpty(): teePos = Vec3(0, 0, 10) else: teePos = self.teeNodePath.getPos() teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH()) self.teePositions = [teePos] teeIndex = 1 teeNode = self.terrainModel.find('**/tee%d' % teeIndex) while not teeNode.isEmpty(): teePos = teeNode.getPos() teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) self.teePositions.append(teePos) self.notify.debug('teeNodeP heading = %s' % teeNode.getH()) teeIndex += 1 teeNode = self.terrainModel.find('**/tee%d' % teeIndex) self.holeBottomNodePath = self.terrainModel.find('**/holebottom0') if self.holeBottomNodePath.isEmpty(): self.holeBottomPos = Vec3(*self.holeInfo['holePos'][0]) else: self.holeBottomPos = self.holeBottomNodePath.getPos() self.holePositions.append(self.holeBottomPos) minHard = Point3(0, 0, 0) maxHard = Point3(0, 0, 0) self.hardSurfaceNodePath.calcTightBounds(minHard, maxHard) centerX = (minHard[0] + maxHard[0]) / 2.0 centerY = (minHard[1] + maxHard[1]) / 2.0 heightX = (centerX - minHard[0]) / math.tan(deg2Rad(23)) heightY = (centerY - minHard[1]) / math.tan(deg2Rad(18)) height = max(heightX, heightY) self.camTopViewPos = Point3(centerX, centerY, height) self.camTopViewHpr = Point3(0, -90, 0) self.createRays() self.createToonRay() self.createCameraRay() def createLocatorDict(self): self.locDict = {} locatorNum = 1 curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) while not curNodePath.isEmpty(): self.locDict[locatorNum] = curNodePath locatorNum += 1 curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) def loadBlockers(self): loadAll = base.config.GetBool('golf-all-blockers', 0) self.createLocatorDict() self.blockerNums = self.holeInfo['blockers'] for locatorNum in self.locDict: if locatorNum in self.blockerNums or loadAll: locator = self.locDict[locatorNum] locatorParent = locator.getParent() locator.getChildren().wrtReparentTo(locatorParent) else: self.locDict[locatorNum].removeNode() self.hardSurfaceNodePath.flattenStrong() def loadSounds(self): self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.ogg') self.holeInOneSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hole_In_One.ogg') self.holeInTwoPlusSfx = loader.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.ogg') self.ballGoesInStartSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Goes_In_Start.ogg') self.ballGoesInLoopSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Goes_In_Loop.ogg') self.ballGoesToRestSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Rest_In_Cup.ogg') self.kickedOutSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Sad_Noise_Kicked_Off_Hole.ogg') self.crowdBuildupSfx = [] self.crowdApplauseSfx = [] self.crowdMissSfx = [] for i in range(4): self.crowdBuildupSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Buildup.ogg')) self.crowdApplauseSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Applause.ogg')) self.crowdMissSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Miss.ogg')) self.bumpHardSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Barrier_3.ogg') self.bumpMoverSfx = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_2.ogg') self.bumpWindmillSfx = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.ogg') def setup(self): self.notify.debug('setup golf hole') self.loadLevel() self.loadSounds() self.camMove = 0 self.arrowKeys = ArrowKeys.ArrowKeys() self.arrowKeys.setPressHandlers([None, None, self.__leftArrowPressed, self.__rightArrowPressed, self.__beginTossGolf]) self.arrowKeys.setReleaseHandlers([None, None, None, None, self.__endTossGolf]) self.targets = render.attachNewNode('targetGameTargets') self.ballFollow = render.attachNewNode('nodeAtBall') self.startingTeeHeading = self.teeNodePath.getH() self.ballFollow.setH(self.startingTeeHeading) self.ballFollowToonSpot = self.ballFollow.attachNewNode('toonAimSpot') self.ballFollowToonSpot.setX(-2.0) self.ballFollowToonSpot.setY(0) self.ballFollowToonSpot.setH(-90) self.clubLookatSpot = self.ballFollow.attachNewNode('clubLookat') self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) camera.reparentTo(self.ballFollow) self.camPosBallFollow = Point3(0.0, -23.0, 12.0) self.camHprBallFollow = Point3(0, -16.0, 0) camera.setPos(self.camPosBallFollow) camera.setHpr(self.camHprBallFollow) if self.holeBottomNodePath.isEmpty(): holePositions = self.holePositions for index in range(len(holePositions)): holePos = holePositions[index] targetNodePathGeom, t1, t2 = BuildGeometry.addCircleGeom(self.targets, 16, 1) targetNodePathGeom.setPos(holePos) targetNodePathGeom.setBin('ground', 0) targetNodePathGeom.setDepthWrite(False) targetNodePathGeom.setDepthTest(False) targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) targetNodePathGeom.setColorScale(0.0, 0.0, 0.0, 1.0) self.holeNodes.append(targetNodePathGeom) holeSphere = CollisionSphere(0, 0, 0, 1) holeSphere.setTangible(1) holeCNode = CollisionNode('Hole') holeCNode.addSolid(holeSphere) holeC = targetNodePathGeom.attachNewNode(holeCNode) holeC.show() holeC.setCollideMask(ToontownGlobals.PieBitmask) toon = base.localAvatar toon.setPos(0.0, 0.0, -100.0) toon.b_setAnimState('neutral', 1.0) self.pollingCtrl = 0 self.timeLastCtrl = 0.0 self.powerBar = DirectWaitBar(guiId='launch power bar', pos=(0.0, 0, -0.65), relief=DGG.SUNKEN, frameSize=(-2.0, 2.0, -0.2, 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) self.power = 0 self.powerBar['value'] = self.power self.powerBar.hide() self.accept('tab', self.tabKeyPressed) self.putAwayAllToons() base.transitions.irisOut(t=0) self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') self.dropShadowModel.setColor(0, 0, 0, 0.5) self.dropShadowModel.flattenMedium() self.dropShadowModel.hide() return def switchToAnimState(self, animStateName, forced = False): curAnimState = base.localAvatar.animFSM.getCurrentState() curAnimStateName = '' if curAnimState: curAnimStateName = curAnimState.getName() if curAnimStateName != animStateName or forced: base.localAvatar.b_setAnimState(animStateName) def __aimTask(self, task): self.attachClub(self.currentGolfer, True) x = -math.sin(self.ballFollow.getH() * 0.0174532925) y = math.cos(self.ballFollow.getH() * 0.0174532925) dt = globalClock.getDt() b = self.curGolfBall() forceMove = 500 forceMoveDt = forceMove * dt posUpdate = False momentumChange = dt * 60.0 if (self.arrowKeys.upPressed() or self.arrowKeys.downPressed()) and not self.golfCourse.canDrive(self.currentGolfer): posUpdate = True self.aimMomentum = 0.0 self.ballFollow.headsUp(self.holeBottomNodePath) elif self.arrowKeys.rightPressed() and not self.arrowKeys.leftPressed(): self.aimMomentum -= momentumChange if self.aimMomentum > 0: self.aimMomentum = 0.0 elif self.aimMomentum < -30.0: self.aimMomentum = -30.0 posUpdate = True self.switchToAnimState('GolfRotateLeft') self.scoreBoard.hide() elif self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed(): self.aimMomentum += momentumChange if self.aimMomentum < 0.0: self.aimMomentum = 0.0 elif self.aimMomentum > 30.0: self.aimMomentum = 30.0 posUpdate = True self.switchToAnimState('GolfRotateRight') self.scoreBoard.hide() else: self.aimMomentum = 0.0 self.switchToAnimState('GolfPuttLoop') self.ballFollow.setH(self.ballFollow.getH() + self.aimMomentum * dt) if self.arrowKeys.upPressed() and self.golfCourse.canDrive(self.currentGolfer): b.enable() b.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) if self.arrowKeys.downPressed() and self.golfCourse.canDrive(self.currentGolfer): b.enable() b.addForce(Vec3(-x * forceMoveDt, -y * forceMoveDt, 0)) if self.arrowKeys.leftPressed() and self.arrowKeys.rightPressed() and self.golfCourse.canDrive(self.currentGolfer): b.enable() b.addForce(Vec3(0, 0, 3000 * dt)) if posUpdate: if globalClock.getFrameTime() - self.lastTimeHeadingSent > 0.2: self.sendUpdate('setTempAimHeading', [localAvatar.doId, self.ballFollow.getH()]) self.lastTimeHeadingSent = globalClock.getFrameTime() self.lastTempHeadingSent = self.ballFollow.getH() elif self.lastTempHeadingSent != self.ballFollow.getH(): self.sendUpdate('setTempAimHeading', [localAvatar.doId, self.ballFollow.getH()]) self.lastTimeHeadingSent = globalClock.getFrameTime() self.lastTempHeadingSent = self.ballFollow.getH() self.setCamera2Ball() self.fixCurrentGolferFeet() self.adjustClub() self.orientCameraRay() return task.cont def fixCurrentGolferFeet(self): golfer = base.cr.doId2do.get(self.currentGolfer) if not golfer: return golferPos = golfer.getPos(render) newPos = Vec3(golferPos[0], golferPos[1], golferPos[2] + 5) self.toonRay.setPosition(newPos) def adjustClub(self): club = self.clubs[self.currentGolfer] if club: distance = club.getDistance(self.clubLookatSpot) scaleFactor = distance / 2.058 club.setScale(1, scaleFactor, 1) def resetPowerBar(self): self.power = 0 self.powerBar['value'] = self.power self.powerBar['text'] = '' def sendSwingInfo(self): kickHimOut = self.updateWarning() if kickHimOut: return curAimTime = globalClock.getRealTime() - self.enterAimStart if curAimTime < 0: curAimTime = 0 if curAimTime > GolfGlobals.AIM_DURATION: curAimTime = GolfGlobals.AIM_DURATION self.notify.debug('curAimTime = %f' % curAimTime) x = -math.sin(self.ballFollow.getH() * 0.0174532925) y = math.cos(self.ballFollow.getH() * 0.0174532925) b = self.curGolfBall() if hasattr(base, 'golfPower') and base.golfPower != None: self.power = float(base.golfPower) if not self.swingInfoSent: self.sendUpdate('postSwingState', [self.getCycleTime(), self.power, b.getPosition()[0], b.getPosition()[1], b.getPosition()[2], x, y, curAimTime, self.getCommonObjectData()]) self.swingInfoSent = True if self.power < 15 and self.golfCourse.scores[localAvatar.doId][self.golfCourse.curHoleIndex] == 0: self.powerReminder = DirectLabel(text=TTLocalizer.GolfPowerReminder, text_shadow=(0, 0, 0, 1), text_fg=VBase4(1, 1, 0.0, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.8), scale=0.12) return def updateWarning(self): retval = False if not self.localToonHitControl: self.localMissedSwings += 1 else: self.localMissedSwings = 0 if self.localMissedSwings == GolfGlobals.KICKOUT_SWINGS - 1: self.warningLabel = DirectLabel(parent=aspect2d, relief=None, pos=(0, 0, 0), text_align=TextNode.ACenter, text=TTLocalizer.GolfWarningMustSwing, text_scale=0.12, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.1, 0.1, 1), text_wordwrap=20) self.warningInterval = Sequence(LerpColorScaleInterval(self.warningLabel, 10, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn'), Func(self.warningLabel.destroy)) self.warningInterval.start() elif self.localMissedSwings >= GolfGlobals.KICKOUT_SWINGS: self.golfCourse.handleFallingAsleepGolf(None) retval = True return retval def assignRecordSwing(self, avId, cycleTime, power, x, y, z, dirX, dirY, commonObjectData): ball = self.ballDict[avId]['golfBall'] holdBallPos = ball.getPosition() self.useCommonObjectData(commonObjectData) self.trackRecordBodyFlight(ball, cycleTime, power, Vec3(x, y, z), dirX, dirY) ball.setPosition(holdBallPos) self.sendUpdate('ballMovie2AI', [cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame, commonObjectData]) self.ballMovie2Client(cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame, commonObjectData) def __watchAimTask(self, task): self.setCamera2Ball() self.attachClub(self.currentGolfer, True) self.adjustClub() self.fixCurrentGolferFeet() self.orientCameraRay() return task.cont def __watchTeeTask(self, task): self.setCamera2Ball() return task.cont def curGolfBall(self): return self.ballDict[self.currentGolfer]['golfBall'] def curGolfBallGeom(self): return self.ballDict[self.currentGolfer]['golfBallGeom'] def curBallShadow(self): return self.ballShadowDict[self.currentGolfer] def cleanupGeom(self): self.targets.removeNode() self.terrainModel.removeNode() self.powerBar.destroy() def cleanupPowerBar(self): self.powerBar.hide() def cleanupPhysics(self): pass def curBall(self): return self.ballDict[self.currentGolfer]['ball'] def curBallANP(self): return self.ballDict[self.currentGolfer]['ballActorNodePath'] def curBallActor(self): return self.ballDict[self.currentGolfer]['ballActor'] def enterAim(self): self.notify.debug('Aim') self.notify.debug('currentGolfer = %s' % self.currentGolfer) self.switchToAnimState('GolfPuttLoop', forced=True) self.swingInfoSent = False self.lastState = self.state self.aimMomentum = 0.0 self.enterAimStart = globalClock.getRealTime() taskMgr.add(self.__aimTask, 'Aim Task') self.showOnlyCurGolfer() strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) self.camPivot = self.ballFollow.attachNewNode('golf-camPivot') self.targetCamPivot = self.ballFollow.attachNewNode('golf-targetCamPivot') self.targetCamPivot.setP(self.DefaultCamP) self.curCamPivot = self.ballFollow.attachNewNode('golf-curCamPivot') self.curCamPivot.setP(self.DefaultCamP) self.ccTrav = CollisionTraverser('golf.ccTrav') self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) self.ccLineNode = CollisionNode('golf.ccLineNode') self.ccLineNode.addSolid(self.ccLine) self.ccLineNodePath = self.camPivot.attachNewNode(self.ccLineNode) self.ccLineBitMask = BitMask32(1048576) self.ccLineNode.setFromCollideMask(self.ccLineBitMask) self.ccLineNode.setIntoCollideMask(BitMask32.allOff()) self.camCollisionQueue = CollisionHandlerQueue() self.ccTrav.addCollider(self.ccLineNodePath, self.camCollisionQueue) if strokes: self.ballFollow.headsUp(self.holeBottomNodePath) self.camPivot.setP(self.DefaultCamP) self._golfBarrierCollection = self.terrainModel.findAllMatches('**/collision?') self._camAdjust = ScratchPad() self._camAdjust.iters = 0 self._camAdjust.lower = self.DefaultCamP self._camAdjust.upper = self.MaxCamP base.camera.setPos(self.camPosBallFollow) base.camera.setHpr(self.camHprBallFollow) self.camPivot.setP(self.DefaultCamP) base.camera.wrtReparentTo(self.camPivot) A = Point3(0, 0, 0) B = base.camera.getPos() AtoB = B - A AtoBnorm = Point3(AtoB) AtoBnorm.normalize() A += AtoBnorm * 0.4 self.ccLine.setPointA(A) self.ccLine.setPointB(B) self.camPivot.setP(self.DefaultCamP) self._camAdjust.task = taskMgr.add(self._adjustCamera, 'adjustCamera') self.resetPowerBar() self.powerBar.show() self.aimDuration = GolfGlobals.AIM_DURATION if not self.unlimitedAimTime: self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.aimDuration) self.timer.countdown(self.aimDuration, self.timerExpired) self.aimInstructions = DirectLabel(text=TTLocalizer.GolfAimInstructions, text_shadow=(0, 0, 0, 1), text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, -0.8), scale=TTLocalizer.DGHaimInstructions) self.skyContact = 1 self.localToonHitControl = False self._adjustCamera() return def exitAim(self): localAvatar.wrtReparentTo(render) taskMgr.remove(self._camAdjust.task) taskMgr.remove('Aim Task') taskMgr.remove(self.golfPowerTaskName) if self.timer: self.timer.stop() self.timer.destroy() self.timer = None self.powerBar.hide() self.ccLineNodePath.detachNode() self.targetCamPivot.detachNode() self.curCamPivot.detachNode() self.camPivot.detachNode() if self.aimInstructions: self.aimInstructions.destroy() self.aimInstructions = None return def timerExpired(self): taskMgr.remove(self.golfPowerTaskName) self.aimStart = None self.sendSwingInfo() self.resetPowerBar() return def _adjustCamera(self, task=None, first=True): if task is None and first: while 1: self._adjustCamera(first=False) if self._camAdjust.iters == 0: return Task.cont MaxIters = 5 finalP = self._camAdjust.lower localAvatar.stash() for barrier in self._golfBarrierCollection: barrier.stash() self.ccTrav.traverse(render) for barrier in self._golfBarrierCollection: barrier.unstash() localAvatar.unstash() midP = (self._camAdjust.lower + self._camAdjust.upper)/2 if self.camCollisionQueue.getNumEntries() > 0: self.camCollisionQueue.sortEntries() entry = self.camCollisionQueue.getEntry(0) sPoint = entry.getSurfacePoint(self.camPivot) self._camAdjust.lower = self.camPivot.getP() finalP = midP self.camPivot.setP(finalP) else: self._camAdjust.upper = self.camPivot.getP() finalP = self._camAdjust.upper self.camPivot.setP(midP) if abs(self._camAdjust.lower - self._camAdjust.upper) < 1.0: self._camAdjust.iters = MaxIters self._camAdjust.iters += 1 if self._camAdjust.iters >= MaxIters: self.targetCamPivot.setP(self._camAdjust.upper) if task is None: self.curCamPivot.setP(finalP) self._camAdjust.iters = 0 self._camAdjust.lower = self.DefaultCamP self._camAdjust.upper = self.MaxCamP self.camPivot.setP(self.DefaultCamP) if task is not None: self.curCamPivot.setP(self.curCamPivot, self.targetCamPivot.getP(self.curCamPivot)*min(1.0, 1.0*globalClock.getDt())) curP = self.curCamPivot.getP() self.curCamPivot.setP(self.DefaultCamP) base.camera.reparentTo(self.ballFollow) base.camera.setPos(self.camPosBallFollow) base.camera.setHpr(self.camHprBallFollow) base.camera.wrtReparentTo(self.curCamPivot) self.curCamPivot.setP(curP) base.camera.wrtReparentTo(self.ballFollow) return Task.cont def enterChooseTee(self): self.notify.debug('ChooseTee') self.curGolfBallGeom().show() self.curBallShadow().show() self.lastState = self.state taskMgr.add(self.__chooseTeeTask, 'ChooseTee Task') self.ballFollow.setH(self.startingTeeHeading) self.localAvatarChosenTee = False self.localTempTee = 0 if len(self.teePositions) > 1: self.localTempTee = 1 self.chooseTeeDuration = GolfGlobals.TEE_DURATION if not self.unlimitedTeeTime: self.teeTimer = ToontownTimer.ToontownTimer() self.teeTimer.posInTopRightCorner() self.teeTimer.setTime(self.chooseTeeDuration) self.teeTimer.countdown(self.chooseTeeDuration, self.teeTimerExpired) self.teeInstructions = DirectLabel(text=TTLocalizer.GolfChooseTeeInstructions, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, text_shadow=(0, 0, 0, 1), relief=None, pos=(0, 0, -0.75), scale=TTLocalizer.DGHteeInstructions) self.powerBar.hide() return def exitChooseTee(self): localAvatar.wrtReparentTo(render) if hasattr(self, 'teeInstructions') and self.teeInstructions: self.teeInstructions.destroy() self.teeInstructions = None taskMgr.remove('ChooseTee Task') taskMgr.remove(self.golfPowerTaskName) if self.teeTimer: self.teeTimer.stop() self.teeTimer.destroy() self.teeTimer = None self.powerBar.show() return def sendTeeInfo(self): self.sendUpdate('setAvatarTee', [self.localTempTee]) self.localAvatarChosenTee = True def __chooseTeeTask(self, task): if self.localAvatarChosenTee: return task.done if self.arrowKeys.jumpPressed(): if self.flyOverInterval and self.flyOverInterval.isPlaying(): pass else: self.sendTeeInfo() return task.cont def changeTee(self, newTee): ball = self.curGolfBall() ball.setPosition(self.teePositions[newTee]) self.setCamera2Ball() self.fixCurrentGolferFeet() self.adjustClub() def changeLocalTee(self, newTee): self.changeTee(newTee) self.sendUpdate('setAvatarTempTee', [localAvatar.doId, newTee]) self.fixCurrentGolferFeet() self.adjustClub() def __leftArrowPressed(self): if self.state != 'ChooseTee': return self.localTempTee -= 1 if self.localTempTee < 0: self.localTempTee = len(self.teePositions) - 1 self.changeLocalTee(self.localTempTee) def __rightArrowPressed(self): if self.state != 'ChooseTee': return self.localTempTee += 1 self.localTempTee %= len(self.teePositions) self.changeLocalTee(self.localTempTee) def teeTimerExpired(self): self.sendTeeInfo() def enterWatchAim(self): self.notify.debug('Watch Aim') self.notify.debugStateCall(self) self.notify.debug('currentGolfer = %s' % self.currentGolfer) strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) if strokes: self.ballFollow.lookAt(self.holeBottomNodePath) self.ballFollow.setP(0) self.showOnlyCurGolfer() taskMgr.add(self.__watchAimTask, 'Watch Aim Task') def exitWatchAim(self): self.notify.debugStateCall(self) av = base.cr.doId2do.get(self.currentGolfer) if av: heading = av.getH(render) toonPos = av.getPos(render) av.reparentTo(render) av.setH(heading) av.setPos(toonPos) self.notify.debug('av %s now at position %s' % (av.getName(), av.getPos())) else: self.notify.debug('could not get avId %d' % self.currentGolfer) taskMgr.remove('Watch Aim Task') def enterWatchTee(self): self.notify.debug('Watch Tee') self.notify.debugStateCall(self) self.curGolfBallGeom().show() self.ballFollow.setH(self.startingTeeHeading) self.ballShadowDict[self.currentGolfer].show() def exitWatchTee(self): self.notify.debugStateCall(self) av = base.cr.doId2do.get(self.currentGolfer) taskMgr.remove('Watch Tee Task') def enterWait(self): self.notify.debug('Wait') self.notify.debugStateCall(self) def exitWait(self): self.notify.debugStateCall(self) def removePlayBackDelayDelete(self): if self.playBackDelayDelete: self.playBackDelayDelete.destroy() self.playBackDelayDelete = None return def enterPlayback(self): def shiftClubToRightHand(): club = self.clubs[self.currentGolfer] av = base.cr.doId2do.get(self.currentGolfer) if av and club: club.wrtReparentTo(av.getRightHands()[0]) av = base.cr.doId2do.get(self.currentGolfer) if not av: return else: self.removePlayBackDelayDelete() self.playBackDelayDelete = DelayDelete.DelayDelete(av, 'GolfHole.enterPlayback') self.accept('clientCleanup', self._handleClientCleanup) self.inPlayBack = 1 self.setLookingAtPutt(False) self.swingInterval = Sequence(ActorInterval(av, 'swing-putt', startFrame=0, endFrame=GolfGlobals.BALL_CONTACT_FRAME), Func(self.startBallPlayback), ActorInterval(av, 'swing-putt', startFrame=GolfGlobals.BALL_CONTACT_FRAME, endFrame=23), Func(shiftClubToRightHand), Func(self.setLookingAtPutt, True), Func(self.removePlayBackDelayDelete)) adjustedBallTouchedHoleTime = self.ballTouchedHoleTime + GolfGlobals.BALL_CONTACT_TIME adjustedBallFirstTouchedHoleTime = self.ballFirstTouchedHoleTime + GolfGlobals.BALL_CONTACT_TIME adjustedBallDropTime = self.ballDropTime + GolfGlobals.BALL_CONTACT_TIME adjustedPlaybackEndTime = self.playbackMovieDuration + GolfGlobals.BALL_CONTACT_TIME self.notify.debug('adjustedTimes ballTouched=%.2f ballFirstTouched=%.2f ballDrop=%.2f playbaybackEnd=%.2f' % (adjustedBallTouchedHoleTime, adjustedBallFirstTouchedHoleTime, adjustedBallDropTime, adjustedPlaybackEndTime)) if self.ballWillGoInHole: curDuration = self.swingInterval.getDuration() lookPuttInterval = ActorInterval(av, 'look-putt') if curDuration < adjustedBallDropTime: self.swingInterval.append(lookPuttInterval) curDuration = self.swingInterval.getDuration() diffTime = adjustedBallDropTime - curDuration if diffTime > 0: self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=diffTime)) self.swingInterval.append(ActorInterval(av, 'good-putt', endTime=self.playbackMovieDuration, loop=1)) elif self.ballTouchedHoleTime: self.notify.debug('doing self.ballTouchedHoleTime') curDuration = self.swingInterval.getDuration() lookPuttInterval = ActorInterval(av, 'look-putt') if curDuration < adjustedBallTouchedHoleTime: self.swingInterval.append(lookPuttInterval) curDuration = self.swingInterval.getDuration() diffTime = adjustedBallTouchedHoleTime - curDuration if diffTime > 0: self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=diffTime)) self.swingInterval.append(ActorInterval(av, 'bad-putt', endFrame=32)) self.swingInterval.append(ActorInterval(av, 'badloop-putt', endTime=self.playbackMovieDuration, loop=1)) else: self.swingInterval.append(ActorInterval(av, 'look-putt')) self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=self.playbackMovieDuration, loop=1)) sfxInterval = Parallel() ballHitInterval = Sequence(Wait(GolfGlobals.BALL_CONTACT_TIME), SoundInterval(self.hitBallSfx)) sfxInterval.append(ballHitInterval) if self.ballWillGoInHole: ballRattle = Sequence() timeToPlayBallRest = adjustedPlaybackEndTime - self.ballGoesToRestSfx.length() if adjustedBallFirstTouchedHoleTime < timeToPlayBallRest: diffTime = timeToPlayBallRest - adjustedBallFirstTouchedHoleTime if self.ballGoesInStartSfx.length() < diffTime: ballRattle.append(Wait(adjustedBallFirstTouchedHoleTime)) ballRattle.append(SoundInterval(self.ballGoesInStartSfx)) timeToPlayLoop = adjustedBallFirstTouchedHoleTime + self.ballGoesInStartSfx.length() loopTime = timeToPlayBallRest - timeToPlayLoop if self.ballGoesInLoopSfx.length() == 0.0: numLoops = 0 else: numLoops = int(loopTime / self.ballGoesInLoopSfx.length()) self.notify.debug('numLoops=%d loopTime=%f' % (numLoops, loopTime)) if loopTime > 0: ballRattle.append(SoundInterval(self.ballGoesInLoopSfx, loop=1, duration=loopTime, seamlessLoop=True)) ballRattle.append(SoundInterval(self.ballGoesToRestSfx)) self.notify.debug('playing full rattling') else: self.notify.debug('playing abbreviated rattling') timeToPlayBallGoesIn = adjustedBallFirstTouchedHoleTime ballRattle.append(Wait(timeToPlayBallGoesIn)) startTime = self.ballGoesInStartSfx.length() - diffTime self.notify.debug('adjustedBallDropTime=%s diffTime=%s starTime=%s' % (adjustedBallDropTime, diffTime, startTime)) ballRattle.append(SoundInterval(self.ballGoesInStartSfx, startTime=startTime)) ballRattle.append(SoundInterval(self.ballGoesToRestSfx)) else: self.notify.debug('playing abbreviated ball goes to rest') ballRattle.append(Wait(adjustedBallFirstTouchedHoleTime)) diffTime = adjustedPlaybackEndTime - adjustedBallFirstTouchedHoleTime startTime = self.ballGoesToRestSfx.length() - diffTime self.notify.debug('adjustedBallDropTime=%s diffTime=%s starTime=%s' % (adjustedBallDropTime, diffTime, startTime)) ballRattle.append(SoundInterval(self.ballGoesToRestSfx, startTime=startTime)) sfxInterval.append(ballRattle) crowdBuildupSfx = self.crowdBuildupSfx[self.avIdList.index(self.currentGolfer)] crowdApplauseSfx = self.crowdApplauseSfx[self.avIdList.index(self.currentGolfer)] crowdMissSfx = self.crowdMissSfx[self.avIdList.index(self.currentGolfer)] if self.ballWillGoInHole: crowdIval = Sequence() buildupLength = crowdBuildupSfx.length() self.notify.debug('buildupLength=%s' % buildupLength) diffTime = adjustedBallFirstTouchedHoleTime - buildupLength if diffTime > 0: crowdIval.append(Wait(diffTime)) crowdIval.append(SoundInterval(crowdBuildupSfx)) crowdIval.append(SoundInterval(crowdApplauseSfx)) else: startTime = buildupLength - adjustedBallFirstTouchedHoleTime self.notify.debug('playing abbreviated crowd build and applause diffTime=%s startTime=%s' % (diffTime, startTime)) crowdIval.append(SoundInterval(crowdBuildupSfx, startTime=startTime)) crowdIval.append(SoundInterval(crowdApplauseSfx)) sfxInterval.append(crowdIval) elif self.ballFirstTouchedHoleTime: crowdIval = Sequence() buildupLength = crowdBuildupSfx.length() self.notify.debug('touched but not going in buildupLength=%s' % buildupLength) diffTime = adjustedBallFirstTouchedHoleTime - buildupLength if diffTime > 0: self.notify.debug('waiting %.2f to play crowd buildup' % diffTime) crowdIval.append(Wait(diffTime)) crowdIval.append(SoundInterval(crowdBuildupSfx)) crowdIval.append(SoundInterval(crowdMissSfx)) else: startTime = buildupLength - adjustedBallFirstTouchedHoleTime self.notify.debug('playing abbreviated crowd build and miss diffTime=%s startTime=%s' % (diffTime, startTime)) crowdIval.append(SoundInterval(crowdBuildupSfx, startTime=startTime)) crowdIval.append(SoundInterval(crowdMissSfx)) sfxInterval.append(crowdIval) if self.sfxInterval: sfxInterval.finish() self.sfxInterval = sfxInterval self.sfxInterval.start() self.swingInterval.start() def exitPlayback(self): self.notify.debug('Exiting Playback') if self.swingInterval: self.swingInterval.pause() av = base.cr.doId2do.get(self.currentGolfer) if av: if self.ballWillGoInHole: av.loop('good-putt', restart=0) elif self.ballTouchedHoleTime: pass else: av.loop('neutral') self.setLookingAtPutt(False) if av == base.localAvatar: if self.ballWillGoInHole: av.b_setAnimState('GolfGoodPutt') elif self.ballTouchedHoleTime: av.b_setAnimState('GolfBadPutt') else: av.b_setAnimState('neutral') taskMgr.remove('playback task') self.curGolfBall().disable() self.readyCurrentGolfer(None) self.inPlayBack = 0 if self.powerReminder: self.powerReminder.destroy() self.powerReminder = None return def setLookingAtPutt(self, newVal): self.isLookingAtPutt = newVal def getLookingAtPutt(self): return self.isLookingAtPutt def startBallPlayback(self): self.playbackFrameNum = 0 self.sourceFrame = self.recording[0] self.destFrameNum = 1 self.destFrame = self.recording[self.destFrameNum] self.aVSourceFrame = self.aVRecording[0] self.aVDestFrameNum = 1 self.aVDestFrame = self.aVRecording[self.aVDestFrameNum] self.inPlayBack = 2 def isCurBallInHole(self): retval = False ball = self.curGolfBall() ballPos = ball.getPosition() for holePos in self.holePositions: displacement = ballPos - holePos length = displacement.length() self.notify.debug('hole %s length=%s' % (holePos, length)) if length <= GolfGlobals.DistanceToBeInHole: retval = True break return retval def handleBallGoingInHole(self): par = GolfGlobals.HoleInfo[self.holeId]['par'] unlimitedSwing = False av = base.cr.doId2do.get(self.currentGolfer) if av: unlimitedSwing = av.getUnlimitedSwing() if not unlimitedSwing: self.curGolfBall().setPosition(0, 0, -100) self.ballShadowDict[self.currentGolfer].setPos(0, 0, -100) self.ballShadowDict[self.currentGolfer].hide() strokes = 3 if self.golfCourse: strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) else: self.notify.warning('self.golfCourse is None') diff = strokes - par if diff > 0: textStr = '+' + str(diff) else: textStr = diff if strokes == 1: textStr = TTLocalizer.GolfHoleInOne elif diff in TTLocalizer.GolfShotDesc: if self.ballWillGoInHole: textStr = TTLocalizer.GolfShotDesc[diff] perfectTextSubnode = hidden.attachNewNode(self.__genText(textStr)) perfectText = hidden.attachNewNode('perfectText') perfectTextSubnode.reparentTo(perfectText) frame = self.__textGen.getCardActual() offsetY = -abs(frame[2] + frame[3]) / 2.0 - 1.35 perfectTextSubnode.setPos(0, 0, offsetY) perfectText.setColor(1, 0.1, 0.1, 1) def fadeFunc(t, text = perfectText): text.setColorScale(1, 1, 1, t) def destroyText(text = perfectText): text.removeNode() animTrack = Sequence() av = base.cr.doId2do.get(self.currentGolfer) animTrack.append(Func(self.golfCourse.updateScoreBoard)) textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(self.sendUpdate, 'turnDone', [])) soundTrack = Sequence() if strokes == 1: soundTrack.append(SoundInterval(self.holeInOneSfx)) elif self.hasCurGolferReachedMaxSwing and not self.ballWillGoInHole: soundTrack.append(SoundInterval(self.kickedOutSfx)) self.perfectIval = Parallel(textTrack, soundTrack, animTrack) self.perfectIval.start() def __playbackTask(self, task): return self.playBackFrame(task) def toonRayCollisionCallback(self, x, y, z): if self.state not in ('Aim', 'WatchAim', 'ChooseTee', 'WatchTee'): return tempPath = render.attachNewNode('temp') tempPath.setPos(x, y, z) relPos = tempPath.getPos(self.ballFollowToonSpot) av = base.cr.doId2do.get(self.currentGolfer) if av: zToUse = relPos[2] if zToUse < 0 - GolfGlobals.GOLF_BALL_RADIUS: zToUse = 0 - GolfGlobals.GOLF_BALL_RADIUS av.setPos(0, 0, zToUse) tempPath.removeNode() def preStep(self): if self.currentGolferActive: GolfHoleBase.GolfHoleBase.preStep(self) def postStep(self): if self.currentGolferActive: GolfHoleBase.GolfHoleBase.postStep(self) DistributedPhysicsWorld.DistributedPhysicsWorld.postStep(self) if self.inPlayBack == 2: self.playBackFrame() self.makeCurGolferLookAtBall() elif self.state == 'Playback' and self.inPlayBack == 0: self.request('Wait') self.updateTranslucentObjects() def updateTranslucentObjects(self): for translucentNodePathLastFrame in self.translucentLastFrame: if translucentNodePathLastFrame not in self.translucentCurFrame: translucentNodePathLastFrame.setColorScale(1, 1, 1, 1) for transNpCurFrame in self.translucentCurFrame: if transNpCurFrame not in self.translucentLastFrame: self.notify.debug('making translucent %s' % transNpCurFrame) transNpCurFrame.setColorScale(1, 1, 1, 0.25) transNpCurFrame.setTransparency(1) def makeCurGolferLookAtBall(self): if self.getLookingAtPutt(): av = base.cr.doId2do.get(self.currentGolfer) if av: ballPos = self.curGolfBall().getPosition() av.headsUp(ballPos[0], ballPos[1], ballPos[2]) av.setH(av.getH() - 90) def playBackFrame(self): doPrint = 0 doAVPrint = 0 lastFrame = self.recording[len(self.recording) - 1][0] if self.playbackFrameNum >= self.destFrame[0]: self.sourceFrame = self.destFrame self.destFrameNum += 1 doPrint = 1 if self.destFrameNum < len(self.recording): self.destFrame = self.recording[self.destFrameNum] else: self.notify.debug('recording length %s' % len(self.recording)) if self.isCurBallInHole() or self.hasCurGolferReachedMaxSwing(): self.handleBallGoingInHole() self.request('Wait') else: self.golfCourse.updateScoreBoard() self.request('Wait') self.sendUpdate('turnDone', []) return self.projLength = self.destFrame[0] - self.sourceFrame[0] self.projPen = self.destFrame[0] - self.playbackFrameNum propSource = float(self.projPen) / float(self.projLength) propDest = 1.0 - propSource projX = self.sourceFrame[1] * propSource + self.destFrame[1] * propDest projY = self.sourceFrame[2] * propSource + self.destFrame[2] * propDest projZ = self.sourceFrame[3] * propSource + self.destFrame[3] * propDest newPos = Vec3(projX, projY, projZ) ball = self.curGolfBall() ball.setPosition(newPos) if self.playbackFrameNum >= self.aVDestFrame[0]: self.aVSourceFrame = self.aVDestFrame self.aVDestFrameNum += 1 doAVPrint = 1 if self.aVDestFrameNum < len(self.aVRecording): self.aVDestFrame = self.aVRecording[self.aVDestFrameNum] newAV = Vec3(self.aVSourceFrame[1], self.aVSourceFrame[2], self.aVSourceFrame[3]) self.projLength = self.aVDestFrame[0] - self.aVSourceFrame[0] self.projPen = self.aVDestFrame[0] - self.playbackFrameNum propSource = float(self.projPen) / float(self.projLength) propDest = 1.0 - propSource projX = self.aVSourceFrame[1] * propSource + self.aVDestFrame[1] * propDest projY = self.aVSourceFrame[2] * propSource + self.aVDestFrame[2] * propDest projZ = self.aVSourceFrame[3] * propSource + self.aVDestFrame[3] * propDest newAV = Vec3(projX, projY, projZ) ball = self.curGolfBall() ball.setAngularVel(newAV) if self.playbackFrameNum < lastFrame - 1: ball.enable() else: ball.disable() self.setCamera2Ball() self.placeBodies() if doAVPrint: pass if doPrint: self.notify.debug('. %s %s %s %s %s' % (self.playbackFrameNum, self.sourceFrame[0], self.destFrame[0], self.destFrameNum, newPos)) self.playbackFrameNum += 1 def enterCleanup(self): taskMgr.remove('update task') if hasattr(self, 'arrowKeys'): self.arrowKeys.destroy() self.arrowKeys = None self.ignoreAll() if self.swingInterval: self.swingInterval.pause() self.swingInterval = None if self.sfxInterval: self.sfxInterval.pause() self.sfxInterval = None self.cleanupGeom() return def exitCleanup(self): pass def setCamera2Ball(self): b = self.curGolfBall() ballPos = Point3(b.getPosition()[0], b.getPosition()[1], b.getPosition()[2]) self.ballFollow.setPos(ballPos) def hitBall(self, ball, power, x, y): self.performSwing(self, ball, power, x, y) def ballMovie2Client(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData): self.notify.debug('received Movie, number of frames %s %s ballInFrame=%d ballTouchedHoleFrame=%d ballFirstTouchedHoleFrame=%d' % (len(movie), len(spinMovie), ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame)) if self.state == 'Playback': self.notify.debug('SMASHED PLAYBACK') return self.ballShadowDict[avId].show() self.holdCycleTime = cycleTime self.holdCommonObjectData = commonObjectData self.useCommonObjectData(self.holdCommonObjectData) self.recording = movie self.aVRecording = spinMovie endingBallPos = Vec3(movie[-1][1], movie[-1][2], movie[-1][3]) endingFrame = movie[-1][0] self.playbackMovieDuration = endingFrame * self.DTAStep self.notify.debug('playback movie duration=%s' % self.playbackMovieDuration) displacement = self.holePositions[0] - endingBallPos self.ballWillGoInHole = False if displacement.length() <= GolfGlobals.DistanceToBeInHole: self.ballWillGoInHole = True self.notify.debug('endingBallPos=%s, distanceToHole=%s, ballWillGoInHole=%s' % (endingBallPos, displacement.length(), self.ballWillGoInHole)) self.ballDropTime = ballInFrame * self.DTAStep self.ballTouchedHoleTime = ballTouchedHoleFrame * self.DTAStep self.ballFirstTouchedHoleTime = ballFirstTouchedHoleFrame * self.DTAStep if self.state == 'WatchTee': self.request('WatchAim') self.request('Playback') def golfersTurn(self, avId): self.readyCurrentGolfer(avId) if avId == localAvatar.doId: self.setCamera2Ball() self.request('Aim') else: self.setCamera2Ball() self.request('WatchAim') def readyCurrentGolfer(self, avId): for index in self.ballDict: self.ballDict[index]['golfBallOdeGeom'].setCollideBits(BitMask32(0)) self.ballDict[index]['golfBallOdeGeom'].setCategoryBits(BitMask32(0)) self.ballDict[index]['golfBall'].disable() if avId: self.currentGolfer = avId self.currentGolferActive = True if avId in self.ballDict: self.ballDict[avId]['golfBallOdeGeom'].setCollideBits(BitMask32(16777215)) self.ballDict[avId]['golfBallOdeGeom'].setCategoryBits(BitMask32(4278190080)) else: self.currentGolferActive = False def setGolferIds(self, avIds): self.avIdList = avIds self.numPlayers = len(self.avIdList) self.teeChosen = {} for avId in self.avIdList: self.teeChosen[avId] = -1 def setHoleId(self, holeId): self.holeId = holeId self.holeInfo = GolfGlobals.HoleInfo[holeId] def createBall(self, avId, index = None): golfBallGeom, golfBall, odeGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, index) startPos = self.teePositions[0] if len(self.teePositions) > 1: startPos = self.teePositions[1] golfBall.setPosition(startPos) golfBallGeom.hide() if self.notify.getDebug(): self.notify.debug('golf ball body id') golfBall.write() self.notify.debug(' -') golfBallGeom.setName('golfBallGeom%s' % avId) self.ballDict[avId] = {'golfBall': golfBall, 'golfBallGeom': golfBallGeom, 'golfBallOdeGeom': odeGeom} golfBall.disable() shadow = self.dropShadowModel.copyTo(render) shadow.setBin('shadow', 100) shadow.setScale(0.09) shadow.setDepthWrite(False) shadow.setDepthTest(True) self.ballShadowDict[avId] = shadow shadow.hide() def setGolfCourseDoId(self, golfCourseDoId): self.golfCourseDoId = golfCourseDoId self.golfCourse = base.cr.doId2do.get(self.golfCourseDoId) if not self.golfCourse: self.cr.relatedObjectMgr.abortRequest(self.golfCourseRequest) self.golfCourseRequest = self.cr.relatedObjectMgr.requestObjects([self.golfCourseDoId], eachCallback=self.__gotGolfCourse) else: self.scoreBoard = self.golfCourse.scoreBoard self.scoreBoard.hide() def __gotGolfCourse(self, golfCourse): self.golfCourseRequest = None self.golfCourse = golfCourse return def __genText(self, text): self.__textGen.setText(text) return self.__textGen.generate() def sendBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2): self.swingBox.setPosition(pos0, pos1, pos2) self.swingBox.setQuaternion(Quat(quat0, quat1, quat2, quat3)) self.swingBox.setAngularVel(anV0, anV1, anV2) self.swingBox.setLinearVel(lnV0, lnV1, lnV2) def hasCurGolferReachedMaxSwing(self): strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) maxSwing = self.holeInfo['maxSwing'] retval = strokes >= maxSwing if retval: pass return retval def __getGolfPower(self, time): elapsed = max(time - self.aimStart, 0.0) t = elapsed / self.golfPowerSpeed t = math.pow(t, self.golfPowerExponent) power = int(t * 100) % 200 if power > 100: power = 200 - power return power def __beginTossGolf(self): if self.aimStart != None: return if not self.state == 'Aim': return if self.swingInfoSent: return self.localToonHitControl = True time = globalClock.getFrameTime() self.aimStart = time messenger.send('wakeup') self.scoreBoard.hide() taskMgr.add(self.__updateGolfPower, self.golfPowerTaskName) return def __endTossGolf(self): if self.aimStart == None: return if not self.state == 'Aim': return messenger.send('wakeup') taskMgr.remove(self.golfPowerTaskName) self.aimStart = None self.sendSwingInfo() self.resetPowerBar() return def __updateGolfPower(self, task): if not self.powerBar: print('### no power bar!!!') return Task.done newPower = self.__getGolfPower(globalClock.getFrameTime()) self.power = newPower self.powerBar['value'] = newPower self.powerBar['text'] = TTLocalizer.GolfPowerBarText % {'power': newPower} return Task.cont def golferChooseTee(self, avId): self.readyCurrentGolfer(avId) self.putAwayAllToons() if self.needToDoFlyOver and self.doFlyOverMovie(avId): pass else: if avId == localAvatar.doId: self.setCamera2Ball() if not self.state == 'ChooseTee': self.request('ChooseTee') else: self.setCamera2Ball() self.request('WatchTee') self.takeOutToon(self.currentGolfer) def setAvatarTempTee(self, avId, tempTee): if self.state != 'WatchTee': return if avId != self.currentGolfer: self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) return self.changeTee(tempTee) def setAvatarFinalTee(self, avId, finalTee): if avId != self.currentGolfer: self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) return self.changeTee(finalTee) def setTempAimHeading(self, avId, heading): if avId != self.currentGolfer: self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) return if self.state != 'WatchAim': return if avId != localAvatar.doId: self.ballFollow.setH(heading) def stickToonToBall(self, avId): av = base.cr.doId2do.get(avId) if av: av.reparentTo(self.ballFollowToonSpot) av.setPos(0, 0, 0) av.setH(0) def putAwayToon(self, avId): av = base.cr.doId2do.get(avId) if av: av.reparentTo(render) av.setPos(0, 0, -1000) av.setH(0) def putAwayAllToons(self): for avId in self.avIdList: self.putAwayToon(avId) def takeOutToon(self, avId): self.stickToonToBall(avId) self.fixCurrentGolferFeet() self.attachClub(avId) def showOnlyCurGolfer(self): self.notify.debug('curGolfer = %s' % self.currentGolfer) self.stickToonToBall(self.currentGolfer) self.fixCurrentGolferFeet() self.attachClub(self.currentGolfer) for avId in self.avIdList: if avId != self.currentGolfer: self.putAwayToon(avId) def tabKeyPressed(self): doInterval = True self.notify.debug('tab key pressed') if not hasattr(self, 'ballFollow'): return if self.flyOverInterval and self.flyOverInterval.isPlaying(): return if self.camInterval and self.camInterval.isPlaying(): self.camInterval.pause() if base.camera.getParent() == self.ballFollow: if doInterval: curHpr = camera.getHpr(render) angle = PythonUtil.closestDestAngle2(curHpr[0], 0) self.camInterval = Sequence(Func(base.camera.wrtReparentTo, render), LerpPosHprInterval(base.camera, 2, self.camTopViewPos, self.camTopViewHpr)) self.camInterval.start() else: base.camera.reparentTo(render) base.camera.setPos(self.camTopViewPos) base.camera.setHpr(self.camTopViewHpr) elif doInterval: curHpr = camera.getHpr(self.ballFollow) angle = PythonUtil.closestDestAngle2(curHpr[0], 0) self.camInterval = Sequence(Func(base.camera.wrtReparentTo, self.ballFollow), LerpPosHprInterval(base.camera, 2, self.camPosBallFollow, self.camHprBallFollow)) self.camInterval.start() else: base.camera.reparentTo(self.ballFollow) base.camera.setPos(self.camPosBallFollow) base.camera.setHpr(self.camHprBallFollow) def doFlyOverMovie(self, avId): title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' :\n ' + GolfGlobals.getHoleName(self.holeId) + '\n' + TTLocalizer.GolfPar + ' : ' + '%s' % self.holeInfo['par'] self.titleLabel = DirectLabel(parent=aspect2d, relief=None, pos=(0, 0, 0.8), text_align=TextNode.ACenter, text=title, text_scale=0.12, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.8, 0.4, 1)) self.titleLabel.setBin('opaque', 19) self.titleLabel.hide() self.needToDoFlyOver = False bamFile = self.holeInfo['terrainModel'] fileName = bamFile.split('/')[-1] dotIndex = fileName.find('.') baseName = fileName[0:dotIndex] camModelName = baseName + '_cammodel.bam' cameraName = baseName + '_camera.bam' path = bamFile[0:bamFile.find(fileName)] camModelFullPath = path + camModelName cameraAnimFullPath = path + cameraName try: self.flyOverActor = Actor.Actor(camModelFullPath, {'camera': cameraAnimFullPath}) except Exception: self.notify.debug("Couldn't find flyover %s" % camModelFullPath) return False base.transitions.noIris() self.flyOverActor.reparentTo(render) self.flyOverActor.setBlend(frameBlend=True) flyOverJoint = self.flyOverActor.find('**/camera1') children = flyOverJoint.getChildren() numChild = children.getNumPaths() for i in range(numChild): childNodePath = children.getPath(i) childNodePath.removeNode() self.flyOverJoint = flyOverJoint self.flyOverInterval = Sequence(Func(base.camera.reparentTo, flyOverJoint), Func(base.camera.clearTransform), Func(self.titleLabel.show), ActorInterval(self.flyOverActor, 'camera'), Func(base.camera.reparentTo, self.ballFollow), Func(base.camera.setPos, self.camPosBallFollow), Func(base.camera.setHpr, self.camHprBallFollow)) if avId == localAvatar.doId: self.flyOverInterval.append(Func(self.setCamera2Ball)) self.flyOverInterval.append(Func(self.safeRequestToState, 'ChooseTee')) else: self.flyOverInterval.append(Func(self.setCamera2Ball)) self.flyOverInterval.append(Func(self.safeRequestToState, 'WatchTee')) self.flyOverInterval.append(Func(self.titleLabel.hide)) self.flyOverInterval.append(Func(self.takeOutToon, avId)) self.flyOverInterval.start() return True def avExited(self, avId): if self.state == 'Playback' and self.currentGolfer == avId: pass else: self.ballDict[avId]['golfBallGeom'].hide() def orientCameraRay(self): pos = base.camera.getPos(self.terrainModel) self.cameraRayNodePath.setPos(pos) self.cameraRayNodePath.lookAt(self.ballFollow) renderPos = self.cameraRayNodePath.getPos(render) if renderPos != pos: self.notify.debug('orientCamerRay this should not happen') ballPos = self.ballFollow.getPos(self.terrainModel) dirCam = Vec3(ballPos - pos) dirCam.normalize() self.cameraRay.set(pos, dirCam) def performSwing(self, ball, power, dirX, dirY): startTime = globalClock.getRealTime() avId = base.localAvatar.doId position = ball.getPosition() x = position[0] y = position[1] z = position[2] if avId not in self.golfCourse.drivingToons: x = position[0] y = position[1] z = position[2] self.swingTime = cycleTime lift = 0 ball = self.ball forceMove = 2500 if power > 50: lift = 0 ball.enable() ball.setPosition(x, y, z) ball.setLinearVel(0.0, 0.0, 0.0) ball.setAngularVel(0.0, 0.0, 0.0) ball.addForce(Vec3(dirX * forceMove * power / 100.0, dirY * forceMove * power / 100.0, lift)) self.initRecord() safety = 0 self.llv = None self.record(ball) while ball.isEnabled() and len(self.recording) < 2000: self.preStep() self.simulate() self.postStep() self.record(ball) safety += 1 self.record(ball) midTime = globalClock.getRealTime() self.processRecording() self.processAVRecording() self.notify.debug('Recording End time %s cycle %s len %s avLen %s' % (self.timingSimTime, self.getSimCycleTime(), len(self.recording), len(self.aVRecording))) self.request('WaitPlayback') length = len(self.recording) - 1 x = self.recording[length][1] y = self.recording[length][2] z = self.recording[length][3] self.ballPos[avId] = Vec3(x, y, z) endTime = globalClock.getRealTime() diffTime = endTime - startTime fpsTime = self.frame / diffTime self.notify.debug('Time Start %s Mid %s End %s Diff %s Fps %s frames %s' % (startTime, midTime, endTime, diffTime, fpsTime, self.frame)) self.ballMovie2Client(cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame) return def handleBallHitNonGrass(self, c0, c1): if not self.inPlayBack: return golfBallPos = self.curGolfBall().getPosition() if self.lastBumpSfxPos == golfBallPos: return if GolfGlobals.HARD_COLLIDE_ID in [c0, c1]: if not self.bumpHardSfx.status() == self.bumpHardSfx.PLAYING: distance = (golfBallPos - self.lastBumpSfxPos).length() if distance > 2.0: base.playSfx(self.bumpHardSfx) self.lastBumpSfxPos = golfBallPos elif GolfGlobals.MOVER_COLLIDE_ID in [c0, c1]: if not self.bumpMoverSfx.status() == self.bumpMoverSfx.PLAYING: base.playSfx(self.bumpMoverSfx) self.lastBumpSfxPos = golfBallPos elif GolfGlobals.WINDMILL_BASE_COLLIDE_ID in [c0, c1]: if not self.bumpWindmillSfx.status() == self.bumpWindmillSfx.PLAYING: base.playSfx(self.bumpWindmillSfx) self.lastBumpSfxPos = golfBallPos def safeRequestToState(self, newState): doingRequest = False if self.state in self.defaultTransitions: if newState in self.defaultTransitions[self.state]: self.request(newState) doingRequest = True if not doingRequest: self.notify.warning('ignoring transition from %s to %s' % (self.state, newState)) def doMagicWordHeading(self, heading): if self.state == 'Aim': self.aimMomentum = 0.0 self.ballFollow.setH(float(heading)) def _handleClientCleanup(self): self.removePlayBackDelayDelete() self.ignore('clientCleanup')