912 lines
37 KiB
Python
912 lines
37 KiB
Python
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)
|