def setupFloorEventSphere(self, avatarNodePath, bitmask, avatarRadius): cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.75) cSphereNode = CollisionNode('Flyer.cFloorEventSphere') cSphereNode.addSolid(cSphere) cSphereNodePath = avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.floorCollisionEvent = CollisionHandlerEvent() self.floorCollisionEvent.addInPattern('%fn-enter-%in') self.floorCollisionEvent.addAgainPattern('%fn-again-%in') self.floorCollisionEvent.addOutPattern('%fn-exit-%in') base.cTrav.addCollider(cSphereNodePath, self.floorCollisionEvent) self.cFloorEventSphereNodePath = cSphereNodePath
def __initCollisions(self): collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius) collSphere.setTangible(0) self.mazeCollisionName = Globals.LocalPlayerCollisionName collNode = CollisionNode(self.mazeCollisionName) collNode.addSolid(collSphere) collNodePath = self.toon.attachNewNode(collNode) collNodePath.hide() handler = CollisionHandlerEvent() handler.addInPattern('%fn-into-%in') base.cTrav.addCollider(collNodePath, handler) self.handler = handler self._collNodePath = collNodePath
def setupHeadSphere(self, avatarNodePath): collSphere = CollisionSphere(0, 0, 0, 1) collSphere.setTangible(1) collNode = CollisionNode('Flyer.cHeadCollSphere') collNode.setFromCollideMask(ToontownGlobals.CeilingBitmask) collNode.setIntoCollideMask(BitMask32.allOff()) collNode.addSolid(collSphere) self.cHeadSphereNodePath = avatarNodePath.attachNewNode(collNode) self.cHeadSphereNodePath.setZ(base.localAvatar.getHeight() + 1.0) self.headCollisionEvent = CollisionHandlerEvent() self.headCollisionEvent.addInPattern('%fn-enter-%in') self.headCollisionEvent.addOutPattern('%fn-exit-%in') base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent)
def setupEventSphere(self, bitmask, avatarRadius): self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius * 1.04) cSphere.setTangible(0) cSphereNode = CollisionNode('Flyer.cEventSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.event = CollisionHandlerEvent() self.event.addInPattern('enter%in') self.event.addOutPattern('exit%in') self.cEventSphereNodePath = cSphereNodePath
def getFlyBallBubble(self): if self.__flyBallBubble == None: bubble = CollisionSphere(0, 0, 0, GolfGlobals.GOLF_BALL_RADIUS) node = CollisionNode('flyBallBubble') node.addSolid(bubble) node.setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) node.setIntoCollideMask(BitMask32.allOff()) self.__flyBallBubble = NodePath(node) self.flyBallHandler = CollisionHandlerEvent() self.flyBallHandler.addInPattern('flyBallHit-%d' % self.index) #self.flyBallHandler.addInPattern('flyBallHit-%d--%in') return self.__flyBallBubble
def __init__(self, id, toon, game, guiMgr): CogdoMazePlayer.__init__(self, id, toon) self.disableGagCollision() self.game = game self.maze = self.game.maze self._guiMgr = guiMgr self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render) self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius orthoDrive = OrthoDrive( Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True) self.orthoWalk = OrthoWalk(orthoDrive) self._audioMgr = base.cogdoGameAudioMgr self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon) self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon) self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon) self._winSfx = self._audioMgr.createSfx('win') self._loseSfx = self._audioMgr.createSfx('lose') self.enabled = False self.pickupCount = 0 self.numEntered = 0 self.throwPending = False self.coolDownAfterHitInterval = Sequence( Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId) self.invulnerable = False self.gagHandler = CollisionHandlerEvent() self.gagHandler.addInPattern('%fn-into-%in') self.exited = False self.hints = { 'find': False, 'throw': False, 'squashed': False, 'boss': False, 'minion': False } self.accept('control', self.controlKeyPressed)
def startActivity(self, timestamp): self.pieHandler = CollisionHandlerEvent() self.pieHandler.setInPattern('pieHit-%fn') if self.player is not None: self.player.resetScore() self.hideTeamFlags(self.player.team) for player in list(self.players.values()): self.finishToonIval(player.toon.doId) player.enable() for cog in self.cogManager.cogs: cog.request("Active", timestamp) for ival in self.pieIvals: if ival.isPlaying(): ival.finish() self.pieIvals = []
class PartyCogActivity(DirectObject): notify = directNotify.newCategory("PartyCogActivity") cog = None arena = None player = None players = {} def __init__(self, activity): self.activity = activity self.root = self.activity.root self.toonPieTracks = {} self.toonPieEventNames = {} self.toonIdsToAnimIntervals = {} self.pieIvals = [] self.resultsIval = None def load(self): self.arena = loader.loadModel( "phase_13/models/parties/cogPieArena_model") self.arena.reparentTo(self.root) ground = self.arena.find("**/ground") # Make the ground plane draw before the shadow! ground.setBin("ground", 1) entranceArrows = self.arena.findAllMatches("**/arrowFlat*") for arrow in entranceArrows: arrow.setBin("ground", 5) # Get Entrance/Exit Locations self.leftEntranceLocator = self.arena.find("**/leftEntrance_locator") self.rightEntranceLocator = self.arena.find("**/rightEntrance_locator") self.leftExitLocator = self.arena.find("**/leftExit_locator") self.rightExitLocator = self.arena.find("**/rightExit_locator") self.teamCamPosLocators = (self.arena.find("**/team0CamPos_locator"), self.arena.find("**/team1CamPos_locator")) self.teamCamAimLocators = ( self.arena.find("**/team0CamAim_locator"), self.arena.find("**/team1CamAim_locator"), ) # Setup team locators # Toons are parented to these guys in order to do # Orthowalk properly leftTeamLocator = NodePath("TeamLocator-%d" % PartyGlobals.TeamActivityTeams.LeftTeam) leftTeamLocator.reparentTo(self.root) leftTeamLocator.setH(90) rightTeamLocator = NodePath("TeamLocator-%d" % PartyGlobals.TeamActivityTeams.RightTeam) rightTeamLocator.reparentTo(self.root) rightTeamLocator.setH(-90) self.teamLocators = (leftTeamLocator, rightTeamLocator) # Used to place the toons in even spaces self._lengthBetweenEntrances = self.leftEntranceLocator.getY( ) - self.rightExitLocator.getY() # Setup Sky Collisions. Important for cannons. self._skyCollisionsCollection = self.arena.findAllMatches( "**/cogPieArena_sky*_collision") if len(self._skyCollisionsCollection) > 0: self._skyCollisionParent = self._skyCollisionsCollection[ 0].getParent() else: self._skyCollisionParent = self.arena # Get all the wall collisions: self._wallCollisionsCollection = self.arena.findAllMatches( "**/cogPieArena_wall*_collision") # Get a hold of the flags: self._arenaFlagGroups = (self.arena.find("**/flagsL_grp"), self.arena.find("**/flagsR_grp")) self._initArenaDoors() # Setup Cogs self.cogManager = PartyCogManager() self.arrows = [] self.distanceLabels = [] self.teamColors = list(PartyGlobals.CogActivityColors) + [ PartyGlobals.TeamActivityStatusColor ] for i in range(3): start = self.arena.find("**/cog%d_start_locator" % (i + 1)) end = self.arena.find("**/cog%d_end_locator" % (i + 1)) cog = self.cogManager.generateCog(self.arena) cog.setEndPoints(start.getPos(), end.getPos()) arrow1 = StretchingArrow(self.arena, useColor="orange") arrow2 = StretchingArrow(self.arena, useColor="blue") arrow1.setZ(0.1) arrow2.setZ(0.1) self.arrows.append([arrow1, arrow2]) distanceLabel = self.createDistanceLabel(0, self.teamColors[1]) distanceLabel[0].stash() distanceLabel2 = self.createDistanceLabel(0, self.teamColors[0]) distanceLabel2[0].stash() self.distanceLabels.append([distanceLabel, distanceLabel2]) self.winText = [] text1 = self.createText(0, Point3(-0.5, 0.0, -0.5), self.teamColors[1]) text2 = self.createText(1, Point3(0.5, 0.0, -0.5), self.teamColors[0]) self.winText.append(text1) self.winText.append(text2) self.winStatus = self.createText(2, Point3(0.0, 0.0, -0.8), self.teamColors[0]) signLocator = self.arena.find("**/eventSign_locator") self.activity.sign.setPos(signLocator.getPos(self.root)) self.enable() def _initArenaDoors(self): """Initializes arena door locators, timers, and animations""" # Setup doors self._arenaDoors = ( self.arena.find("**/doorL"), self.arena.find("**/doorR"), ) arenaDoorLocators = (self.arena.find("**/doorL_locator"), self.arena.find("**/doorR_locator")) # Reparent those locators to the doors. for i in range(len(arenaDoorLocators)): arenaDoorLocators[i].wrtReparentTo(self._arenaDoors[i]) self._arenaDoorTimers = (self.createDoorTimer( PartyGlobals.TeamActivityTeams.LeftTeam), self.createDoorTimer( PartyGlobals.TeamActivityTeams.RightTeam)) self._arenaDoorIvals = [None, None] self._doorStartPos = [] for i in range(len(self._arenaDoors)): door = self._arenaDoors[i] timer = self._arenaDoorTimers[i] timer.reparentTo(arenaDoorLocators[i]) timer.hide() self._doorStartPos.append(door.getPos()) door.setPos(door, 0, 0, -7.0) def _destroyArenaDoors(self): for ival in self._arenaDoorIvals: ival.finish() self._arenaDoorIvals = None self._arenaDoors = None for timer in self._arenaDoorTimers: timer.stop() timer.removeNode() self._arenaDoorTimers = None def createDoorTimer(self, team): timer = ToontownTimer(useImage=False, highlightNearEnd=False) timer["text_font"] = ToontownGlobals.getMinnieFont() timer.setFontColor(PartyGlobals.CogActivityColors[team]) timer.setScale(7.0) timer.setPos(0.2, -0.03, 0.0) return timer def createText(self, number, position, color): text = TextNode("winText%d" % number) text.setAlign(TextNode.ACenter) text.setTextColor(color) text.setFont(ToontownGlobals.getSignFont()) text.setText("") noteText = aspect2d.attachNewNode(text) noteText.setScale(0.2) noteText.setPos(position) noteText.stash() return text, noteText def createDistanceLabel(self, number, color): text = TextNode("distanceText-%d" % number) text.setAlign(TextNode.ACenter) text.setTextColor(color) text.setFont(ToontownGlobals.getSignFont()) text.setText("10 ft") node = self.root.attachNewNode(text) node.setBillboardPointEye() node.setScale(2.5) node.setZ(5.0) return (node, text) def unload(self): self.disable() self._cleanupResultsIval() if self.winText is not None: for pair in self.winText: pair[1].reparentTo(hidden) pair[1].removeNode() self.winText = None if self.winStatus is not None: self.winStatus[1].reparentTo(hidden) self.winStatus[1].removeNode() self.winStatus = None if self.cogManager is not None: self.cogManager.unload() self.cogManager = None if self.arrows is not None: for pair in self.arrows: for arrow in pair: arrow.destroy() arrow = None pair = None self.arrows = None if self.distanceLabels is not None: for pair in self.distanceLabels: for (node, text) in pair: node.removeNode() pair = None self.distanceLabels = None if len(self.players): for player in list(self.players.values()): player.disable() player.destroy() self.players.clear() self.player = None if self.arena is not None: self.leftEntranceLocator = None self.rightEntranceLocator = None self.leftExitLocator = None self.rightExitLocator = None self._skyCollisions = None self._skyCollisionParent = None self._arenaFlagGroups = None self._destroyArenaDoors() self.arena.removeNode() self.arena = None for ival in list(self.toonPieTracks.values()): if ival is not None and ival.isPlaying(): ival.finish() self.toonPieTracks = {} for ival in self.pieIvals: if ival is not None and ival.isPlaying(): ival.finish() self.pieIvals = [] self.toonIdsToAnimIntervals = {} for eventName in list(self.toonPieEventNames.values()): self.ignore(eventName) self.toonPieEventNames = {} def enable(self): self.enableEnterGateCollision() def disable(self): self.disableEnterGateCollision() self.ignoreAll() def hideTeamFlags(self, team): self._arenaFlagGroups[team].stash() def showTeamFlags(self, team): self._arenaFlagGroups[team].unstash() def _playArenaDoorIval(self, team, opening=True): ival = self._arenaDoorIvals[team] if ival is not None and ival.isPlaying(): ival.pause() if not opening: pos = LVecBase3f(tuple(self._doorStartPos[team])) else: pos = LVecBase3f(tuple(self._doorStartPos[team])) + LVecBase3f( Point3(0, 0, -7.0)) ival = self._arenaDoors[team].posInterval(0.75, pos, blendType="easeIn") self._arenaDoorIvals[team] = ival ival.start() def openArenaDoorForTeam(self, team): self._playArenaDoorIval(team, opening=True) def closeArenaDoorForTeam(self, team): self._playArenaDoorIval(team, opening=False) def openArenaDoors(self): self.enableEnterGateCollision() for i in range(len(self._arenaDoors)): self.openArenaDoorForTeam(i) def closeArenaDoors(self): self.disableEnterGateCollision() for i in range(len(self._arenaDoors)): self.closeArenaDoorForTeam(i) def showArenaDoorTimers(self, duration): for timer in self._arenaDoorTimers: timer.setTime(duration) timer.countdown(duration) timer.show() def hideArenaDoorTimers(self): for timer in self._arenaDoorTimers: timer.hide() def enableEnterGateCollision(self): self.acceptOnce("entercogPieArena_entranceLeft_collision", self.handleEnterLeftEntranceTrigger) self.acceptOnce("entercogPieArena_entranceRight_collision", self.handleEnterRightEntranceTrigger) def disableEnterGateCollision(self): self.ignore("entercogPieArena_entranceLeft_collision") self.ignore("entercogPieArena_entranceRight_collision") def enableWallCollisions(self): self._wallCollisionsCollection.unstash() def disableWallCollisions(self): self._wallCollisionsCollection.stash() def enableSkyCollisions(self): self._skyCollisionsCollection.unstash() def disableSkyCollisions(self): self._skyCollisionsCollection.stash() def handleEnterLeftEntranceTrigger(self, collEntry): assert (self.notify.debug("handleEnterGateCollision")) self.activity.d_toonJoinRequest( PartyGlobals.TeamActivityTeams.LeftTeam) def handleEnterRightEntranceTrigger(self, collEntry): assert (self.notify.debug("handleEnterGateCollision")) self.activity.d_toonJoinRequest( PartyGlobals.TeamActivityTeams.RightTeam) def checkOrthoDriveCollision(self, oldPos, newPos): """Used by OrthoDrive to guarantee that the toon's pos stays inside the play area""" x = bound(newPos[0], -16.8, 16.8) y = bound(newPos[1], -17.25, -24.1) newPos.setX(x) newPos.setY(y) return newPos def getPlayerStartPos(self, team, spot): if team == PartyGlobals.TeamActivityTeams.LeftTeam: node = self.leftExitLocator else: node = self.rightExitLocator d = self._lengthBetweenEntrances / ( self.activity.getMaxPlayersPerTeam() + 1) yOffset = node.getY(self.root) + d * (spot + 1) pos = node.getPos(self.root) pos.setY(yOffset) return pos def handleToonJoined(self, toon, team, lateEntry=False): pos = self.getPlayerStartPos(team, self.activity.getIndex(toon.doId, team)) if toon == base.localAvatar: player = PartyCogActivityLocalPlayer(self.activity, pos, team, self.handleToonExited) player.entersActivity() self.player = player self.disableSkyCollisions() self.playPlayerEnterIval() else: player = PartyCogActivityPlayer(self.activity, toon, pos, team) player.entersActivity() # This only happens if the toon is joining after the activity has started if lateEntry: player.updateToonPosition() self.players[toon.doId] = player def handleToonSwitchedTeams(self, toon): toonId = toon.doId player = self.players.get(toonId) if player is None: self.notify.warning( "handleToonSwitchedTeams: toonId %s not found" % toonId) return team = self.activity.getTeam(toonId) spot = self.activity.getIndex(toonId, team) pos = self.getPlayerStartPos(team, spot) self.finishToonIval(toonId) player.setTeam(team) player.setToonStartPosition(pos) player.updateToonPosition() def handleToonShifted(self, toon): toonId = toon.doId if toonId in self.players: player = self.players[toonId] spot = self.activity.getIndex(toonId, player.team) pos = self.getPlayerStartPos(player.team, spot) player.setToonStartPosition(pos) if self.player is not None and toon == self.player.toon: self.playToonIval(base.localAvatar.doId, self.player.getRunToStartPositionIval()) def handleToonDisabled(self, toonId): self.finishToonIval(toonId) player = self.players.get(toonId) if player is not None: player.disable() if player == self.player: self.player = None del self.players[toonId] def playPlayerEnterIval(self): # Note: Disable "Switch Team" button while running b/c an unknown, bad interaction between # LerpPosInterval and startPosHprBroadcast (both in the run ival) causes the toon to be # immobile for approx. 200 ms or more if we call ival.finish(). def conditionallyShowSwitchButton(self=self, enable=True): if enable and self.activity.activityFSM._state in [ "WaitForEnough", "WaitToStart" ]: self.activity.teamActivityGui.enableSwitchButton() else: self.activity.teamActivityGui.disableSwitchButton() ival = Sequence(Func(self.disableWallCollisions), Func(conditionallyShowSwitchButton, self, False), self.player.getRunToStartPositionIval(), Func(conditionallyShowSwitchButton, self, True), Func(self.enableWallCollisions)) self.playToonIval(base.localAvatar.doId, ival) def finishToonIval(self, toonId): if self.toonIdsToAnimIntervals.get(toonId) is not None and \ self.toonIdsToAnimIntervals[toonId].isPlaying(): self.toonIdsToAnimIntervals[toonId].finish() def playToonIval(self, toonId, ival): self.finishToonIval(toonId) self.toonIdsToAnimIntervals[toonId] = ival ival.start() def startActivity(self, timestamp): self.pieHandler = CollisionHandlerEvent() self.pieHandler.setInPattern('pieHit-%fn') if self.player is not None: self.player.resetScore() self.hideTeamFlags(self.player.team) for player in list(self.players.values()): self.finishToonIval(player.toon.doId) player.enable() for cog in self.cogManager.cogs: cog.request("Active", timestamp) for ival in self.pieIvals: if ival.isPlaying(): ival.finish() self.pieIvals = [] def stopActivity(self): for player in list(self.players.values()): player.disable() for eventName in list(self.toonPieEventNames.values()): self.ignore(eventName) self.toonPieEventNames.clear() for cog in self.cogManager.cogs: cog.request("Static") def handleToonExited(self, toon): self.finishToonIval(toon.doId) player = self.players[toon.doId] player.disable() player.exitsActivity() player.destroy() if player == self.player: self.showTeamFlags(self.activity.getTeam(toon.doId)) self.player = None self.enableEnterGateCollision() self.enableSkyCollisions() del self.players[toon.doId] def pieThrow(self, avId, timestamp, heading, pos, power): """Show local or remote toon throwing a pie.""" toon = self.activity.getAvatar(avId) if toon is None: return tossTrack, pieTrack, flyPie = self.getTossPieInterval( toon, pos[0], pos[1], pos[2], heading, 0, 0, power) if avId == base.localAvatar.doId: flyPie.setTag('throwerId', str(avId)) collSphere = CollisionSphere(0, 0, 0, 0.5) # Make the sphere intangible collSphere.setTangible(0) name = "PieSphere-%d" % avId collSphereName = self.activity.uniqueName(name) collNode = CollisionNode(collSphereName) collNode.setFromCollideMask(ToontownGlobals.PieBitmask) collNode.addSolid(collSphere) collNP = flyPie.attachNewNode(collNode) base.cTrav.addCollider(collNP, self.pieHandler) self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName self.accept(self.toonPieEventNames[collNP], self.handlePieCollision) else: player = self.players.get(avId) if player is not None: player.faceForward() def matchRunningAnim(toon=toon): toon.playingAnim = None toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) pieTrack = Parallel(newTossTrack, pieTrack, name="PartyCogActivity.pieTrack-%d-%s" % (avId, timestamp)) elapsedTime = globalClockDelta.localElapsedTime(timestamp) if elapsedTime < 16. / 24.: elapsedTime = 16. / 24. # make the pie fly immediately pieTrack.start(elapsedTime) self.pieIvals.append(pieTrack) self.toonPieTracks[avId] = pieTrack def getTossPieInterval(self, toon, x, y, z, h, p, r, power, beginFlyIval=Sequence()): """Adapted from toon.py to suit our needs. Returns (toss, pie, flyPie), where toss is an interval to animate the toon tossing a pie, pie is the interval to animate the pie flying through the air, and pieModel is the model that flies. This is used in the final BossBattle sequence of CogHQ when we all throw pies directly at the boss cog. """ from toontown.toonbase import ToontownBattleGlobals from toontown.battle import BattleProps pie = toon.getPieModel() pie.setScale(0.5) flyPie = pie.copyTo(NodePath('a')) pieName = ToontownBattleGlobals.pieNames[toon.pieType] pieType = BattleProps.globalPropPool.getPropType(pieName) animPie = Sequence() if pieType == 'actor': animPie = ActorInterval(pie, pieName, startFrame=48) sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.mp3') # First, create a ProjectileInterval to compute the relative # velocity. assert 0 <= power <= 100, "invalid pie throw power %s" % power t = power / 100.0 # Distance ranges from CogActivityPieMinDist to CogActivityPieMaxDist ft, time ranges from 1 to 1.5 s. dist = lerp(PartyGlobals.CogActivityPieMinDist, PartyGlobals.CogActivityPieMaxDist, t) time = lerp(1.0, 1.5, t) proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) relVel = proj.startVel def getVelocity(toon=toon, relVel=relVel): return render.getRelativeVector(toon, relVel) * 0.6 toss = Track( ( 0, Sequence( Func(toon.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, toon.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), animPie, Parallel( ActorInterval( toon, 'throw', startFrame=48, #duration=0.25, #self.throwPieLimitTime, playRate=1.5, partName='torso'), animPie), Func(toon.setAnimState, 'Happy'), )), (16. / 24., Func(pie.detachNode))) fly = Track( (14. / 24., SoundInterval( sound, node=toon, cutOff=PartyGlobals.PARTY_COG_CUTOFF)), ( 16. / 24., Sequence( Func(flyPie.reparentTo, render), Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), beginFlyIval, ProjectileInterval( flyPie, startVel=getVelocity, duration=6), #LerpPosInterval(flyPie, duration = 3, Point3(0.52,50,2.24)), Func(flyPie.detachNode), )), ) return (toss, fly, flyPie) def handlePieCollision(self, colEntry): """Handle the pie thrown by the local toon hitting something.""" if not self.activity.isState("Active") or self.player is None: return handled = False into = colEntry.getIntoNodePath() intoName = into.getName() timestamp = globalClockDelta.localToNetworkTime( globalClock.getFrameTime(), bits=32) if "PartyCog" in intoName: if self.toonPieTracks.get(base.localAvatar.doId) is not None: self.toonPieTracks[base.localAvatar.doId].finish() self.toonPieTracks[base.localAvatar.doId] = None parts = intoName.split('-') cogID = int(parts[1]) point = colEntry.getSurfacePoint(self.cogManager.cogs[cogID].root) cog = self.cogManager.cogs[cogID] hitHead = ((point.getZ() > cog.getHeadLocation()) and not parts[2].startswith("Arm")) if self.activity.getTeam( base.localAvatar.doId ) == PartyGlobals.TeamActivityTeams.LeftTeam: direction = -1.0 else: direction = 1.0 self.activity.b_pieHitsCog(timestamp, cogID, point, direction, hitHead) if hitHead: hitPoints = self.player.hitHead() else: hitPoints = self.player.hitBody() self.player.updateScore() if hitPoints > 0: cog.showHitScore(hitPoints) handled = True elif "distAvatarCollNode" in intoName: parts = intoName.split('-') hitToonId = int(parts[1]) toon = base.cr.doId2do.get(hitToonId) if toon is not None and self.activity.getTeam( hitToonId) != self.player.team: point = colEntry.getSurfacePoint(toon) self.activity.b_pieHitsToon(hitToonId, timestamp, point) handled = True # Ignore other collision events if this collision was handled. if handled: eventName = self.toonPieEventNames.get(colEntry.getFromNodePath()) if eventName is not None: self.ignore(eventName) del self.toonPieEventNames[colEntry.getFromNodePath()] def pieHitsCog(self, timestamp, cogNum, pos, direction, part): """A toon hit the suit, make the suit do something.""" cog = self.cogManager.cogs[cogNum] cog.respondToPieHit(timestamp, pos, part, direction) def pieHitsToon(self, toonId, timestamp, pos): player = self.players.get(toonId) if player is not None: player.respondToPieHit(timestamp, pos) def setCogDistances(self, distances): self.cogManager.updateDistances(distances) def showCogs(self): for cog in self.cogManager.cogs: cog.request("Static") def hideCogs(self): for cog in self.cogManager.cogs: cog.request("Down") def showResults(self, resultsText, winner, totals): if self.player is None: return base.localAvatar.showName() self.resultsIval = Sequence( Wait(0.1), Func(self.activity.setStatus, TTLocalizer.PartyCogTimeUp), Func(self.activity.showStatus), Wait(2.0), Func(self.activity.hideStatus), Wait(0.5), Func(self.player.lookAtArena), Func(self.showTeamFlags, self.activity.getTeam(base.localAvatar.doId)), Wait(1.0), Func(self.showArrow, 0), Wait(1.3), Func(self.showArrow, 1), Wait(1.3), Func(self.showArrow, 2), Wait(1.3), Func(self.showTotals, totals), Wait(1.0), Func(self.showWinner, resultsText, winner), Func(self._cleanupResultsIval), name="PartyCog-conclusionSequence") # Cancel the rewards ival if the jellybean screen pops up. If this happens it means the client # is lagging; the rewards screen tears down the GUI, which this ival uses. self.accept('DistributedPartyActivity-showJellybeanReward', self._cleanupResultsIval) self.resultsIval.start() def _cleanupResultsIval(self): if self.resultsIval: if self.resultsIval.isPlaying(): self.resultsIval.pause() self.resultsIval = None self.ignore('DistributedPartyActivity-showJellybeanReward') def showTotals(self, totals): newtotals = (totals[1] - totals[0] + (PartyGlobals.CogActivityArenaLength / 2.0) * 3, totals[0] - totals[1] + (PartyGlobals.CogActivityArenaLength / 2.0) * 3) self.winText[0][0].setText(TTLocalizer.PartyCogDistance % newtotals[0]) self.winText[1][0].setText(TTLocalizer.PartyCogDistance % newtotals[1]) for textPair in self.winText: textPair[1].unstash() def hideTotals(self): for textPair in self.winText: textPair[0].setText("") textPair[1].stash() def showWinner(self, text, winner): self.winStatus[0].setText(text) self.winStatus[0].setTextColor(self.teamColors[winner]) self.winStatus[1].unstash() def hideWinner(self): self.winStatus[0].setText("") self.winStatus[1].stash() def showArrow(self, arrowNum): arrows = self.arrows[arrowNum] cog = self.cogManager.cogs[arrowNum] points = [ self.arena.find("**/cog%d_start_locator" % (arrowNum + 1)), self.arena.find("**/cog%d_end_locator" % (arrowNum + 1)) ] Y = cog.root.getY() for point in points: point.setY(Y) for i in range(len(arrows)): arrow = arrows[i] arrow.draw(points[i].getPos(), cog.root.getPos(), animate=False) arrow.unstash() i = -1 length = PartyGlobals.CogActivityArenaLength for (node, text) in self.distanceLabels[arrowNum]: current = bound(i, 0, 1) node.setPos(cog.root.getPos(self.root) + Point3(i * 4, 2, 4)) dist = PartyCogUtils.getCogDistanceUnitsFromCenter(cog.currentT) dist = abs(dist - (i * length / 2)) #if i == -1: # dist = length/2 + -i*dist if dist > (length - dist): node.setScale(2.8) else: node.setScale(2.2) text.setText(TTLocalizer.PartyCogDistance % dist) if dist > 0: node.unstash() else: arrows[current].stash() i += 2 def hideArrows(self): for pair in self.arrows: for arrow in pair: arrow.stash() for pair in self.distanceLabels: for (node, text) in pair: node.stash() def hideResults(self): self.hideArrows() self.hideTotals() self.hideWinner()
class CogdoMazeLocalPlayer(CogdoMazePlayer): notify = directNotify.newCategory('CogdoMazeLocalPlayer') def __init__(self, id, toon, game, guiMgr): CogdoMazePlayer.__init__(self, id, toon) self.disableGagCollision() self.game = game self.maze = self.game.maze self._guiMgr = guiMgr self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render) self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius orthoDrive = OrthoDrive( Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True) self.orthoWalk = OrthoWalk(orthoDrive) self._audioMgr = base.cogdoGameAudioMgr self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon) self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon) self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon) self._winSfx = self._audioMgr.createSfx('win') self._loseSfx = self._audioMgr.createSfx('lose') self.enabled = False self.pickupCount = 0 self.numEntered = 0 self.throwPending = False self.coolDownAfterHitInterval = Sequence( Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId) self.invulnerable = False self.gagHandler = CollisionHandlerEvent() self.gagHandler.addInPattern('%fn-into-%in') self.exited = False self.hints = { 'find': False, 'throw': False, 'squashed': False, 'boss': False, 'minion': False } self.accept('control', self.controlKeyPressed) def destroy(self): self.toon.showName() self.ignoreAll() self.coolDownAfterHitInterval.clearToInitial() del self.coolDownAfterHitInterval del self._getMemoSfx del self._waterCoolerFillSfx del self._hitByDropSfx del self._winSfx self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk CogdoMazePlayer.destroy(self) def __initCollisions(self): collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius) collSphere.setTangible(0) self.mazeCollisionName = Globals.LocalPlayerCollisionName collNode = CollisionNode(self.mazeCollisionName) collNode.addSolid(collSphere) collNodePath = self.toon.attachNewNode(collNode) collNodePath.hide() handler = CollisionHandlerEvent() handler.addInPattern('%fn-into-%in') base.cTrav.addCollider(collNodePath, handler) self.handler = handler self._collNodePath = collNodePath def clearCollisions(self): self.handler.clear() def __disableCollisions(self): self._collNodePath.removeNode() del self._collNodePath def _isNearPlayer(self, player): return self.toon.getDistance(player.toon) <= self._proximityRadius def update(self, dt): if self.getCurrentOrNextState() != 'Off': self._updateCamera(dt) def _updateCamera(self, dt): numPlayers = 0.0 for player in self.game.players: if player != self and player.toon and self._isNearPlayer(player): numPlayers += 1 d = clamp( Globals.CameraMinDistance + numPlayers / (CogdoGameConsts.MaxPlayers - 1) * (Globals.CameraMaxDistance - Globals.CameraMinDistance), Globals.CameraMinDistance, Globals.CameraMaxDistance) self.cameraMgr.setCameraTargetDistance(d) self.cameraMgr.update(dt) def enterOff(self): CogdoMazePlayer.enterOff(self) def exitOff(self): CogdoMazePlayer.exitOff(self) self.toon.hideName() def enterReady(self): CogdoMazePlayer.enterReady(self) self.cameraMgr.enable() def exitReady(self): CogdoMazePlayer.enterReady(self) def enterNormal(self): CogdoMazePlayer.enterNormal(self) self.orthoWalk.start() def exitNormal(self): CogdoMazePlayer.exitNormal(self) self.orthoWalk.stop() def enterHit(self, elapsedTime=0.0): CogdoMazePlayer.enterHit(self, elapsedTime) self.setInvulnerable(True) def exitHit(self): CogdoMazePlayer.exitHit(self) self.coolDownAfterHitInterval.clearToInitial() self.coolDownAfterHitInterval.start() def enterDone(self): CogdoMazePlayer.enterDone(self) self._guiMgr.hideQuestArrow() self.ignore('control') self._guiMgr.setMessage('') if self.exited == False: self.lostMemos() def exitDone(self): CogdoMazePlayer.exitDone(self) def hitByDrop(self): if self.equippedGag is not None and not self.hints['squashed']: self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeSquashHint, Globals.HintTimeout) self.hints['squashed'] = True self._hitByDropSfx.play() CogdoMazePlayer.hitByDrop(self) return def equipGag(self): CogdoMazePlayer.equipGag(self) self._waterCoolerFillSfx.play() messenger.send(Globals.WaterCoolerHideEventName, []) if not self.hints['throw']: self._guiMgr.setMessage(TTLocalizer.CogdoMazeThrowHint) self.hints['throw'] = True def hitSuit(self, suitType): if suitType == Globals.SuitTypes.Boss and not self.hints['boss']: self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeBossHint, Globals.HintTimeout) self.hints['boss'] = True if suitType != Globals.SuitTypes.Boss and not self.hints['minion']: self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeMinionHint, Globals.HintTimeout) self.hints['minion'] = True def createThrowGag(self, gag): throwGag = CogdoMazePlayer.createThrowGag(self, gag) collSphere = CollisionSphere(0, 0, 0, 0.5) collSphere.setTangible(0) name = Globals.GagCollisionName collNode = CollisionNode(name) collNode.setFromCollideMask(ToontownGlobals.PieBitmask) collNode.addSolid(collSphere) colNp = throwGag.attachNewNode(collNode) base.cTrav.addCollider(colNp, self.gagHandler) return throwGag def showToonThrowingGag(self, heading, pos): self._guiMgr.clearMessage() return CogdoMazePlayer.showToonThrowingGag(self, heading, pos) def removeGag(self): if self.equippedGag is None: return CogdoMazePlayer.removeGag(self) self.throwPending = False messenger.send(Globals.WaterCoolerShowEventName, []) return def controlKeyPressed(self): if self.game.finished or self.throwPending or self.getCurrentOrNextState( ) == 'Hit' or self.equippedGag == None: return self.throwPending = True heading = self.toon.getH() pos = self.toon.getPos() self.game.requestUseGag(pos.getX(), pos.getY(), heading) return def completeThrow(self): self.clearCollisions() CogdoMazePlayer.completeThrow(self) def shakeCamera(self, strength): self.cameraMgr.shake(strength) def getCameraShake(self): return self.cameraMgr.shakeStrength def setInvulnerable(self, bool): self.invulnerable = bool def handleGameStart(self): self.numEntered = len(self.game.players) self.__initCollisions() self._guiMgr.startGame(TTLocalizer.CogdoMazeFindHint) self.hints['find'] = True self.notify.info( 'toonId:%d laff:%d/%d %d player(s) started maze game' % (self.toon.doId, self.toon.hp, self.toon.maxHp, len(self.game.players))) def handleGameExit(self): self.cameraMgr.disable() self.__disableCollisions() def handlePickUp(self, toonId): if toonId == self.toon.doId: self.pickupCount += 1 self._guiMgr.setPickupCount(self.pickupCount) if self.pickupCount == 1: self._guiMgr.showPickupCounter() self._getMemoSfx.play() def handleOpenDoor(self, door): self._guiMgr.setMessage(TTLocalizer.CogdoMazeGameDoorOpens) self._guiMgr.showQuestArrow(self.toon, door, Point3(0, 0, self.toon.getHeight() + 2)) def handleTimeAlert(self): self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeAlert) def handleToonRevealsDoor(self, toonId, door): if toonId == self.toon.doId: self._guiMgr.setMessageTemporary( TTLocalizer.CogdoMazeGameLocalToonFoundExit) def handleToonEntersDoor(self, toonId, door): self.exited = True message = '' if door.getPlayerCount() < len(self.game.players): message = TTLocalizer.CogdoMazeGameWaitingForToons if toonId == self.toon.doId: self._guiMgr.setMessage(message) self._winSfx.play() self._audioMgr.stopMusic() self.notify.info( 'toonId:%d laff:%d/%d %d player(s) succeeded in maze game. Going to the executive suit building.' % (toonId, self.toon.hp, self.toon.maxHp, len(self.game.players))) if self.numEntered > len(self.game.players): self.notify.info('%d player(s) failed in maze game' % (self.numEntered - len(self.game.players))) def lostMemos(self): self.pickupCount = 0 self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeOut) self._guiMgr.setPickupCount(self.pickupCount) self.notify.info( 'toonId:%d laff:%d/%d %d player(s) failed in maze game' % (self.toon.doId, self.toon.hp, self.toon.maxHp, len(self.game.players)))
class CogdoFlyingCollisions(GravityWalker): wantFloorSphere = 0 def __init__(self): GravityWalker.__init__(self, gravity=0.0) def initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius=1.4, floorOffset=1.0, reach=1.0): self.cHeadSphereNodePath = None self.cFloorEventSphereNodePath = None self.setupHeadSphere(avatarNodePath) self.setupFloorEventSphere(avatarNodePath, ToontownGlobals.FloorEventBitmask, avatarRadius) GravityWalker.initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius, floorOffset, reach) return def setupWallSphere(self, bitmask, avatarRadius): self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius) cSphereNode = CollisionNode('Flyer.cWallSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) if ConfigVariableBool('want-fluid-pusher', 0).getValue(): self.pusher = CollisionHandlerFluidPusher() else: self.pusher = CollisionHandlerPusher() self.pusher.addCollider(cSphereNodePath, self.avatarNodePath) self.cWallSphereNodePath = cSphereNodePath def setupEventSphere(self, bitmask, avatarRadius): self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius * 1.04) cSphere.setTangible(0) cSphereNode = CollisionNode('Flyer.cEventSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.event = CollisionHandlerEvent() self.event.addInPattern('enter%in') self.event.addOutPattern('exit%in') self.cEventSphereNodePath = cSphereNodePath def setupRay(self, bitmask, floorOffset, reach): cRay = CollisionRay(0.0, 0.0, 3.0, 0.0, 0.0, -1.0) cRayNode = CollisionNode('Flyer.cRayNode') cRayNode.addSolid(cRay) self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode) cRayNode.setFromCollideMask(bitmask) cRayNode.setIntoCollideMask(BitMask32.allOff()) self.lifter = CollisionHandlerGravity() self.lifter.setLegacyMode(self._legacyLifter) self.lifter.setGravity(self.getGravity(0)) self.lifter.addInPattern('%fn-enter-%in') self.lifter.addAgainPattern('%fn-again-%in') self.lifter.addOutPattern('%fn-exit-%in') self.lifter.setOffset(floorOffset) self.lifter.setReach(reach) self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath) def setupHeadSphere(self, avatarNodePath): collSphere = CollisionSphere(0, 0, 0, 1) collSphere.setTangible(1) collNode = CollisionNode('Flyer.cHeadCollSphere') collNode.setFromCollideMask(ToontownGlobals.CeilingBitmask) collNode.setIntoCollideMask(BitMask32.allOff()) collNode.addSolid(collSphere) self.cHeadSphereNodePath = avatarNodePath.attachNewNode(collNode) self.cHeadSphereNodePath.setZ(base.localAvatar.getHeight() + 1.0) self.headCollisionEvent = CollisionHandlerEvent() self.headCollisionEvent.addInPattern('%fn-enter-%in') self.headCollisionEvent.addOutPattern('%fn-exit-%in') base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent) def setupFloorEventSphere(self, avatarNodePath, bitmask, avatarRadius): cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.75) cSphereNode = CollisionNode('Flyer.cFloorEventSphere') cSphereNode.addSolid(cSphere) cSphereNodePath = avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.floorCollisionEvent = CollisionHandlerEvent() self.floorCollisionEvent.addInPattern('%fn-enter-%in') self.floorCollisionEvent.addAgainPattern('%fn-again-%in') self.floorCollisionEvent.addOutPattern('%fn-exit-%in') base.cTrav.addCollider(cSphereNodePath, self.floorCollisionEvent) self.cFloorEventSphereNodePath = cSphereNodePath def deleteCollisions(self): GravityWalker.deleteCollisions(self) if self.cHeadSphereNodePath != None: base.cTrav.removeCollider(self.cHeadSphereNodePath) self.cHeadSphereNodePath.detachNode() self.cHeadSphereNodePath = None self.headCollisionsEvent = None if self.cFloorEventSphereNodePath != None: base.cTrav.removeCollider(self.cFloorEventSphereNodePath) self.cFloorEventSphereNodePath.detachNode() self.cFloorEventSphereNodePath = None self.floorCollisionEvent = None self.cRayNodePath.detachNode() del self.cRayNodePath self.cEventSphereNodePath.detachNode() del self.cEventSphereNodePath return def setCollisionsActive(self, active=1): if self.collisionsActive != active: if self.cHeadSphereNodePath != None: base.cTrav.removeCollider(self.cHeadSphereNodePath) if active: base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent) if self.cFloorEventSphereNodePath != None: base.cTrav.removeCollider(self.cFloorEventSphereNodePath) if active: base.cTrav.addCollider(self.cFloorEventSphereNodePath, self.floorCollisionEvent) GravityWalker.setCollisionsActive(self, active) return def enableAvatarControls(self): pass def disableAvatarControls(self): pass def handleAvatarControls(self, task): pass
class DistributedGolfSpot(DistributedObject.DistributedObject, FSM.FSM): """ This is one of four golf spots to appear in the corner of the CEO banquet room. """ notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfSpot') positions = ((-45, 100, GolfGlobals.GOLF_BALL_RADIUS), (-15, 100, GolfGlobals.GOLF_BALL_RADIUS), (15, 100, GolfGlobals.GOLF_BALL_RADIUS), (45, 100, GolfGlobals.GOLF_BALL_RADIUS)) toonGolfOffsetPos = Point3(-2, 0, -GolfGlobals.GOLF_BALL_RADIUS) toonGolfOffsetHpr = Point3(-90, 0, 0) rotateSpeed = 20 # degrees per second # The number of seconds it takes to move the power meter to # full the first time. golfPowerSpeed = base.config.GetDouble('golf-power-speed', 3) # The exponent that controls the factor at which the power # meter slows down over time. Values closer to 1.0 slow down less # quickly. golfPowerExponent = base.config.GetDouble('golf-power-exponent', 0.75) def __init__(self, cr): DistributedObject.DistributedObject.__init__(self, cr) FSM.FSM.__init__(self, 'DistributedGolfSpot') self.boss = None self.index = None self.avId = 0 self.toon = None # the actual toon which should match up with self.avId self.golfSpotSmoother = SmoothMover() self.golfSpotSmoother.setSmoothMode(SmoothMover.SMOn) self.smoothStarted = 0 self.__broadcastPeriod = 0.2 if (self.index is not None) and (self.index > len(self.positions)): self.notify.error("Invalid index %d" % self.index) self.fadeTrack = None # stuff related to power bar self.setupPowerBar() self.aimStart = None self.golfSpotAdviceLabel = None # This number increments each time we change direction on the # crane controls. It's used to update the animation # appropriately. self.changeSeq = 0 self.lastChangeSeq = 0 self.controlKeyAllowed = False self.flyBallTracks = {} self.splatTracks = {} self.__flyBallBubble = None self.flyBallHandler = None self.__flyBallSequenceNum = 0 self.swingInterval = None self.lastHitSequenceNum = -1 self.goingToReward = False self.gotHitByBoss = False self.releaseTrack = None self.grabTrack = None self.restoreScaleTrack = None def setBossCogId(self, bossCogId): """Handle receiving the CEO doId from the server.""" self.bossCogId = bossCogId # This would be risky if we had toons entering the zone during # a battle--but since all the toons are always there from the # beginning, we can be confident that the BossCog has already # been generated by the time we receive the generate for its # associated battles. self.boss = base.cr.doId2do[bossCogId] self.boss.setGolfSpot(self, self.index) def setIndex(self, index): """Handle receiving the index which identifies our side the server.""" self.index = index # WARNING debug only, remove this #if (index == 0): # base.gs = self def disable(self): """Disable ourself.""" DistributedObject.DistributedObject.disable(self) self.ignoreAll() def delete(self): """Delete ourself.""" DistributedObject.DistributedObject.delete(self) self.ignoreAll() self.boss = None def announceGenerate(self): """Do more setup once all required fields are in.""" DistributedObject.DistributedObject.announceGenerate(self) self.triggerName = self.uniqueName('trigger') self.triggerEvent = 'enter%s' % (self.triggerName) self.smoothName = self.uniqueName('golfSpotSmooth') self.golfSpotAdviceName = self.uniqueName('golfSpotAdvice') self.posHprBroadcastName = self.uniqueName('golfSpotBroadcast') self.ballPowerTaskName = self.uniqueName('updateGolfPower') self.adjustClubTaskName = self.uniqueName('adjustClub') self.loadAssets() self.accept('flyBallHit-%d' % self.index, self.__flyBallHit) def loadAssets(self): """Load our assets.""" self.root = render.attachNewNode('golfSpot-%d' % self.index) self.root.setPos(*self.positions[self.index]) self.ballModel = loader.loadModel('phase_6/models/golf/golf_ball') self.ballColor = VBase4(1, 1, 1, 1) if self.index < len(GolfGlobals.PlayerColors): self.ballColor = VBase4(*GolfGlobals.PlayerColors[self.index]) self.ballModel.setColorScale(self.ballColor) self.ballModel.reparentTo(self.root) self.club = loader.loadModel('phase_6/models/golf/putter') self.clubLookatSpot = self.root.attachNewNode('clubLookat') self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) # create a collision sphere to trigger when we touch the ball # Make a trigger sphere so we can detect when the local avatar # runs up to the controls. We bury the sphere mostly under # the floor to minimize accidental collisions. cs = CollisionSphere(0, 0, 0, 1) cs.setTangible(0) cn = CollisionNode(self.triggerName) cn.addSolid(cs) cn.setIntoCollideMask(ToontownGlobals.WallBitmask) self.trigger = self.root.attachNewNode(cn) self.trigger.stash() self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.mp3') def cleanup(self): if self.swingInterval: self.swingInterval.finish() self.swingInterval = None if self.releaseTrack: self.releaseTrack.finish() self.releaseTrack = None flyTracks = list(self.flyBallTracks.values()) for track in flyTracks: track.finish() if self.fadeTrack: self.fadeTrack.finish() self.fadeTrack = None if self.restoreScaleTrack: self.restoreScaleTrack.finish() self.restoreScaleTrack = None self.root.removeNode() self.ballModel.removeNode() self.club.removeNode() if self.powerBar: self.powerBar.destroy() self.powerBar = None taskMgr.remove(self.triggerName) assert self.notify.debugStateCall(self) self.boss = None def setState(self, state, avId, extraInfo): """Handle the AI telling us the current state, and who controls us.""" if not self.isDisabled(): self.gotHitByBoss = extraInfo if state == 'C': self.demand('Controlled', avId) elif state == 'F': self.demand('Free') elif state == 'O': self.demand('Off') else: self.notify.error("Invalid state from AI: %s" % (state)) ### FSM States ### def enterOff(self): """Handle entering the off state.""" assert self.notify.debugStateCall(self) pass def exitOff(self): """Handle exiting the off state.""" assert self.notify.debugStateCall(self) pass def enterFree(self): """Handle entering the free state.""" assert self.notify.debugStateCall(self) if self.fadeTrack: self.fadeTrack.finish() self.fadeTrack = None # Wait a few seconds before neutralizing the scale; maybe the # same avatar wants to come right back (after his 5-second # timeout). self.restoreScaleTrack = Sequence(Wait(6), self.getRestoreScaleInterval(), name="restoreScaleTrack") self.restoreScaleTrack.start() if self.avId == localAvatar.doId: # Five second timeout on grabbing the same crane again. Go # get a different crane! if not self.isDisabled(): self.ballModel.setAlphaScale(0.3) self.ballModel.setTransparency(1) taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) self.fadeTrack = Sequence(Func(self.ballModel.setTransparency, 1), self.ballModel.colorScaleInterval( 0.2, VBase4(1, 1, 1, 0.3)), name='fadeTrack-enterFree') self.fadeTrack.start() else: # Other players can grab this crane immediately. self.trigger.unstash() self.accept(self.triggerEvent, self.__hitTrigger) self.avId = 0 def exitFree(self): """Handle exiting the free state.""" if self.fadeTrack: self.fadeTrack.finish() self.fadeTrack = None self.restoreScaleTrack.finish() # make sure we don't see a small ball self.restoreScaleTrack = None taskMgr.remove(self.triggerName) #self.ballModel.clearColorScale() self.ballModel.clearTransparency() self.trigger.stash() self.ignore(self.triggerEvent) pass def enterControlled(self, avId): """Handle entering the controlled state.""" assert self.notify.debugStateCall(self) self.avId = avId toon = base.cr.doId2do.get(avId) if not toon: return self.enableControlKey() self.toon = toon self.grabTrack = self.makeToonGrabInterval(toon) if avId == localAvatar.doId: # The local toon is beginning to control the crane. self.boss.toCraneMode() camera.reparentTo(self.root) camera.setPosHpr(0, -10, 3, 0, 0, 0) #self.tube.stash() #localAvatar.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) localAvatar.setPos(self.root, self.toonGolfOffsetPos) localAvatar.setHpr(self.root, self.toonGolfOffsetHpr) localAvatar.sendCurrentPosition() #self.__activatePhysics() self.__enableControlInterface() self.startPosHprBroadcast() #self.startShadow() # If we get a message from the Place that we exited Crane # mode--for instance, because we got hit by flying # gears--then ask the AI to yield us up. self.accept('exitCrane', self.gotBossZapped) else: pass #self.startSmooth() #toon.stopSmooth() #self.grabTrack = Sequence(self.grabTrack, # Func(toon.startSmooth)) self.grabTrack.start() pass def exitControlled(self): """Handle exiting the controlled state.""" assert self.notify.debugStateCall(self) self.grabTrack.finish() del self.grabTrack if self.swingInterval: self.swingInterval.finish() self.swingInterval = None if not self.ballModel.isEmpty(): if self.ballModel.isHidden(): self.notify.debug('ball is hidden scale =%s' % self.ballModel.getScale()) else: self.notify.debug('ball is showing scale=%s' % self.ballModel.getScale()) if self.toon and not self.toon.isDisabled(): #self.toon.loop('neutral') #self.notify.debug('looping neutral') self.toon.startSmooth() self.releaseTrack = self.makeToonReleaseInterval(self.toon) self.stopPosHprBroadcast() #self.stopShadow() self.stopSmooth() if self.avId == localAvatar.doId: # The local toon is no longer in control of the crane. self.__disableControlInterface() #self.__deactivatePhysics() #self.tube.unstash() if not self.goingToReward: camera.reparentTo(base.localAvatar) camera.setPos(base.localAvatar.cameraPositions[0][0]) camera.setHpr(0, 0, 0) #self.__straightenCable() #self.avId = 0 #self.toon = None self.stopAdjustClubTask() self.releaseTrack.start() self.enableControlKey() def __allowDetect(self, task): if self.fadeTrack: self.fadeTrack.finish() self.fadeTrack = Sequence( self.ballModel.colorScaleInterval(0.2, self.ballColor), #Func(self.ballModel.clearColorScale), Func(self.ballModel.clearTransparency), name='fadeTrack-allowDetect') self.fadeTrack.start() self.trigger.unstash() self.accept(self.triggerEvent, self.__hitTrigger) def __hitTrigger(self, event): self.d_requestControl() def getRestoreScaleInterval(self): # This undoes the effect of accomodateToon(), to restore the # controls' scale to neutral position. Unlike # accomodateToon(), it has no side effects; you must play (or # immediately finish) the interval to restore the scale. return Sequence() #lerpTime = 1 #return Parallel( # self.controlModel.scaleInterval(lerpTime, 1, blendType = 'easeInOut'), # self.cc.posInterval(lerpTime, Point3(0, 0, 0), blendType = 'easeInOut'), # self.bottom.posInterval(lerpTime, self.bottomPos, blendType = 'easeInOut'), # self.stickHinge.quatInterval(lerpTime, self.neutralStickHinge, blendType = 'easeInOut'), # ) def d_requestControl(self): self.sendUpdate('requestControl') def d_requestFree(self, gotHitByBoss): self.sendUpdate('requestFree', [gotHitByBoss]) def makeToonGrabInterval(self, toon): # Generates an interval showing the crane controls scaling to # match the toon and the toon simultaneously reaching to grab # the controls. Thenceforth, the toon will animate with the # controls. origPos = toon.getPos(self.root) origHpr = toon.getHpr(self.root) a = self.accomodateToon(toon) newPos = toon.getPos() newHpr = toon.getHpr() origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) self.notify.debug('toon.setPosHpr %s %s' % (origPos, origHpr)) toon.setPosHpr(origPos, origHpr) walkTime = 0.2 reach = Sequence() #ActorInterval(toon, 'GolfPuttLoop') if reach.getDuration() < walkTime: reach = Sequence( ActorInterval(toon, 'walk', loop=1, duration=walkTime - reach.getDuration()), reach) i = Sequence( Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), reach), #Func(self.startWatchJoystick, toon) Func(toon.stopLookAround), ) if toon == base.localAvatar: i.append(Func(self.switchToAnimState, 'GolfPuttLoop')) i.append(Func(self.startAdjustClubTask)) i = Parallel(i, a) return i def accomodateToon(self, toon): # This method has two effects: # (1) It computes and returns an interval to scale and slide # the crane controls to suit the indicated toon. # (2) As a side effect, when it returns, the crane controls are # *already* scaled and slid to accomodate the toon, and the toon # has been positioned in place to operate the controls. # Thus, you can use it either by calling it and playing the # interval that it returns to get a smooth lerp, or simply by # calling it and ignoring the return value, to jump to # position. toon.wrtReparentTo(self.root) toon.setPos(self.toonGolfOffsetPos) toon.setHpr(self.toonGolfOffsetHpr) return Sequence() def switchToAnimState(self, animStateName, forced=False): """Switch the toon to another anim state if not in it already.""" # temp since we only have anims for male medium torso medium legs #dna = base.localAvatar.getStyle() #if not( dna.gender == 'm' and (dna.torso =='ms' or dna.torso =='ls')\ # and (dna.legs=='m' or dna.legs=='l')): # return curAnimState = base.localAvatar.animFSM.getCurrentState() #self.notify.debug('curAnimState=%s' % curAnimState) curAnimStateName = '' if curAnimState: curAnimStateName = curAnimState.getName() if curAnimStateName != animStateName or forced: base.localAvatar.b_setAnimState(animStateName) def __enableControlInterface(self): """Enable the control interface.""" gui = loader.loadModel("phase_3.5/models/gui/avatar_panel_gui") self.closeButton = DirectButton( image=( gui.find("**/CloseBtn_UP"), gui.find("**/CloseBtn_DN"), gui.find("**/CloseBtn_Rllvr"), gui.find("**/CloseBtn_UP"), ), relief=None, scale=2, text=TTLocalizer.BossbotGolfSpotLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitGolfSpot, ) self.accept('escape', self.__exitGolfSpot) self.accept('control', self.__controlPressed) self.accept('control-up', self.__controlReleased) self.accept('InputState-forward', self.__upArrow) self.accept('InputState-reverse', self.__downArrow) self.accept('InputState-turnLeft', self.__leftArrow) self.accept('InputState-turnRight', self.__rightArrow) taskMgr.add(self.__watchControls, 'watchGolfSpotControls') # In case they don't figure it out, hit them over the head # with it after a few seconds. taskMgr.doMethodLater(5, self.__displayGolfSpotAdvice, self.golfSpotAdviceName) #taskMgr.doMethodLater(10, self.__displayMagnetAdvice, # self.magnetAdviceName) # Up in the sky, it's hard to read what people are saying. #NametagGlobals.setOnscreenChatForced(1) self.arrowVert = 0 self.arrowHorz = 0 if self.powerBar: self.powerBar.show() def __disableControlInterface(self): """Disable the control interface.""" #self.__turnOffMagnet() if self.closeButton: self.closeButton.destroy() self.closeButton = None self.__cleanupGolfSpotAdvice() #self.__cleanupMagnetAdvice() self.ignore('escape') self.ignore('control') self.ignore('control-up') self.ignore('InputState-forward') self.ignore('InputState-reverse') self.ignore('InputState-turnLeft') self.ignore('InputState-turnRight') self.arrowVert = 0 self.arrowHorz = 0 #NametagGlobals.setOnscreenChatForced(0) taskMgr.remove('watchGolfSpotControls') if self.powerBar: self.powerBar.hide() else: self.notify.debug('self.powerBar is none') #self.__setMoveSound(None) def setupPowerBar(self): """Create the power bar for the water golfSpot.""" self.powerBar = DirectWaitBar( pos=(0.0, 0, -0.94), 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() def resetPowerBar(self): """Bring the power and power bar to zero.""" self.power = 0 self.powerBar['value'] = self.power self.powerBar['text'] = '' def __displayGolfSpotAdvice(self, task): """Display golfSpot advice on the screen.""" if self.golfSpotAdviceLabel == None: self.golfSpotAdviceLabel = DirectLabel( text=TTLocalizer.BossbotGolfSpotAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) def __cleanupGolfSpotAdvice(self): """Remove golfSpot advice from the screen.""" if self.golfSpotAdviceLabel: self.golfSpotAdviceLabel.destroy() self.golfSpotAdviceLabel = None taskMgr.remove(self.golfSpotAdviceName) def showExiting(self): """Indicate that we've sent an exiting message to AI.""" if self.closeButton: self.closeButton.destroy() self.closeButton = DirectLabel( relief=None, text=TTLocalizer.BossbotGolfSpotLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1), ) self.__cleanupGolfSpotAdvice() def __exitGolfSpot(self): """Handle the toon clicking on exit button.""" self.d_requestFree(False) def __controlPressed(self): """Handle control key being pressed.""" if self.controlKeyAllowed: self.__beginFireBall() pass #self.__cleanupMagnetAdvice() #self.__turnOnMagnet() def __controlReleased(self): """Handle control key being released.""" if self.controlKeyAllowed: self.__endFireBall() def __upArrow(self, pressed): """Handle up arrow key being pressed.""" self.__incrementChangeSeq() self.__cleanupGolfSpotAdvice() if pressed: self.arrowVert = 1 elif self.arrowVert > 0: self.arrowVert = 0 def __downArrow(self, pressed): """Handle down arrow key being pressed.""" self.__incrementChangeSeq() self.__cleanupGolfSpotAdvice() if pressed: self.arrowVert = -1 elif self.arrowVert < 0: self.arrowVert = 0 def __rightArrow(self, pressed): """Handle right arrow key being pressed.""" self.__incrementChangeSeq() self.__cleanupGolfSpotAdvice() if pressed: self.arrowHorz = 1 self.switchToAnimState('GolfRotateLeft') elif self.arrowHorz > 0: self.arrowHorz = 0 self.switchToAnimState('GolfPuttLoop') def __leftArrow(self, pressed): """Handle left arrow key being pressed.""" self.__incrementChangeSeq() self.__cleanupGolfSpotAdvice() if pressed: self.arrowHorz = -1 self.switchToAnimState('GolfRotateRight') elif self.arrowHorz < 0: self.arrowHorz = 0 self.switchToAnimState('GolfPuttLoop') def __watchControls(self, task): """Check the arrow key press and call move golfSpot if needed.""" if self.arrowHorz: self.__moveGolfSpot(self.arrowHorz) else: pass #self.__setMoveSound(None) return Task.cont def __moveGolfSpot(self, xd): """Rotate the golfSpot by the given xdelta.""" dt = globalClock.getDt() h = self.root.getH() - xd * self.rotateSpeed * dt h %= 360 limitH = h self.root.setH(limitH) #self.__setMoveSound(self.craneMoveSfx) def __incrementChangeSeq(self): """Increment our change counter.""" self.changeSeq = (self.changeSeq + 1) & 0xff def __beginFireBall(self): """Handle player pressing control and starting the power meter.""" # The control key was pressed. if self.aimStart != None: # This is probably just key-repeat. return if not self._state == 'Controlled': return if not self.avId == localAvatar.doId: return time = globalClock.getFrameTime() self.aimStart = time messenger.send('wakeup') taskMgr.add(self.__updateBallPower, self.ballPowerTaskName) def __endFireBall(self): """Handle player releasing control and shooting the ball.""" # The control key was released. Fire the ball. if self.aimStart == None: return if not self._state == 'Controlled': return if not self.avId == localAvatar.doId: return #if not self.power: # return taskMgr.remove(self.ballPowerTaskName) self.disableControlKey() messenger.send('wakeup') self.aimStart = None power = self.power angle = self.root.getH() self.notify.debug('incrementing self.__flyBallSequenceNum') self.__flyBallSequenceNum = (self.__flyBallSequenceNum + 1) % 0xff self.sendSwingInfo(power, angle, self.__flyBallSequenceNum) self.setSwingInfo(power, angle, self.__flyBallSequenceNum) self.resetPowerBar() pass #self.__turnOffMagnet() def __updateBallPower(self, task): """Change the value of the power meter.""" if not self.powerBar: print("### no power bar!!!") return task.done newPower = self.__getBallPower(globalClock.getFrameTime()) self.power = newPower self.powerBar['value'] = newPower return task.cont def __getBallPower(self, time): """Return a value between 0 and 100 to indicate golf power.""" 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 stopPosHprBroadcast(self): """Stop the pitcher rotation broadcast task.""" taskName = self.posHprBroadcastName taskMgr.remove(taskName) def startPosHprBroadcast(self): """Start the golfSpot rotation broadcast task.""" taskName = self.posHprBroadcastName # Broadcast our initial position self.b_clearSmoothing() self.d_sendGolfSpotPos() # remove any old tasks taskMgr.remove(taskName) taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) def __posHprBroadcast(self, task): """Periodically broadcast the golfSpot rotation.""" self.d_sendGolfSpotPos() taskName = self.posHprBroadcastName taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) return Task.done def d_sendGolfSpotPos(self): """Send the golfSpot rotation to the other clients.""" timestamp = globalClockDelta.getFrameNetworkTime() self.sendUpdate( 'setGolfSpotPos', [self.changeSeq, self.root.getH(), timestamp]) def setGolfSpotPos(self, changeSeq, h, timestamp): """Handle another client sending an update on the golfSpot rotation.""" #assert self.notify.debugStateCall(self) self.changeSeq = changeSeq if self.smoothStarted: now = globalClock.getFrameTime() local = globalClockDelta.networkToLocalTime(timestamp, now) #self.golfSpotSmoother.setY(y) self.golfSpotSmoother.setH(h) self.golfSpotSmoother.setTimestamp(local) self.golfSpotSmoother.markPosition() else: #self.crane.setY(y) self.root.setH(h) ### Handle smoothing of distributed updates. This is similar to ### code in DistributedSmoothNode, but streamlined for our ### purposes. def b_clearSmoothing(self): """Tell us and other clients to clear smoothing.""" self.d_clearSmoothing() self.clearSmoothing() def d_clearSmoothing(self): """Tell other clients to clear smoothing.""" self.sendUpdate("clearSmoothing", [0]) def clearSmoothing(self, bogus=None): """Invalidate old position reports.""" # Call this to invalidate all the old position reports # (e.g. just before popping to a new position). self.golfSpotSmoother.clearPositions(1) def doSmoothTask(self, task): """ This function updates the position of the node to its computed smoothed position. This may be overridden by a derived class to specialize the behavior. """ self.golfSpotSmoother.computeAndApplySmoothHpr(self.root) return Task.cont def startSmooth(self): """ This function starts the task that ensures the node is positioned correctly every frame. However, while the task is running, you won't be able to lerp the node or directly position it. """ if not self.smoothStarted: taskName = self.smoothName taskMgr.remove(taskName) self.reloadPosition() taskMgr.add(self.doSmoothTask, taskName) self.smoothStarted = 1 def stopSmooth(self): """ This function stops the task spawned by startSmooth(), and allows show code to move the node around directly. """ if self.smoothStarted: taskName = self.smoothName taskMgr.remove(taskName) self.forceToTruePosition() self.smoothStarted = 0 def makeToonReleaseInterval(self, toon): """Return an interval of the toon jumping to pitcher position.""" def getSlideToPos(toon=toon): return render.getRelativePoint(toon, Point3(0, -5, 0)) if self.gotHitByBoss: grabIval = Sequence(Func(self.detachClub), name='makeToonReleaseInterval-gotHitByBoss') if not toon.isEmpty(): toonIval = Sequence(Func(toon.wrtReparentTo, render), Parallel( ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1)), name='makeToonReleaseInterval-toonIval') #Func(toon.loop, 'neutral'), grabIval.append(toonIval) else: grabIval = Sequence(Func(self.detachClub), ) if not toon.isEmpty(): toonIval = Sequence( Parallel( ActorInterval(toon, 'walk', duration=1.0, playRate=-1.0), LerpPosInterval( toon, duration=1.0, pos=Point3(-10, 0, 0), )), Func(toon.wrtReparentTo, render), #Func(toon.loop, 'neutral'), ) grabIval.append(toonIval) if localAvatar.doId == toon.doId: if not self.goingToReward and toon.hp > 0: grabIval.append(Func(self.goToFinalBattle)) grabIval.append( Func(self.notify.debug, 'goingToFinalBattlemode')) grabIval.append(Func(self.safeBossToFinalBattleMode)) return grabIval def safeBossToFinalBattleMode(self): """Call boss.toFinalBattleMode if self.boss is valid.""" if self.boss: self.boss.toFinalBattleMode() def goToFinalBattle(self): """Go to final battle if we're in crane mode.""" # This is a bit hacky. Go back to finalBattle mode, but # only if we're still in crane mode. (We might have been # zapped to 'ouch' mode by a hit.) if self.cr: place = self.cr.playGame.getPlace() if place and hasattr(place, 'fsm'): curState = place.fsm.getCurrentState().getName() if place.fsm.getCurrentState().getName() == 'crane': place.setState('finalBattle') else: self.notify.debug('NOT going to final battle, state=%s' % curState) def attachClub(self, avId, pointToBall=False): """Attach the club to the right hand.""" club = self.club 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) # we have to account for small toons like the mouse 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) # self.notify.debug('after lookat, hpr = %s' % club.getHpr()) def detachClub(self): """Detach the club and store it someplace safe.""" if not self.club.isEmpty(): self.club.reparentTo(self.root) self.club.setZ(-20) self.club.setScale(1) def adjustClub(self): """Change the club so that the head is more or less behind the ball.""" club = self.club if club: distance = club.getDistance(self.clubLookatSpot) # from maya the club has a length of 2.058, scaleFactor = distance / 2.058 # self.notify.debug('scaleFactor = %s' % scaleFactor) club.setScale(1, scaleFactor, 1) def startAdjustClubTask(self): """Start the task to automatically adjust the club so it looks right.""" taskMgr.add(self.adjustClubTask, self.adjustClubTaskName) def stopAdjustClubTask(self): """Stop the task to automatically adjust the club so it looks right.""" taskMgr.remove(self.adjustClubTaskName) def adjustClubTask(self, task): """Continuously adjust the club so the head is behind the ball.""" self.attachClub(self.avId, True) self.adjustClub() return task.cont def enableControlKey(self): """Allow control key events to come in.""" self.controlKeyAllowed = True def disableControlKey(self): """Disable control key events from coming in.""" self.controlKeyAllowed = False def sendSwingInfo(self, power, angle, sequenceNum): """Tell the other clients we're firing.""" self.sendUpdate('setSwingInfo', [power, angle, sequenceNum]) def startBallPlayback(self, power, angle, sequenceNum): """Start the ball flying in the air.""" assert self.notify.debugStateCall(self) # duplicate our current ball model flyBall = self.ballModel.copyTo(NodePath()) flyBall.setScale(1.0) flyBallBubble = self.getFlyBallBubble().instanceTo(NodePath()) flyBallBubble.reparentTo(flyBall) flyBall.setTag('pieSequence', str(sequenceNum)) flyBall.setTag('throwerId', str(self.avId)) # First, create a ProjectileInterval to compute the relative # velocity. t = power / 100.0 # make the ball travel farther, the longer you press the bar t = 1.0 - t # Distance ranges from 300 - 100 ft, time ranges from 1.5 - 2 s. dist = 300 - 200 * t time = 1.5 + 0.5 * t proj = ProjectileInterval( None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time, ) relVel = proj.startVel def getVelocity(root=self.root, relVel=relVel): return render.getRelativeVector(root, relVel) fly = Sequence( #Func(self.ballModel.hide), Func(flyBall.reparentTo, render), Func(flyBall.setPosHpr, self.root, 0, 0, 0, 0, 0, 0), Func(base.cTrav.addCollider, flyBallBubble, self.flyBallHandler), ProjectileInterval(flyBall, startVel=getVelocity, duration=3), Func(flyBall.detachNode), Func(base.cTrav.removeCollider, flyBallBubble), Func(self.notify.debug, "removed collider"), Func(self.flyBallFinishedFlying, sequenceNum)) flyWithSound = Parallel(fly, SoundInterval(self.hitBallSfx, node=self.root), name='flyWithSound') self.notify.debug('starting flyball track') flyWithSound.start() self.flyBallTracks[sequenceNum] = flyWithSound pass def setSwingInfo(self, power, angle, sequenceNum): """Handle a toon swinging at the golf ball.""" assert self.notify.debugStateCall(self) av = base.cr.doId2do.get(self.avId) self.swingInterval = Sequence() if av: self.stopAdjustClubTask() self.swingInterval = Sequence( ActorInterval(av, 'swing-putt', startFrame=0, endFrame=GolfGlobals.BALL_CONTACT_FRAME), Func(self.startBallPlayback, power, angle, sequenceNum), Func(self.ballModel.hide), ActorInterval(av, 'swing-putt', startFrame=GolfGlobals.BALL_CONTACT_FRAME, endFrame=24), Func(self.ballModel.setScale, 0.1), Func(self.ballModel.show), LerpScaleInterval(self.ballModel, 1.0, Point3(1, 1, 1)), Func(self.enableControlKey), ) if av == localAvatar: self.swingInterval.append( Func(self.switchToAnimState, 'GolfPuttLoop', True)) self.swingInterval.start() def getFlyBallBubble(self): if self.__flyBallBubble == None: bubble = CollisionSphere(0, 0, 0, GolfGlobals.GOLF_BALL_RADIUS) node = CollisionNode('flyBallBubble') node.addSolid(bubble) node.setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) node.setIntoCollideMask(BitMask32.allOff()) self.__flyBallBubble = NodePath(node) self.flyBallHandler = CollisionHandlerEvent() self.flyBallHandler.addInPattern('flyBallHit-%d' % self.index) #self.flyBallHandler.addInPattern('flyBallHit-%d--%in') return self.__flyBallBubble def __flyBallHit(self, entry): """Handle the flying golf ball hitting something in the world.""" #import pdb; pdb.set_trace() print(entry) pass def flyBallFinishedFlying(self, sequence): """Handle the flyball sequence finishing.""" if sequence in self.flyBallTracks: del self.flyBallTracks[sequence] def __finishFlyBallTrack(self, sequence): """Force the flyball sequence to finish prematurely.""" if sequence in self.flyBallTracks: flyBallTrack = self.flyBallTracks[sequence] del self.flyBallTracks[sequence] flyBallTrack.finish() def flyBallFinishedSplatting(self, sequence): """Handle the flyball splatt finishing.""" if sequence in self.splatTracks: del self.splatTracks[sequence] def __flyBallHit(self, entry): """Handle the flyball colliding against something in the world.""" if not entry.hasSurfacePoint() or not entry.hasInto(): # Not a collision solid we understand. Weird. return if not entry.getInto().isTangible(): # Just a trigger polygon. Ignore it. return sequence = int(entry.getFromNodePath().getNetTag('pieSequence')) self.__finishFlyBallTrack(sequence) if sequence in self.splatTracks: splatTrack = self.splatTracks[sequence] del self.splatTracks[sequence] splatTrack.finish() # The pie hit something solid. Generate a distributed splat. # Check the thing we hit for a pieCode. If it has one, it # gets passed along with the message. This may mean something # different according to context (it may indicate, for # instance, the kind of target we hit). flyBallCode = 0 flyBallCodeStr = entry.getIntoNodePath().getNetTag('pieCode') if flyBallCodeStr: flyBallCode = int(flyBallCodeStr) pos = entry.getSurfacePoint(render) timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) throwerId = int(entry.getFromNodePath().getNetTag('throwerId')) splat = self.getFlyBallSplatInterval(pos[0], pos[1], pos[2], flyBallCode, throwerId) splat = Sequence(splat, Func(self.flyBallFinishedSplatting, sequence)) assert sequence not in self.splatTracks self.splatTracks[sequence] = splat splat.start() self.notify.debug( 'doId=%d into=%s flyBallCode=%d, throwerId=%d' % (self.doId, entry.getIntoNodePath(), flyBallCode, throwerId)) if flyBallCode == ToontownGlobals.PieCodeBossCog and \ self.avId == localAvatar.doId and \ self.lastHitSequenceNum != self.__flyBallSequenceNum: self.lastHitSequenceNum = self.__flyBallSequenceNum self.boss.d_ballHitBoss(1) elif flyBallCode == ToontownGlobals.PieCodeToon and \ self.avId == localAvatar.doId and \ self.lastHitSequenceNum != self.__flyBallSequenceNum: self.lastHitSequenceNum = self.__flyBallSequenceNum avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') if avatarDoId == '': self.notify.warning("Toon %s has no avatarDoId tag." % (repr(entry.getIntoNodePath()))) return doId = int(avatarDoId) if doId != localAvatar.doId: # turn this off since food comes out of the belt # self.boss.d_hitToon(doId) pass def getFlyBallSplatInterval(self, x, y, z, flyBallCode, throwerId): """Return an interval showing a flyBall hitting a target in the world.""" from toontown.toonbase import ToontownBattleGlobals from toontown.battle import BattleProps splatName = 'dust' splat = BattleProps.globalPropPool.getProp(splatName) splat.setBillboardPointWorld(2) color = ToontownGlobals.PieCodeColors.get(flyBallCode) if color: splat.setColor(*color) if flyBallCode == ToontownGlobals.PieCodeBossCog: self.notify.debug('changing color to %s' % self.ballColor) splat.setColor(self.ballColor) sound = loader.loadSfx('phase_11/audio/sfx/LB_evidence_miss.mp3') vol = 1.0 if flyBallCode == ToontownGlobals.PieCodeBossCog: sound = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.mp3') soundIval = SoundInterval(sound, node=splat, volume=vol) if flyBallCode == ToontownGlobals.PieCodeBossCog and \ localAvatar.doId == throwerId: vol = 1.0 soundIval = SoundInterval(sound, node=localAvatar, volume=vol) ival = Parallel( Func(splat.reparentTo, render), Func(splat.setPos, x, y, z), soundIval, Sequence(ActorInterval(splat, splatName), Func(splat.detachNode)), ) return ival def setGoingToReward(self): """Flag that the boss battle is going to the reward state.""" assert self.notify.debugStateCall(self) self.goingToReward = True def gotBossZapped(self): """Handle the local toon getting hit by a ranged attack.""" self.showExiting() self.d_requestFree(True)