toontown-just-works/toontown/minigame/DistributedRingGame.py

913 lines
37 KiB
Python
Raw Permalink Normal View History

2024-07-07 18:08:39 -05:00
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
import ArrowKeys
import Ring
import RingTrack
import RingGameGlobals
import RingGroup
import RingTrackGroups
from toontown.toonbase import ToontownGlobals
from toontown.toonbase import TTLocalizer
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.loadMusic('phase_4/audio/bgm/MG_toontag.ogg')
self.sndAmbience = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg')
self.sndPerfect = base.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.setMinFov(80/(4./3.))
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 xrange(0, 2):
instance = self.environModel.instanceUnderNode(self.environNode, 'model')
y = self.ENVIRON_LENGTH * i
instance.setY(y)
self.environBlocks.append(instance)
for j in xrange(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 xrange(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 xrange(0, self.numPlayers):
self.sndTable['gotRing'][i] = base.loadSfx('phase_4/audio/sfx/ring_get.ogg')
self.sndTable['missedRing'][i] = base.loadSfx('phase_4/audio/sfx/ring_miss.ogg')
self.__addToonDropShadow(self.getAvatar(self.localAvId))
self.__spawnUpdateEnvironTask()
self.__spawnUpdateShadowsTask()
self.__spawnUpdateLocalToonTask()
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.setMinFov(settings['fov']/(4./3.))
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')
base.playMusic(self.music, looping=0, volume=0.8)
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(base.a2dBottomRight)
self.colorRing.setTwoSided(0)
self.colorRing.setPos(-0.143, 10, 0.14)
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 xrange(0, self.__numRingGroups):
self.__createTallyMarker(i, self.RT_UNKNOWN)
return
def __destroyTallyDisplay(self):
for i in xrange(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 = base.a2dBottomLeft.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(0.333 + xSpacing * index, 0, 0.07 + 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 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 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 xrange(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 xrange(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 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 xrange(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 xrange(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)