from panda3d.core import * from toontown.toonbase.ToonBaseGlobal import * from direct.interval.IntervalGlobal import * from .DistributedMinigame import * from direct.distributed.ClockDelta import * from direct.fsm import ClassicFSM, State from direct.fsm import State from direct.task import Task from . import ArrowKeys from . import Ring from . import RingTrack from . import RingGameGlobals from . import RingGroup from . import RingTrackGroups from toontown.toonbase import ToontownGlobals from toontown.toonbase import TTLocalizer from functools import reduce class DistributedRingGame(DistributedMinigame): UPDATE_ENVIRON_TASK = 'RingGameUpdateEnvironTask' UPDATE_LOCALTOON_TASK = 'RingGameUpdateLocalToonTask' UPDATE_RINGS_TASK = 'RingGameUpdateRingsTask' UPDATE_SHADOWS_TASK = 'RingGameUpdateShadowsTask' COLLISION_DETECTION_TASK = 'RingGameCollisionDetectionTask' END_GAME_WAIT_TASK = 'RingGameCollisionDetectionTask' COLLISION_DETECTION_PRIORITY = 5 UPDATE_SHADOWS_PRIORITY = 47 RT_UNKNOWN = 0 RT_SUCCESS = 1 RT_GROUPSUCCESS = 2 RT_FAILURE = 3 def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM('DistributedRingGame', [State.State('off', self.enterOff, self.exitOff, ['swim']), State.State('swim', self.enterSwim, self.exitSwim, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') self.addChildGameFSM(self.gameFSM) def getTitle(self): return TTLocalizer.RingGameTitle def getInstructions(self): p = self.avIdList.index(self.localAvId) if self.isSinglePlayer(): text = TTLocalizer.RingGameInstructionsSinglePlayer else: text = TTLocalizer.RingGameInstructionsMultiPlayer return text % RingGameGlobals.ringColors[self.colorIndices[p]][0] def getMaxDuration(self): return RingGameGlobals.NUM_RING_GROUPS * self.ringGroupArrivalPeriod + self.T_FIRST_RING_GROUP_ARRIVES + self.GAME_END_DELAY def defineConstants(self): self.CAMERA_Y = -15 self.TOON_Y = 0 self.FAR_PLANE_DIST = 150 tScreenCenterToEdge = 1.0 self.TOONXZ_SPEED = RingGameGlobals.MAX_TOONXZ / tScreenCenterToEdge self.WATER_DEPTH = 35.0 self.ENVIRON_LENGTH = 150.0 self.ENVIRON_START_OFFSET = 20.0 self.TOON_INITIAL_SPACING = 4.0 waterZOffset = 3.0 self.SEA_FLOOR_Z = -self.WATER_DEPTH / 2.0 + waterZOffset farPlaneDist = self.CAMERA_Y + self.FAR_PLANE_DIST - self.TOON_Y self.ringGroupArrivalPeriod = 3.0 self.RING_GROUP_SPACING = farPlaneDist / 2.0 self.TOON_SWIM_VEL = self.RING_GROUP_SPACING / self.ringGroupArrivalPeriod self.T_FIRST_RING_GROUP_ARRIVES = farPlaneDist / self.TOON_SWIM_VEL self.WATER_COLOR = Vec4(0, 0, 0.6, 1) self.SHADOW_Z_OFFSET = 0.1 self.Y_VIS_MAX = self.FAR_PLANE_DIST self.Y_VIS_MIN = self.CAMERA_Y ringRadius = RingGameGlobals.RING_RADIUS * 1.025 self.RING_RADIUS_SQRD = ringRadius * ringRadius self.GAME_END_DELAY = 1.0 self.RING_RESPONSE_DELAY = 0.1 self.TOON_LOD = 1000 self.NumRingGroups = 16 def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.defineConstants() self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') self.sndAmbience = base.loader.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') self.sndPerfect = base.loader.loadSfx('phase_4/audio/sfx/ring_perfect.ogg') loadBase = 'phase_4/models/minigames/' self.environModel = loader.loadModel(loadBase + 'swimming_game.bam') self.environModel.setPos(0, self.ENVIRON_LENGTH / 2.0, self.SEA_FLOOR_Z) self.environModel.flattenMedium() self.ringModel = loader.loadModel(loadBase + 'swimming_game_ring.bam') self.ringModel.setTransparency(1) modelRadius = 4.0 self.ringModel.setScale(RingGameGlobals.RING_RADIUS / modelRadius) self.ringModel.flattenMedium() self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') self.dropShadowModel.setColor(0, 0, 0, 0.5) self.dropShadowModel.flattenMedium() self.toonDropShadows = [] self.ringDropShadows = [] self.__textGen = TextNode('ringGame') self.__textGen.setFont(ToontownGlobals.getSignFont()) self.__textGen.setAlign(TextNode.ACenter) def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) del self.__textGen del self.toonDropShadows del self.ringDropShadows self.dropShadowModel.removeNode() del self.dropShadowModel self.environModel.removeNode() del self.environModel self.ringModel.removeNode() del self.ringModel del self.music del self.sndAmbience del self.sndPerfect self.removeChildGameFSM(self.gameFSM) del self.gameFSM def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.arrowKeys = ArrowKeys.ArrowKeys() toon = base.localAvatar toon.reparentTo(render) toon.setAnimState('swim', 1.0) toon.stopBobSwimTask() toon.useLOD(self.TOON_LOD) self.__placeToon(self.localAvId) toon.dropShadow.hide() camera.reparentTo(render) camera.reparentTo(base.localAvatar) camera.setPosHpr(0, self.CAMERA_Y + self.TOON_Y, 0, 0, 0, 0) base.camLens.setFov(80) base.camLens.setFar(self.FAR_PLANE_DIST) base.setBackgroundColor(self.WATER_COLOR) self.__fog = Fog('ringGameFog') if base.wantFog: self.__fog.setColor(self.WATER_COLOR) self.__fog.setLinearRange(0.1, self.FAR_PLANE_DIST - 1.0) render.setFog(self.__fog) self.environNode = render.attachNewNode('environNode') self.environBlocks = [] for i in range(0, 2): instance = self.environModel.instanceUnderNode(self.environNode, 'model') y = self.ENVIRON_LENGTH * i instance.setY(y) self.environBlocks.append(instance) for j in range(0, 2): instance = self.environModel.instanceUnderNode(self.environNode, 'blocks') x = self.ENVIRON_LENGTH * (j + 1) instance.setY(y) instance.setX(-x) self.environBlocks.append(instance) for j in range(0, 2): instance = self.environModel.instanceUnderNode(self.environNode, 'blocks') x = self.ENVIRON_LENGTH * (j + 1) instance.setY(y) instance.setX(x) self.environBlocks.append(instance) self.ringNode = render.attachNewNode('ringNode') self.sndTable = {'gotRing': [None] * self.numPlayers, 'missedRing': [None] * self.numPlayers} for i in range(0, self.numPlayers): self.sndTable['gotRing'][i] = base.loader.loadSfx('phase_4/audio/sfx/ring_get.ogg') self.sndTable['missedRing'][i] = base.loader.loadSfx('phase_4/audio/sfx/ring_miss.ogg') self.__addToonDropShadow(self.getAvatar(self.localAvId)) self.__spawnUpdateEnvironTask() self.__spawnUpdateShadowsTask() self.__spawnUpdateLocalToonTask() base.playMusic(self.music, looping=0, volume=0.8) if None != self.sndAmbience: base.playSfx(self.sndAmbience, looping=1, volume=0.8) return def offstage(self): self.notify.debug('offstage') DistributedMinigame.offstage(self) self.music.stop() if None != self.sndAmbience: self.sndAmbience.stop() self.__killUpdateLocalToonTask() self.__killUpdateShadowsTask() self.__killUpdateEnvironTask() del self.sndTable self.__removeAllToonDropShadows() render.clearFog() base.camLens.setFar(ToontownGlobals.DefaultCameraFar) base.camLens.setFov(ToontownGlobals.DefaultCameraFov) base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) self.arrowKeys.destroy() del self.arrowKeys for block in self.environBlocks: del block self.environNode.removeNode() del self.environNode self.ringNode.removeNode() del self.ringNode for avId in self.avIdList: av = self.getAvatar(avId) if av: av.dropShadow.show() av.resetLOD() av.setAnimState('neutral', 1.0) return def handleDisabledAvatar(self, avId): self.notify.debug('handleDisabledAvatar') self.notify.debug('avatar ' + str(avId) + ' disabled') self.__removeToonDropShadow(self.remoteToons[avId]) DistributedMinigame.handleDisabledAvatar(self, avId) def __genText(self, text): self.__textGen.setText(text) return self.__textGen.generate() def __placeToon(self, avId): toon = self.getAvatar(avId) i = self.avIdList.index(avId) numToons = float(self.numPlayers) x = i * self.TOON_INITIAL_SPACING x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 toon.setPosHpr(x, self.TOON_Y, 0, 0, 0, 0) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return if not self.isSinglePlayer(): base.localAvatar.collisionsOff() cSphere = CollisionSphere(0.0, 0.0, 0.0, RingGameGlobals.CollisionRadius) cSphereNode = CollisionNode('RingGameSphere-%s' % self.localAvId) cSphereNode.addSolid(cSphere) cSphereNode.setFromCollideMask(RingGameGlobals.CollideMask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.cSphereNodePath = base.localAvatar.attachNewNode(cSphereNode) self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.cSphereNodePath, base.localAvatar) self.pusher.setHorizontal(0) self.cTrav = CollisionTraverser('DistributedRingGame') self.cTrav.addCollider(self.cSphereNodePath, self.pusher) self.remoteToonCollNPs = {} for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: cSphere = CollisionSphere(0.0, 0.0, 0.0, RingGameGlobals.CollisionRadius) cSphereNode = CollisionNode('RingGameSphere-%s' % avId) cSphereNode.addSolid(cSphere) cSphereNode.setCollideMask(RingGameGlobals.CollideMask) cSphereNP = toon.attachNewNode(cSphereNode) self.remoteToonCollNPs[avId] = cSphereNP for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toon.setAnimState('swim', 1.0) toon.stopBobSwimTask() toon.useLOD(self.TOON_LOD) toon.dropShadow.hide() self.__addToonDropShadow(toon) toon.startSmooth() self.remoteToons = {} for avId in self.remoteAvIdList: toon = self.getAvatar(avId) self.remoteToons[avId] = toon self.__nextRingGroupResultIndex = 0 def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) self.gameFSM.request('swim') def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterSwim(self): self.notify.debug('enterSwim') self.__ringTimeBase = self.gameStartTime self.__numRingGroups = RingGameGlobals.NUM_RING_GROUPS self.__ringGroupsPassed = 0 self.__generateRings() self.__spawnUpdateRingsTask() self.__spawnCollisionDetectionTask() self.__ringTracks = [] self.colorRing = self.ringModel.copyTo(hidden) self.colorRing.reparentTo(aspect2d) self.colorRing.setTwoSided(0) self.colorRing.setPos(1.19, 10, -0.86) self.colorRing.setScale(0.04) p = self.avIdList.index(self.localAvId) self.colorRing.setColor(RingGameGlobals.ringColors[self.colorIndices[p]][1]) self.resultTable = [self.RT_UNKNOWN] * self.__numRingGroups self.__initTallyDisplay() def __initTallyDisplay(self): self.__tallyTextNode = TextNode('tally') self.__tallyTextNode.setFont(ToontownGlobals.getSignFont()) self.__tallyTextNode.setAlign(TextNode.ACenter) self.tallyMarkers = [None] * self.__numRingGroups for i in range(0, self.__numRingGroups): self.__createTallyMarker(i, self.RT_UNKNOWN) return def __destroyTallyDisplay(self): for i in range(0, self.__numRingGroups): self.__deleteTallyMarker(i) del self.tallyMarkers del self.__tallyTextNode def __createTallyMarker(self, index, result): chars = '-OOX' colors = (Point4(0.8, 0.8, 0.8, 1), Point4(0, 1, 0, 1), Point4(1, 1, 0, 1), Point4(1, 0, 0, 1)) self.__deleteTallyMarker(index) self.__tallyTextNode.setText(chars[result]) node = self.__tallyTextNode.generate() tallyText = aspect2d.attachNewNode(node) tallyText.setColor(colors[result]) tallyText.setScale(0.1) zOffset = 0 if result == self.RT_UNKNOWN: zOffset = 0.015 xSpacing = 0.085 tallyText.setPos(-1.0 + xSpacing * index, 0, -.93 + zOffset) self.tallyMarkers[index] = tallyText def __deleteTallyMarker(self, index): marker = self.tallyMarkers[index] if None != marker: marker.removeNode() self.tallyMarkers[index] = None return def __updateTallyDisplay(self, index): self.__createTallyMarker(index, self.resultTable[index]) def __generateRings(self): self.ringGroups = [] difficultyDistributions = {ToontownGlobals.ToontownCentral: [14, 2, 0], ToontownGlobals.DonaldsDock: [10, 6, 0], ToontownGlobals.DaisyGardens: [4, 12, 0], ToontownGlobals.MinniesMelodyland: [4, 8, 4], ToontownGlobals.TheBrrrgh: [4, 6, 6], ToontownGlobals.DonaldsDreamland: [2, 6, 8]} for distr in list(difficultyDistributions.values()): sum = reduce(lambda x, y: x + y, distr) difficultyPatterns = {ToontownGlobals.ToontownCentral: [[0] * 14 + [1] * 2 + [2] * 0, [0, 0, 0, 0, 0, 0, 0, 1] * 2, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1]], ToontownGlobals.DonaldsDock: [[0] * 10 + [1] * 6 + [2] * 0, [0, 0, 0, 0, 0, 1, 1, 1] * 2, [0, 0, 0, 1, 0, 0, 1, 1] * 2], ToontownGlobals.DaisyGardens: [[0] * 4 + [1] * 12 + [2] * 0, [0, 0, 1, 1, 1, 1, 1, 1] * 2, [0, 1, 1, 1, 0, 1, 1, 1] * 2], ToontownGlobals.MinniesMelodyland: [[0] * 4 + [1] * 8 + [2] * 4, [0, 0, 1, 1, 1, 1, 2, 2] * 2, [0, 1, 1, 1, 1, 0, 2, 2] * 2, [0, 1, 1, 2, 0, 1, 1, 2] * 2, [0, 1, 2, 1, 0, 1, 2, 1] * 2], ToontownGlobals.TheBrrrgh: [[0] * 4 + [1] * 6 + [2] * 6, [0, 0, 1, 1, 1, 2, 2, 2] * 2, [0, 1, 1, 1, 0, 2, 2, 2] * 2, [0, 1, 1, 2, 0, 1, 2, 2] * 2, [0, 1, 2, 1, 0, 1, 2, 2] * 2], ToontownGlobals.DonaldsDreamland: [[0] * 2 + [1] * 6 + [2] * 8, [0, 1, 1, 1, 2, 2, 2, 2] * 2, [0, 1, 1, 2, 2, 1, 2, 2] * 2, [0, 1, 2, 1, 2, 1, 2, 2] * 2]} safezone = self.getSafezoneId() numGroupsPerDifficulty = difficultyDistributions[safezone] def patternsAreValid(difficultyPatterns = difficultyPatterns, difficultyDistributions = difficultyDistributions): for sz in list(difficultyPatterns.keys()): for pattern in difficultyPatterns[sz]: for difficulty in [0, 1, 2]: numGroupsPerDifficulty = difficultyDistributions[sz] if numGroupsPerDifficulty[difficulty] != pattern.count(difficulty): print('safezone:', sz) print('pattern:', pattern) print('difficulty:', difficulty) print('expected %s %ss, found %s' % (numGroupsPerDifficulty[difficulty], difficulty, pattern.count(difficulty))) return 0 return 1 pattern = self.randomNumGen.choice(difficultyPatterns[self.getSafezoneId()]) for i in range(0, self.__numRingGroups): numRings = self.numPlayers trackGroup = RingTrackGroups.getRandomRingTrackGroup(pattern[i], numRings, self.randomNumGen) ringGroup = RingGroup.RingGroup(trackGroup, self.ringModel, RingGameGlobals.MAX_TOONXZ, self.colorIndices) for r in range(numRings): self.__addRingDropShadow(ringGroup.getRing(r)) self.ringGroups.append(ringGroup) ringGroup.reparentTo(self.ringNode) firstGroupOffset = self.TOON_Y + self.T_FIRST_RING_GROUP_ARRIVES * self.TOON_SWIM_VEL ringGroup.setY(i * self.RING_GROUP_SPACING + firstGroupOffset) def __destroyRings(self): for group in self.ringGroups: group.delete() group.removeNode() self.__removeAllRingDropShadows() del self.ringGroups def __spawnUpdateLocalToonTask(self): self.__initPosBroadcast() taskMgr.remove(self.UPDATE_LOCALTOON_TASK) taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK) def __killUpdateLocalToonTask(self): taskMgr.remove(self.UPDATE_LOCALTOON_TASK) def __initPosBroadcast(self): self.__posBroadcastPeriod = 0.2 self.__timeSinceLastPosBroadcast = 0.0 self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() self.__storeStop = 0 lt = self.getAvatar(self.localAvId) lt.d_clearSmoothing() lt.sendCurrentPosition() def __posBroadcast(self, dt): self.__timeSinceLastPosBroadcast += dt if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() def __updateLocalToonTask(self, task): dt = globalClock.getDt() toonPos = self.getAvatar(self.localAvId).getPos() pos = [toonPos[0], 0, toonPos[2]] xVel = 0.0 if self.arrowKeys.leftPressed(): xVel -= self.TOONXZ_SPEED if self.arrowKeys.rightPressed(): xVel += self.TOONXZ_SPEED pos[0] += xVel * dt if pos[0] < -RingGameGlobals.MAX_TOONXZ: pos[0] = -RingGameGlobals.MAX_TOONXZ if pos[0] > RingGameGlobals.MAX_TOONXZ: pos[0] = RingGameGlobals.MAX_TOONXZ zVel = 0.0 if self.arrowKeys.upPressed(): zVel += self.TOONXZ_SPEED if self.arrowKeys.downPressed(): zVel -= self.TOONXZ_SPEED pos[2] += zVel * dt if pos[2] < -RingGameGlobals.MAX_TOONXZ: pos[2] = -RingGameGlobals.MAX_TOONXZ if pos[2] > RingGameGlobals.MAX_TOONXZ: pos[2] = RingGameGlobals.MAX_TOONXZ self.getAvatar(self.localAvId).setPos(pos[0], self.TOON_Y, pos[2]) if hasattr(self, 'cTrav'): self.cTrav.traverse(render) self.__posBroadcast(dt) return Task.cont def exitSwim(self): for track in self.__ringTracks: track.finish() del self.__ringTracks self.colorRing.removeNode() del self.colorRing self.__destroyTallyDisplay() del self.resultTable taskMgr.remove(self.END_GAME_WAIT_TASK) self.__killUpdateRingsTask() self.__killCollisionDetectionTask() self.__destroyRings() def enterCleanup(self): self.notify.debug('enterCleanup') if not self.isSinglePlayer(): for np in list(self.remoteToonCollNPs.values()): np.removeNode() del self.remoteToonCollNPs self.cSphereNodePath.removeNode() del self.cSphereNodePath del self.pusher del self.cTrav base.localAvatar.collisionsOn() def exitCleanup(self): pass def __addDropShadow_INTERNAL(self, object, scale_x, scale_y, scale_z, list): shadow = self.dropShadowModel.copyTo(render) shadow.setPos(0, self.CAMERA_Y, -100) shadow.setScale(scale_x, scale_y, scale_z) list.append([shadow, object]) def __removeDropShadow_INTERNAL(self, object, list): for i in range(len(list)): entry = list[i] if entry[1] == object: entry[0].removeNode() list.pop(i) return self.notify.warning('parent object ' + str(object) + ' not found in drop shadow list!') def __addToonDropShadow(self, object): self.__addDropShadow_INTERNAL(object, 0.5, 0.5, 0.5, self.toonDropShadows) def __removeToonDropShadow(self, object): self.__removeDropShadow_INTERNAL(object, self.toonDropShadows) def __addRingDropShadow(self, object): self.__addDropShadow_INTERNAL(object, 1.2, 0.31, 1.0, self.ringDropShadows) def __removeRingDropShadow(self, object): self.__removeDropShadow_INTERNAL(object, self.ringDropShadows) def __removeAllToonDropShadows(self): for entry in self.toonDropShadows: entry[0].removeNode() self.toonDropShadows = [] def __removeAllRingDropShadows(self): for entry in self.ringDropShadows: entry[0].removeNode() self.ringDropShadows = [] def __spawnUpdateShadowsTask(self): taskMgr.remove(self.UPDATE_SHADOWS_TASK) taskMgr.add(self.__updateShadowsTask, self.UPDATE_SHADOWS_TASK, priority=self.UPDATE_SHADOWS_PRIORITY) def __killUpdateShadowsTask(self): taskMgr.remove(self.UPDATE_SHADOWS_TASK) def __updateShadowsTask(self, task): list = self.toonDropShadows + self.ringDropShadows for entry in list: object = entry[1] y = object.getY(render) if y > self.Y_VIS_MAX: continue x = object.getX(render) z = self.SEA_FLOOR_Z + self.SHADOW_Z_OFFSET shadow = entry[0] shadow.setPos(x, y, z) return Task.cont def __spawnUpdateEnvironTask(self): taskMgr.remove(self.UPDATE_ENVIRON_TASK) taskMgr.add(self.__updateEnvironTask, self.UPDATE_ENVIRON_TASK) def __killUpdateEnvironTask(self): taskMgr.remove(self.UPDATE_ENVIRON_TASK) def __updateEnvironTask(self, task): t = globalClock.getFrameTime() - self.__timeBase distance = t * self.TOON_SWIM_VEL distance %= self.ENVIRON_LENGTH distance += self.ENVIRON_START_OFFSET self.environNode.setY(-distance) return Task.cont def __spawnUpdateRingsTask(self): taskMgr.remove(self.UPDATE_RINGS_TASK) taskMgr.add(self.__updateRingsTask, self.UPDATE_RINGS_TASK) def __killUpdateRingsTask(self): taskMgr.remove(self.UPDATE_RINGS_TASK) def __updateRingsTask(self, task): t = globalClock.getFrameTime() - self.__ringTimeBase distance = t * self.TOON_SWIM_VEL self.ringNode.setY(-distance) for ringGroup in self.ringGroups: groupY = ringGroup.getY(render) if groupY <= self.Y_VIS_MAX and groupY >= self.Y_VIS_MIN: ringGroup.setT(t) return Task.cont def __spawnCollisionDetectionTask(self): self.__ringGroupsPassed = 0 taskMgr.remove(self.COLLISION_DETECTION_TASK) taskMgr.add(self.__collisionDetectionTask, self.COLLISION_DETECTION_TASK, priority=self.COLLISION_DETECTION_PRIORITY) def __killCollisionDetectionTask(self): taskMgr.remove(self.COLLISION_DETECTION_TASK) def __makeRingSuccessFadeTrack(self, ring, duration, endScale, ringIndex): targetScale = Point3(endScale, endScale, endScale) dFade = 0.5 * duration dColorChange = duration - dFade origColor = ring.getColor() targetColor = Point4(1.0 - origColor[0], 1.0 - origColor[1], 1.0 - origColor[2], 1) def colorChangeFunc(t, ring = ring, targetColor = targetColor, origColor = origColor): newColor = targetColor * t + origColor * (1.0 - t) ring.setColor(newColor) def fadeFunc(t, ring = ring): ring.setColorScale(1, 1, 1, 1.0 - t) fadeAwayTrack = Parallel(Sequence(LerpFunctionInterval(colorChangeFunc, fromData=0.0, toData=1.0, duration=dColorChange), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=dFade)), LerpScaleInterval(ring, duration, targetScale)) successTrack = Sequence(Wait(self.RING_RESPONSE_DELAY), Parallel(SoundInterval(self.sndTable['gotRing'][ringIndex]), Sequence(Func(ring.wrtReparentTo, render), fadeAwayTrack, Func(self.__removeRingDropShadow, ring), Func(ring.reparentTo, hidden)))) return successTrack def __makeRingFailureFadeTrack(self, ring, duration, ringIndex): ts = 0.01 targetScale = Point3(ts, ts, ts) missedTextNode = self.__genText(TTLocalizer.RingGameMissed) missedText = hidden.attachNewNode(missedTextNode) ringColor = RingGameGlobals.ringColors[self.colorIndices[ringIndex]][1] def addMissedText(ring = ring, ringColor = ringColor, missedText = missedText): missedText.reparentTo(render) missedText.setPos(ring.getPos(render) + Point3(0, -1, 0)) missedText.setColor(ringColor) def removeMissedText(missedText = missedText): missedText.removeNode() missedText = None return failureTrack = Sequence(Wait(self.RING_RESPONSE_DELAY), Parallel(SoundInterval(self.sndTable['missedRing'][ringIndex]), Sequence(Func(ring.wrtReparentTo, render), Func(addMissedText), LerpScaleInterval(ring, duration, targetScale, blendType='easeIn'), Func(removeMissedText), Func(self.__removeRingDropShadow, ring), Func(ring.reparentTo, hidden)))) return failureTrack def __makeRingFadeAway(self, ring, success, ringIndex): if success: track = self.__makeRingSuccessFadeTrack(ring, 0.5, 2.0, ringIndex) else: track = self.__makeRingFailureFadeTrack(ring, 0.5, ringIndex) self.__ringTracks.append(track) track.start() def __collisionDetectionTask(self, task): nextRingGroupIndex = self.__ringGroupsPassed nextRingGroup = self.ringGroups[nextRingGroupIndex] if nextRingGroup.getY(render) < 0: groupIndex = nextRingGroupIndex gotRing = 0 ourRing = nextRingGroup.getRing(self.avIdList.index(self.localAvId)) ringPos = ourRing.getPos(render) localToonPos = base.localAvatar.getPos(render) distX = localToonPos[0] - ringPos[0] distZ = localToonPos[2] - ringPos[2] distSqrd = distX * distX + distZ * distZ if distSqrd <= self.RING_RADIUS_SQRD: gotRing = 1 self.__makeRingFadeAway(ourRing, gotRing, self.avIdList.index(self.localAvId)) if gotRing: self.resultTable[groupIndex] = self.RT_SUCCESS else: self.resultTable[groupIndex] = self.RT_FAILURE self.__updateTallyDisplay(groupIndex) self.sendUpdate('setToonGotRing', [gotRing]) if self.isSinglePlayer(): self.__processRingGroupResults([gotRing]) self.__ringGroupsPassed += 1 if self.__ringGroupsPassed >= self.__numRingGroups: self.__killCollisionDetectionTask() return Task.cont def __endGameDolater(self, task): self.gameOver() return Task.done def setTimeBase(self, timestamp): if not self.hasLocalToon: return self.__timeBase = globalClockDelta.networkToLocalTime(timestamp) def setColorIndices(self, a, b, c, d): if not self.hasLocalToon: return self.colorIndices = [a, b, c, d] def __getSuccessTrack(self, groupIndex): def makeSuccessTrack(text, holdDuration, fadeDuration = 0.5, perfect = 0, self = self): successText = hidden.attachNewNode(self.__genText(text)) successText.setScale(0.25) successText.setColor(1, 1, 0.5, 1) def fadeFunc(t, text): text.setColorScale(1, 1, 1, 1.0 - t) def destroyText(text): text.removeNode() text = None return track = Sequence(Func(successText.reparentTo, aspect2d), Wait(holdDuration), LerpFunctionInterval(fadeFunc, extraArgs=[successText], fromData=0.0, toData=1.0, duration=fadeDuration, blendType='easeIn'), Func(destroyText, successText)) if perfect: track = Parallel(track, SoundInterval(self.sndPerfect)) return track def isPerfect(list, goodValues): for value in list: if value not in goodValues: return 0 return 1 if groupIndex >= self.__numRingGroups - 1: if not self.isSinglePlayer(): if isPerfect(self.resultTable, [self.RT_GROUPSUCCESS]): return makeSuccessTrack(TTLocalizer.RingGameGroupPerfect, 1.5, perfect=1) if isPerfect(self.resultTable, [self.RT_SUCCESS, self.RT_GROUPSUCCESS]): return makeSuccessTrack(TTLocalizer.RingGamePerfect, 1.5, perfect=1) return Wait(1.0) if not self.isSinglePlayer(): if self.resultTable[groupIndex] == self.RT_GROUPSUCCESS: return makeSuccessTrack(TTLocalizer.RingGameGroupBonus, 0.0, fadeDuration=0.4) return None def __processRingGroupResults(self, results): groupIndex = self.__nextRingGroupResultIndex ringGroup = self.ringGroups[self.__nextRingGroupResultIndex] self.__nextRingGroupResultIndex += 1 for i in range(0, self.numPlayers): if self.avIdList[i] != self.localAvId: ring = ringGroup.getRing(i) self.__makeRingFadeAway(ring, results[i], i) if not self.isSinglePlayer(): if 0 not in results: self.notify.debug('Everyone got their rings!!') self.resultTable[groupIndex] = self.RT_GROUPSUCCESS self.__updateTallyDisplay(groupIndex) successTrack = self.__getSuccessTrack(groupIndex) endGameTrack = None if groupIndex >= self.__numRingGroups - 1: def endTheGame(self = self): if not RingGameGlobals.ENDLESS_GAME: taskMgr.doMethodLater(self.GAME_END_DELAY, self.__endGameDolater, self.END_GAME_WAIT_TASK) endGameTrack = Func(endTheGame) if None != successTrack or None != endGameTrack: track = Sequence() if None != successTrack: track.append(Wait(self.RING_RESPONSE_DELAY)) track.append(successTrack) if None != endGameTrack: track.append(endGameTrack) self.__ringTracks.append(track) track.start() return def setRingGroupResults(self, bitfield): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() != 'swim': return results = [] mask = 1 for avId in self.avIdList: results.append(not bitfield & mask) mask <<= 1 self.__processRingGroupResults(results)