960 lines
38 KiB
Python
960 lines
38 KiB
Python
from panda3d.core import *
|
|
from toontown.toonbase.ToonBaseGlobal import *
|
|
from DistributedMinigame import *
|
|
from direct.interval.IntervalGlobal import *
|
|
from OrthoWalk import *
|
|
from direct.showbase.PythonUtil import Functor, bound, lineupPos, lerp
|
|
from direct.fsm import ClassicFSM, State
|
|
from direct.fsm import State
|
|
from toontown.toonbase import TTLocalizer
|
|
import CatchGameGlobals
|
|
from direct.task.Task import Task
|
|
from toontown.toon import Toon
|
|
from toontown.suit import Suit
|
|
import MinigameAvatarScorePanel
|
|
from toontown.toonbase import ToontownTimer
|
|
from toontown.toonbase import ToontownGlobals
|
|
import CatchGameToonSD
|
|
import Trajectory
|
|
import math
|
|
from direct.distributed import DistributedSmoothNode
|
|
from direct.showbase.RandomNumGen import RandomNumGen
|
|
import MinigameGlobals
|
|
from toontown.toon import ToonDNA
|
|
from toontown.suit import SuitDNA
|
|
from CatchGameGlobals import DropObjectTypes
|
|
from CatchGameGlobals import Name2DropObjectType
|
|
from DropPlacer import *
|
|
from DropScheduler import *
|
|
|
|
class DistributedCatchGame(DistributedMinigame):
|
|
DropTaskName = 'dropSomething'
|
|
EndGameTaskName = 'endCatchGame'
|
|
SuitWalkTaskName = 'catchGameSuitWalk'
|
|
DropObjectPlurals = {'apple': TTLocalizer.CatchGameApples,
|
|
'orange': TTLocalizer.CatchGameOranges,
|
|
'pear': TTLocalizer.CatchGamePears,
|
|
'coconut': TTLocalizer.CatchGameCoconuts,
|
|
'watermelon': TTLocalizer.CatchGameWatermelons,
|
|
'pineapple': TTLocalizer.CatchGamePineapples,
|
|
'anvil': TTLocalizer.CatchGameAnvils}
|
|
|
|
def __init__(self, cr):
|
|
DistributedMinigame.__init__(self, cr)
|
|
self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup')
|
|
self.addChildGameFSM(self.gameFSM)
|
|
self.setUsesSmoothing()
|
|
self.setUsesLookAround()
|
|
|
|
def getTitle(self):
|
|
return TTLocalizer.CatchGameTitle
|
|
|
|
def getInstructions(self):
|
|
return TTLocalizer.CatchGameInstructions % {'fruit': self.DropObjectPlurals[self.fruitName],
|
|
'badThing': self.DropObjectPlurals['anvil']}
|
|
|
|
def getMaxDuration(self):
|
|
return CatchGameGlobals.GameDuration + 5
|
|
|
|
def load(self):
|
|
self.notify.debug('load')
|
|
DistributedMinigame.load(self)
|
|
self.defineConstants()
|
|
groundModels = ['phase_4/models/minigames/treehouse_2players',
|
|
'phase_4/models/minigames/treehouse_2players',
|
|
'phase_4/models/minigames/treehouse_3players',
|
|
'phase_4/models/minigames/treehouse_4players']
|
|
index = self.getNumPlayers() - 1
|
|
self.ground = loader.loadModel(groundModels[index])
|
|
self.ground.setHpr(180, -90, 0)
|
|
self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow')
|
|
self.dropObjModels = {}
|
|
for objType in DropObjectTypes:
|
|
if objType.name not in ['anvil', self.fruitName]:
|
|
continue
|
|
model = loader.loadModel(objType.modelPath)
|
|
self.dropObjModels[objType.name] = model
|
|
modelScales = {'apple': 0.7,
|
|
'orange': 0.7,
|
|
'pear': 0.5,
|
|
'coconut': 0.7,
|
|
'watermelon': 0.6,
|
|
'pineapple': 0.45}
|
|
if objType.name in modelScales:
|
|
model.setScale(modelScales[objType.name])
|
|
if objType == Name2DropObjectType['pear']:
|
|
model.setZ(-.6)
|
|
if objType == Name2DropObjectType['coconut']:
|
|
model.setP(180)
|
|
if objType == Name2DropObjectType['watermelon']:
|
|
model.setH(135)
|
|
model.setZ(-.5)
|
|
if objType == Name2DropObjectType['pineapple']:
|
|
model.setZ(-1.7)
|
|
if objType == Name2DropObjectType['anvil']:
|
|
model.setZ(-self.ObjRadius)
|
|
model.flattenMedium()
|
|
|
|
self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg')
|
|
self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg')
|
|
self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
|
|
self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg')
|
|
self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg')
|
|
self.toonSDs = {}
|
|
avId = self.localAvId
|
|
toonSD = CatchGameToonSD.CatchGameToonSD(avId, self)
|
|
self.toonSDs[avId] = toonSD
|
|
toonSD.load()
|
|
if self.WantSuits:
|
|
suitTypes = ['f',
|
|
'tm',
|
|
'pp',
|
|
'dt']
|
|
self.suits = []
|
|
for type in suitTypes:
|
|
suit = Suit.Suit()
|
|
d = SuitDNA.SuitDNA()
|
|
d.newSuit(type)
|
|
suit.setDNA(d)
|
|
suit.nametag3d.stash()
|
|
suit.nametag.destroy()
|
|
suit.pose('walk', 0)
|
|
self.suits.append(suit)
|
|
|
|
self.__textGen = TextNode('ringGame')
|
|
self.__textGen.setFont(ToontownGlobals.getSignFont())
|
|
self.__textGen.setAlign(TextNode.ACenter)
|
|
self.introMovie = self.getIntroMovie()
|
|
|
|
def unload(self):
|
|
self.notify.debug('unload')
|
|
DistributedMinigame.unload(self)
|
|
self.removeChildGameFSM(self.gameFSM)
|
|
del self.gameFSM
|
|
self.introMovie.finish()
|
|
del self.introMovie
|
|
del self.__textGen
|
|
for avId in self.toonSDs.keys():
|
|
toonSD = self.toonSDs[avId]
|
|
toonSD.unload()
|
|
|
|
del self.toonSDs
|
|
for suit in self.suits:
|
|
suit.reparentTo(hidden)
|
|
suit.delete()
|
|
|
|
del self.suits
|
|
self.ground.removeNode()
|
|
del self.ground
|
|
self.dropShadow.removeNode()
|
|
del self.dropShadow
|
|
for model in self.dropObjModels.values():
|
|
model.removeNode()
|
|
|
|
del self.dropObjModels
|
|
del self.music
|
|
del self.sndGoodCatch
|
|
del self.sndOof
|
|
del self.sndAnvilLand
|
|
del self.sndPerfect
|
|
|
|
def getObjModel(self, objName):
|
|
return self.dropObjModels[objName].copyTo(hidden)
|
|
|
|
def __genText(self, text):
|
|
self.__textGen.setText(text)
|
|
return self.__textGen.generate()
|
|
|
|
def calcDifficultyConstants(self, difficulty, numPlayers):
|
|
ToonSpeedRange = [16.0, 25.0]
|
|
self.ToonSpeed = lerp(ToonSpeedRange[0], ToonSpeedRange[1], difficulty)
|
|
self.SuitSpeed = self.ToonSpeed / 2.0
|
|
self.SuitPeriodRange = [lerp(5.0, 3.0, self.getDifficulty()), lerp(15.0, 8.0, self.getDifficulty())]
|
|
|
|
def scaledDimensions(widthHeight, scale):
|
|
w, h = widthHeight
|
|
return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)]
|
|
|
|
BaseStageDimensions = [20, 15]
|
|
areaScales = [1.0,
|
|
1.0,
|
|
3.0 / 2,
|
|
4.0 / 2]
|
|
self.StageAreaScale = areaScales[numPlayers - 1]
|
|
self.StageLinearScale = math.sqrt(self.StageAreaScale)
|
|
self.notify.debug('StageLinearScale: %s' % self.StageLinearScale)
|
|
self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale)
|
|
self.notify.debug('StageDimensions: %s' % self.StageDimensions)
|
|
self.StageHalfWidth = self.StageDimensions[0] / 2.0
|
|
self.StageHalfHeight = self.StageDimensions[1] / 2.0
|
|
MOHs = [24] * 2 + [26, 28]
|
|
self.MinOffscreenHeight = MOHs[self.getNumPlayers() - 1]
|
|
distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1])
|
|
distance /= self.StageLinearScale
|
|
if self.DropPlacerType == PathDropPlacer:
|
|
distance /= 1.5
|
|
ToonRunDuration = distance / self.ToonSpeed
|
|
offScreenOnScreenRatio = 1.0
|
|
fraction = 1.0 / 3 * 0.85
|
|
self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio))
|
|
self.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration)
|
|
self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration
|
|
self.notify.debug('OffscreenTime=%s' % self.OffscreenTime)
|
|
self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime
|
|
self.MaxDropDuration = self.BaselineDropDuration
|
|
self.DropPeriod = self.BaselineDropDuration / 2.0
|
|
scaledNumPlayers = (numPlayers - 1.0) * 0.75 + 1.0
|
|
self.DropPeriod /= scaledNumPlayers
|
|
typeProbs = {'fruit': 3,
|
|
'anvil': 1}
|
|
probSum = reduce(lambda x, y: x + y, typeProbs.values())
|
|
for key in typeProbs.keys():
|
|
typeProbs[key] = float(typeProbs[key]) / probSum
|
|
|
|
scheduler = DropScheduler(CatchGameGlobals.GameDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.FasterDropDelay, self.FasterDropPeriodMult)
|
|
self.totalDrops = 0
|
|
while not scheduler.doneDropping():
|
|
scheduler.stepT()
|
|
self.totalDrops += 1
|
|
|
|
self.numFruits = int(self.totalDrops * typeProbs['fruit'])
|
|
self.numAnvils = int(self.totalDrops - self.numFruits)
|
|
|
|
def getNumPlayers(self):
|
|
return self.numPlayers
|
|
|
|
def defineConstants(self):
|
|
self.notify.debug('defineConstants')
|
|
self.DropPlacerType = RegionDropPlacer
|
|
fruits = {ToontownGlobals.ToontownCentral: 'apple',
|
|
ToontownGlobals.DonaldsDock: 'orange',
|
|
ToontownGlobals.DaisyGardens: 'pear',
|
|
ToontownGlobals.MinniesMelodyland: 'coconut',
|
|
ToontownGlobals.TheBrrrgh: 'watermelon',
|
|
ToontownGlobals.DonaldsDreamland: 'pineapple'}
|
|
self.fruitName = fruits[self.getSafezoneId()]
|
|
self.ShowObjSpheres = 0
|
|
self.ShowToonSpheres = 0
|
|
self.ShowSuitSpheres = 0
|
|
self.PredictiveSmoothing = 1
|
|
self.UseGravity = 1
|
|
self.TrickShadows = 1
|
|
self.WantSuits = 1
|
|
self.FirstDropDelay = 0.5
|
|
self.FasterDropDelay = int(2.0 / 3 * CatchGameGlobals.GameDuration)
|
|
self.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay)
|
|
self.FasterDropPeriodMult = 0.5
|
|
self.calcDifficultyConstants(self.getDifficulty(), self.getNumPlayers())
|
|
self.notify.debug('ToonSpeed: %s' % self.ToonSpeed)
|
|
self.notify.debug('total drops: %s' % self.totalDrops)
|
|
self.notify.debug('numFruits: %s' % self.numFruits)
|
|
self.notify.debug('numAnvils: %s' % self.numAnvils)
|
|
self.ObjRadius = 1.0
|
|
dropGridDimensions = [[5, 5],
|
|
[5, 5],
|
|
[6, 6],
|
|
[7, 7]]
|
|
self.DropRows, self.DropColumns = dropGridDimensions[self.getNumPlayers() - 1]
|
|
self.cameraPosTable = [[0, -29.36, 28.17]] * 2 + [[0, -32.87, 30.43], [0, -35.59, 32.1]]
|
|
self.cameraHpr = [0, -35, 0]
|
|
self.CameraPosHpr = self.cameraPosTable[self.getNumPlayers() - 1] + self.cameraHpr
|
|
for objType in DropObjectTypes:
|
|
self.notify.debug('*** Object Type: %s' % objType.name)
|
|
objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration
|
|
self.notify.debug('onscreenDuration=%s' % objType.onscreenDuration)
|
|
v_0 = 0.0
|
|
t = objType.onscreenDuration
|
|
x_0 = self.MinOffscreenHeight
|
|
x = 0.0
|
|
g = 2.0 * (x - x_0 - v_0 * t) / (t * t)
|
|
self.notify.debug('gravity=%s' % g)
|
|
objType.trajectory = Trajectory.Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.Trajectory.gravity))
|
|
objType.fallDuration = objType.onscreenDuration + self.OffscreenTime
|
|
|
|
def grid2world(self, column, row):
|
|
x = column / float(self.DropColumns - 1)
|
|
y = row / float(self.DropRows - 1)
|
|
x = x * 2.0 - 1.0
|
|
y = y * 2.0 - 1.0
|
|
x *= self.StageHalfWidth
|
|
y *= self.StageHalfHeight
|
|
return (x, y)
|
|
|
|
def showPosts(self):
|
|
self.hidePosts()
|
|
self.posts = [Toon.Toon(),
|
|
Toon.Toon(),
|
|
Toon.Toon(),
|
|
Toon.Toon()]
|
|
for i in xrange(len(self.posts)):
|
|
toon = self.posts[i]
|
|
toon.setDNA(base.localAvatar.getStyle())
|
|
toon.reparentTo(render)
|
|
x = self.StageHalfWidth
|
|
y = self.StageHalfHeight
|
|
if i > 1:
|
|
x = -x
|
|
if i % 2:
|
|
y = -y
|
|
toon.setPos(x, y, 0)
|
|
|
|
def hidePosts(self):
|
|
if hasattr(self, 'posts'):
|
|
for toon in self.posts:
|
|
toon.removeNode()
|
|
|
|
del self.posts
|
|
|
|
def showDropGrid(self):
|
|
self.hideDropGrid()
|
|
self.dropMarkers = []
|
|
print 'dropRows: %s' % self.DropRows
|
|
print 'dropCols: %s' % self.DropColumns
|
|
for row in xrange(self.DropRows):
|
|
self.dropMarkers.append([])
|
|
rowList = self.dropMarkers[row]
|
|
for column in xrange(self.DropColumns):
|
|
toon = Toon.Toon()
|
|
toon.setDNA(base.localAvatar.getStyle())
|
|
toon.reparentTo(render)
|
|
toon.setScale(1.0 / 3)
|
|
x, y = self.grid2world(column, row)
|
|
toon.setPos(x, y, 0)
|
|
rowList.append(toon)
|
|
|
|
def hideDropGrid(self):
|
|
if hasattr(self, 'dropMarkers'):
|
|
for row in self.dropMarkers:
|
|
for marker in row:
|
|
marker.removeNode()
|
|
|
|
del self.dropMarkers
|
|
|
|
def onstage(self):
|
|
self.notify.debug('onstage')
|
|
DistributedMinigame.onstage(self)
|
|
self.ground.reparentTo(render)
|
|
self.scorePanels = []
|
|
camera.reparentTo(render)
|
|
camera.setPosHpr(*self.CameraPosHpr)
|
|
lt = base.localAvatar
|
|
lt.reparentTo(render)
|
|
self.__placeToon(self.localAvId)
|
|
lt.setSpeed(0, 0)
|
|
toonSD = self.toonSDs[self.localAvId]
|
|
toonSD.enter()
|
|
toonSD.fsm.request('normal')
|
|
self.orthoWalk.stop()
|
|
radius = 0.7
|
|
handler = CollisionHandlerEvent()
|
|
handler.setInPattern('ltCatch%in')
|
|
self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
|
|
self.ltLegsCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
|
|
self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
|
|
self.ltHeadCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
|
|
self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
|
|
self.ltLHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
|
|
self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
|
|
self.ltRHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
|
|
legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode)
|
|
legsCollNodepath.hide()
|
|
head = base.localAvatar.getHeadParts().getPath(2)
|
|
headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
|
|
headCollNodepath.hide()
|
|
lHand = base.localAvatar.getLeftHands()[0]
|
|
lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
|
|
lHandCollNodepath.hide()
|
|
rHand = base.localAvatar.getRightHands()[0]
|
|
rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
|
|
rHandCollNodepath.hide()
|
|
lt.cTrav.addCollider(legsCollNodepath, handler)
|
|
lt.cTrav.addCollider(headCollNodepath, handler)
|
|
lt.cTrav.addCollider(lHandCollNodepath, handler)
|
|
lt.cTrav.addCollider(lHandCollNodepath, handler)
|
|
if self.ShowToonSpheres:
|
|
legsCollNodepath.show()
|
|
headCollNodepath.show()
|
|
lHandCollNodepath.show()
|
|
rHandCollNodepath.show()
|
|
self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
|
|
self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
|
|
self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
|
|
self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
|
|
self.toonCollNodes = [legsCollNodepath,
|
|
headCollNodepath,
|
|
lHandCollNodepath,
|
|
rHandCollNodepath]
|
|
if self.PredictiveSmoothing:
|
|
DistributedSmoothNode.activateSmoothing(1, 1)
|
|
self.introMovie.start()
|
|
|
|
def offstage(self):
|
|
self.notify.debug('offstage')
|
|
DistributedSmoothNode.activateSmoothing(1, 0)
|
|
self.introMovie.finish()
|
|
for avId in self.toonSDs.keys():
|
|
self.toonSDs[avId].exit()
|
|
|
|
self.hidePosts()
|
|
self.hideDropGrid()
|
|
for collNode in self.toonCollNodes:
|
|
while collNode.node().getNumSolids():
|
|
collNode.node().removeSolid(0)
|
|
|
|
base.localAvatar.cTrav.removeCollider(collNode)
|
|
|
|
del self.toonCollNodes
|
|
for panel in self.scorePanels:
|
|
panel.cleanup()
|
|
|
|
del self.scorePanels
|
|
self.ground.reparentTo(hidden)
|
|
DistributedMinigame.offstage(self)
|
|
|
|
def handleDisabledAvatar(self, avId):
|
|
self.notify.debug('handleDisabledAvatar')
|
|
self.notify.debug('avatar ' + str(avId) + ' disabled')
|
|
self.toonSDs[avId].exit(unexpectedExit=True)
|
|
del self.toonSDs[avId]
|
|
DistributedMinigame.handleDisabledAvatar(self, avId)
|
|
|
|
def __placeToon(self, avId):
|
|
toon = self.getAvatar(avId)
|
|
idx = self.avIdList.index(avId)
|
|
x = lineupPos(idx, self.numPlayers, 4.0)
|
|
toon.setPos(x, 0, 0)
|
|
toon.setHpr(180, 0, 0)
|
|
|
|
def setGameReady(self):
|
|
if not self.hasLocalToon:
|
|
return
|
|
self.notify.debug('setGameReady')
|
|
if DistributedMinigame.setGameReady(self):
|
|
return
|
|
headCollNP = base.localAvatar.find('**/catchHeadCollNode')
|
|
if headCollNP and not headCollNP.isEmpty():
|
|
headCollNP.hide()
|
|
for avId in self.remoteAvIdList:
|
|
toon = self.getAvatar(avId)
|
|
if toon:
|
|
toon.reparentTo(render)
|
|
self.__placeToon(avId)
|
|
toonSD = CatchGameToonSD.CatchGameToonSD(avId, self)
|
|
self.toonSDs[avId] = toonSD
|
|
toonSD.load()
|
|
toonSD.enter()
|
|
toonSD.fsm.request('normal')
|
|
toon.startSmooth()
|
|
|
|
def setGameStart(self, timestamp):
|
|
if not self.hasLocalToon:
|
|
return
|
|
self.notify.debug('setGameStart')
|
|
DistributedMinigame.setGameStart(self, timestamp)
|
|
self.introMovie.finish()
|
|
camera.reparentTo(render)
|
|
camera.setPosHpr(*self.CameraPosHpr)
|
|
self.gameFSM.request('play')
|
|
|
|
def enterOff(self):
|
|
self.notify.debug('enterOff')
|
|
|
|
def exitOff(self):
|
|
pass
|
|
|
|
def enterPlay(self):
|
|
self.notify.debug('enterPlay')
|
|
self.orthoWalk.start()
|
|
for suit in self.suits:
|
|
suitCollSphere = CollisionSphere(0, 0, 0, 1.0)
|
|
suit.collSphereName = 'suitCollSphere%s' % self.suits.index(suit)
|
|
suitCollSphere.setTangible(0)
|
|
suitCollNode = CollisionNode(self.uniqueName(suit.collSphereName))
|
|
suitCollNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
|
|
suitCollNode.addSolid(suitCollSphere)
|
|
suit.collNodePath = suit.attachNewNode(suitCollNode)
|
|
suit.collNodePath.hide()
|
|
if self.ShowSuitSpheres:
|
|
suit.collNodePath.show()
|
|
self.accept(self.uniqueName('enter' + suit.collSphereName), self.handleSuitCollision)
|
|
|
|
self.scores = [0] * self.numPlayers
|
|
spacing = 0.4
|
|
for i in xrange(self.numPlayers):
|
|
avId = self.avIdList[i]
|
|
avName = self.getAvatarName(avId)
|
|
scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName)
|
|
scorePanel.setScale(0.9)
|
|
scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15)
|
|
scorePanel.reparentTo(base.a2dTopRight)
|
|
scorePanel.makeTransparent(0.75)
|
|
self.scorePanels.append(scorePanel)
|
|
|
|
self.fruitsCaught = 0
|
|
self.droppedObjCaught = {}
|
|
self.dropIntervals = {}
|
|
self.droppedObjNames = []
|
|
self.dropSchedule = []
|
|
self.numItemsDropped = 0
|
|
self.scheduleDrops()
|
|
self.startDropTask()
|
|
if self.WantSuits:
|
|
self.startSuitWalkTask()
|
|
self.timer = ToontownTimer.ToontownTimer()
|
|
self.timer.posInTopRightCorner()
|
|
self.timer.setTime(CatchGameGlobals.GameDuration)
|
|
self.timer.countdown(CatchGameGlobals.GameDuration, self.timerExpired)
|
|
self.timer.setTransparency(1)
|
|
self.timer.setColorScale(1, 1, 1, 0.75)
|
|
base.playMusic(self.music, looping=0, volume=0.9)
|
|
|
|
def exitPlay(self):
|
|
self.stopDropTask()
|
|
self.stopSuitWalkTask()
|
|
if hasattr(self, 'perfectIval'):
|
|
self.perfectIval.pause()
|
|
del self.perfectIval
|
|
self.timer.stop()
|
|
self.timer.destroy()
|
|
del self.timer
|
|
self.music.stop()
|
|
for suit in self.suits:
|
|
self.ignore(self.uniqueName('enter' + suit.collSphereName))
|
|
suit.collNodePath.removeNode()
|
|
|
|
for ival in self.dropIntervals.values():
|
|
ival.finish()
|
|
|
|
del self.dropIntervals
|
|
del self.droppedObjNames
|
|
del self.droppedObjCaught
|
|
del self.dropSchedule
|
|
taskMgr.remove(self.EndGameTaskName)
|
|
|
|
def timerExpired(self):
|
|
pass
|
|
|
|
def __handleCatch(self, objNum):
|
|
self.notify.debug('catch: %s' % objNum)
|
|
self.showCatch(self.localAvId, objNum)
|
|
objName = self.droppedObjNames[objNum]
|
|
objTypeId = CatchGameGlobals.Name2DOTypeId[objName]
|
|
self.sendUpdate('claimCatch', [objNum, objTypeId])
|
|
self.finishDropInterval(objNum)
|
|
|
|
def showCatch(self, avId, objNum):
|
|
isLocal = avId == self.localAvId
|
|
objName = self.droppedObjNames[objNum]
|
|
objType = Name2DropObjectType[objName]
|
|
if objType.good:
|
|
if objNum not in self.droppedObjCaught:
|
|
if isLocal:
|
|
base.playSfx(self.sndGoodCatch)
|
|
fruit = self.getObjModel(objName)
|
|
toon = self.getAvatar(avId)
|
|
rHand = toon.getRightHands()[0]
|
|
self.toonSDs[avId].eatFruit(fruit, rHand)
|
|
else:
|
|
self.toonSDs[avId].fsm.request('fallForward')
|
|
self.droppedObjCaught[objNum] = 1
|
|
|
|
def setObjectCaught(self, avId, objNum):
|
|
if not self.hasLocalToon:
|
|
return
|
|
if self.gameFSM.getCurrentState().getName() != 'play':
|
|
self.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId))
|
|
return
|
|
isLocal = avId == self.localAvId
|
|
if not isLocal:
|
|
self.notify.debug('AI: avatar %s caught %s' % (avId, objNum))
|
|
self.finishDropInterval(objNum)
|
|
self.showCatch(avId, objNum)
|
|
objName = self.droppedObjNames[objNum]
|
|
if Name2DropObjectType[objName].good:
|
|
i = self.avIdList.index(avId)
|
|
self.scores[i] += 1
|
|
self.scorePanels[i].setScore(self.scores[i])
|
|
self.fruitsCaught += 1
|
|
|
|
def finishDropInterval(self, objNum):
|
|
if objNum in self.dropIntervals:
|
|
self.dropIntervals[objNum].finish()
|
|
|
|
def scheduleDrops(self):
|
|
self.droppedObjNames = [self.fruitName] * self.numFruits + ['anvil'] * self.numAnvils
|
|
self.randomNumGen.shuffle(self.droppedObjNames)
|
|
dropPlacer = self.DropPlacerType(self, self.getNumPlayers(), self.droppedObjNames)
|
|
while not dropPlacer.doneDropping():
|
|
self.dropSchedule.append(dropPlacer.getNextDrop())
|
|
|
|
def startDropTask(self):
|
|
taskMgr.add(self.dropTask, self.DropTaskName)
|
|
|
|
def stopDropTask(self):
|
|
taskMgr.remove(self.DropTaskName)
|
|
|
|
def dropTask(self, task):
|
|
curT = self.getCurrentGameTime()
|
|
while self.dropSchedule[0][0] <= curT:
|
|
drop = self.dropSchedule[0]
|
|
self.dropSchedule = self.dropSchedule[1:]
|
|
dropTime, objName, dropCoords = drop
|
|
objNum = self.numItemsDropped
|
|
lastDrop = len(self.dropSchedule) == 0
|
|
x, y = self.grid2world(*dropCoords)
|
|
dropIval = self.getDropIval(x, y, objName, objNum)
|
|
|
|
def cleanup(self = self, objNum = objNum, lastDrop = lastDrop):
|
|
del self.dropIntervals[objNum]
|
|
if lastDrop:
|
|
self.sendUpdate('reportDone')
|
|
|
|
dropIval.append(Func(cleanup))
|
|
self.dropIntervals[objNum] = dropIval
|
|
self.numItemsDropped += 1
|
|
dropIval.start(curT - dropTime)
|
|
if lastDrop:
|
|
return Task.done
|
|
|
|
return Task.cont
|
|
|
|
def setEveryoneDone(self):
|
|
if not self.hasLocalToon:
|
|
return
|
|
if self.gameFSM.getCurrentState().getName() != 'play':
|
|
self.notify.warning('ignoring setEveryoneDone msg')
|
|
return
|
|
self.notify.debug('setEveryoneDone')
|
|
|
|
def endGame(task, self = self):
|
|
if not CatchGameGlobals.EndlessGame:
|
|
self.gameOver()
|
|
return Task.done
|
|
|
|
self.notify.debug('num fruits: %s' % self.numFruits)
|
|
self.notify.debug('num catches: %s' % self.fruitsCaught)
|
|
self.timer.hide()
|
|
|
|
if self.fruitsCaught >= self.numFruits:
|
|
self.notify.debug('perfect game!')
|
|
perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.CatchGamePerfect))
|
|
perfectText = hidden.attachNewNode('perfectText')
|
|
perfectTextSubnode.reparentTo(perfectText)
|
|
frame = self.__textGen.getCardActual()
|
|
offsetY = -abs(frame[2] + frame[3]) / 2.0
|
|
perfectTextSubnode.setPos(0, 0, offsetY)
|
|
perfectText.setColor(1, 0.1, 0.1, 1)
|
|
|
|
def fadeFunc(t, text = perfectText):
|
|
text.setColorScale(1, 1, 1, t)
|
|
|
|
def destroyText(text = perfectText):
|
|
text.removeNode()
|
|
|
|
textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None))
|
|
soundTrack = SoundInterval(self.sndPerfect)
|
|
self.perfectIval = Parallel(textTrack, soundTrack)
|
|
self.perfectIval.start()
|
|
else:
|
|
taskMgr.doMethodLater(1, endGame, self.EndGameTaskName)
|
|
return
|
|
|
|
def getDropIval(self, x, y, dropObjName, num):
|
|
objType = Name2DropObjectType[dropObjName]
|
|
dropNode = hidden.attachNewNode('catchDropNode%s' % num)
|
|
dropNode.setPos(x, y, 0)
|
|
shadow = self.dropShadow.copyTo(dropNode)
|
|
shadow.setZ(0.2)
|
|
shadow.setColor(1, 1, 1, 1)
|
|
object = self.getObjModel(dropObjName)
|
|
object.reparentTo(dropNode)
|
|
if dropObjName in ['watermelon', 'anvil']:
|
|
objH = object.getH()
|
|
absDelta = {'watermelon': 12,
|
|
'anvil': 15}[dropObjName]
|
|
delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta
|
|
newH = objH + delta
|
|
else:
|
|
newH = self.randomNumGen.random() * 360.0
|
|
object.setH(newH)
|
|
sphereName = 'FallObj%s' % num
|
|
radius = self.ObjRadius
|
|
if objType.good:
|
|
radius *= lerp(1.0, 1.3, self.getDifficulty())
|
|
collSphere = CollisionSphere(0, 0, 0, radius)
|
|
collSphere.setTangible(0)
|
|
collNode = CollisionNode(sphereName)
|
|
collNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
|
|
collNode.addSolid(collSphere)
|
|
collNodePath = object.attachNewNode(collNode)
|
|
collNodePath.hide()
|
|
if self.ShowObjSpheres:
|
|
collNodePath.show()
|
|
catchEventName = 'ltCatch' + sphereName
|
|
|
|
def eatCollEntry(forward, collEntry):
|
|
forward()
|
|
|
|
self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, num)))
|
|
|
|
def cleanup(self = self, dropNode = dropNode, num = num, event = catchEventName):
|
|
self.ignore(event)
|
|
dropNode.removeNode()
|
|
|
|
duration = objType.fallDuration
|
|
onscreenDuration = objType.onscreenDuration
|
|
dropHeight = self.MinOffscreenHeight
|
|
targetShadowScale = 0.3
|
|
if self.TrickShadows:
|
|
intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration)
|
|
shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0))
|
|
shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale))
|
|
else:
|
|
shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0)
|
|
targetShadowAlpha = 0.4
|
|
shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0))
|
|
shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
|
|
if self.UseGravity:
|
|
|
|
def setObjPos(t, objType = objType, object = object):
|
|
z = objType.trajectory.calcZ(t)
|
|
object.setZ(z)
|
|
|
|
setObjPos(0)
|
|
dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration)
|
|
else:
|
|
startPos = Point3(0, 0, self.MinOffscreenHeight)
|
|
object.setPos(startPos)
|
|
dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn')
|
|
ival = Sequence(Func(Functor(dropNode.reparentTo, render)), Parallel(Sequence(WaitInterval(self.OffscreenTime), dropIval), shadowIval), Func(cleanup), name='drop%s' % num)
|
|
landSound = None
|
|
if objType == Name2DropObjectType['anvil']:
|
|
landSound = self.sndAnvilLand
|
|
if landSound:
|
|
ival.append(SoundInterval(landSound))
|
|
return ival
|
|
|
|
def startSuitWalkTask(self):
|
|
ival = Parallel(name='catchGameMetaSuitWalk')
|
|
rng = RandomNumGen(self.randomNumGen)
|
|
delay = 0.0
|
|
while delay < CatchGameGlobals.GameDuration:
|
|
delay += lerp(self.SuitPeriodRange[0], self.SuitPeriodRange[0], rng.random())
|
|
walkIval = Sequence(name='catchGameSuitWalk')
|
|
walkIval.append(Wait(delay))
|
|
|
|
def pickY(self = self, rng = rng):
|
|
return lerp(-self.StageHalfHeight, self.StageHalfHeight, rng.random())
|
|
|
|
m = [2.5,
|
|
2.5,
|
|
2.3,
|
|
2.1][self.getNumPlayers() - 1]
|
|
startPos = Point3(-(self.StageHalfWidth * m), pickY(), 0)
|
|
stopPos = Point3(self.StageHalfWidth * m, pickY(), 0)
|
|
if rng.choice([0, 1]):
|
|
startPos, stopPos = stopPos, startPos
|
|
walkIval.append(self.getSuitWalkIval(startPos, stopPos, rng))
|
|
ival.append(walkIval)
|
|
|
|
ival.start()
|
|
self.suitWalkIval = ival
|
|
|
|
def stopSuitWalkTask(self):
|
|
self.suitWalkIval.finish()
|
|
del self.suitWalkIval
|
|
|
|
def getSuitWalkIval(self, startPos, stopPos, rng):
|
|
data = {}
|
|
lerpNP = render.attachNewNode('catchGameSuitParent')
|
|
|
|
def setup(self = self, startPos = startPos, stopPos = stopPos, data = data, lerpNP = lerpNP, rng = rng):
|
|
if len(self.suits) == 0:
|
|
return
|
|
suit = rng.choice(self.suits)
|
|
data['suit'] = suit
|
|
self.suits.remove(suit)
|
|
suit.reparentTo(lerpNP)
|
|
suit.loop('walk')
|
|
suit.setPlayRate(self.SuitSpeed / ToontownGlobals.SuitWalkSpeed, 'walk')
|
|
suit.setPos(0, 0, 0)
|
|
lerpNP.setPos(startPos)
|
|
suit.lookAt(stopPos)
|
|
|
|
def cleanup(self = self, data = data, lerpNP = lerpNP):
|
|
if 'suit' in data:
|
|
suit = data['suit']
|
|
suit.reparentTo(hidden)
|
|
self.suits.append(suit)
|
|
lerpNP.removeNode()
|
|
|
|
distance = Vec3(stopPos - startPos).length()
|
|
duration = distance / self.SuitSpeed
|
|
ival = Sequence(FunctionInterval(setup), LerpPosInterval(lerpNP, duration, stopPos), FunctionInterval(cleanup))
|
|
return ival
|
|
|
|
def handleSuitCollision(self, collEntry):
|
|
self.toonSDs[self.localAvId].fsm.request('fallBack')
|
|
timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime())
|
|
self.sendUpdate('hitBySuit', [self.localAvId, timestamp])
|
|
|
|
def hitBySuit(self, avId, timestamp):
|
|
if not self.hasLocalToon:
|
|
return
|
|
if self.gameFSM.getCurrentState().getName() != 'play':
|
|
self.notify.warning('ignoring msg: av %s hit by suit' % avId)
|
|
return
|
|
toon = self.getAvatar(avId)
|
|
if toon == None:
|
|
return
|
|
self.notify.debug('avatar %s hit by a suit' % avId)
|
|
if avId != self.localAvId:
|
|
self.toonSDs[avId].fsm.request('fallBack')
|
|
return
|
|
|
|
def enterCleanup(self):
|
|
self.notify.debug('enterCleanup')
|
|
|
|
def exitCleanup(self):
|
|
pass
|
|
|
|
def initOrthoWalk(self):
|
|
self.notify.debug('startOrthoWalk')
|
|
|
|
def doCollisions(oldPos, newPos, self = self):
|
|
x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
|
|
y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
|
|
newPos.setX(x)
|
|
newPos.setY(y)
|
|
return newPos
|
|
|
|
orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions)
|
|
self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())
|
|
|
|
def destroyOrthoWalk(self):
|
|
self.notify.debug('destroyOrthoWalk')
|
|
self.orthoWalk.destroy()
|
|
del self.orthoWalk
|
|
|
|
def getIntroMovie(self):
|
|
locNode = self.ground.find('**/locator_tree')
|
|
treeNode = locNode.attachNewNode('treeNode')
|
|
treeNode.setHpr(render, 0, 0, 0)
|
|
|
|
def cleanupTree(treeNode = treeNode):
|
|
treeNode.removeNode()
|
|
|
|
initialCamPosHpr = (-0.21,
|
|
-19.56,
|
|
13.94,
|
|
0.0,
|
|
26.57,
|
|
0.0)
|
|
suitViewCamPosHpr = (0, -11.5, 13, 0, -35, 0)
|
|
finalCamPosHpr = self.CameraPosHpr
|
|
cameraIval = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, treeNode, *initialCamPosHpr), WaitInterval(4.0), LerpPosHprInterval(camera, 2.0, Point3(*suitViewCamPosHpr[:3]), Point3(*suitViewCamPosHpr[3:]), blendType='easeInOut', name='lerpToSuitView'), WaitInterval(4.0), LerpPosHprInterval(camera, 3.0, Point3(*finalCamPosHpr[:3]), Point3(*finalCamPosHpr[3:]), blendType='easeInOut', name='lerpToPlayView'))
|
|
|
|
def getIntroToon(toonProperties, parent, pos):
|
|
toon = Toon.Toon()
|
|
dna = ToonDNA.ToonDNA()
|
|
dna.newToonFromProperties(*toonProperties)
|
|
toon.setDNA(dna)
|
|
toon.reparentTo(parent)
|
|
toon.setPos(*pos)
|
|
toon.setH(180)
|
|
toon.startBlink()
|
|
return toon
|
|
|
|
def cleanupIntroToon(toon):
|
|
toon.detachNode()
|
|
toon.stopBlink()
|
|
toon.delete()
|
|
|
|
def getThrowIval(toon, hand, object, leftToon, isAnvil = 0):
|
|
anim = 'catch-intro-throw'
|
|
grabFrame = 12
|
|
fullSizeFrame = 30
|
|
framePeriod = 1.0 / toon.getFrameRate(anim)
|
|
objScaleDur = (fullSizeFrame - grabFrame) * framePeriod
|
|
releaseFrame = 35
|
|
trajDuration = 1.6
|
|
trajDistance = 4
|
|
if leftToon:
|
|
releaseFrame = 34
|
|
trajDuration = 1.0
|
|
trajDistance = 1
|
|
animIval = ActorInterval(toon, anim, loop=0)
|
|
|
|
def getThrowDest(object = object, offset = trajDistance):
|
|
dest = object.getPos(render)
|
|
dest += Point3(0, -offset, 0)
|
|
dest.setZ(0)
|
|
return dest
|
|
|
|
if leftToon:
|
|
trajIval = ProjectileInterval(object, startVel=Point3(0, 0, 0), duration=trajDuration)
|
|
else:
|
|
trajIval = ProjectileInterval(object, endPos=getThrowDest, duration=trajDuration)
|
|
trajIval = Sequence(Func(object.wrtReparentTo, render), trajIval, Func(object.wrtReparentTo, hidden))
|
|
if isAnvil:
|
|
trajIval.append(SoundInterval(self.sndAnvilLand))
|
|
objIval = Track((grabFrame * framePeriod, Sequence(Func(object.reparentTo, hand), Func(object.setPosHpr, 0.05, -.13, 0.62, 0, 0, 336.8), LerpScaleInterval(object, objScaleDur, 1.0, startScale=0.1, blendType='easeInOut'))), (releaseFrame * framePeriod, trajIval))
|
|
|
|
def cleanup(object = object):
|
|
object.reparentTo(hidden)
|
|
object.removeNode()
|
|
|
|
throwIval = Sequence(Parallel(animIval, objIval), Func(cleanup))
|
|
return throwIval
|
|
|
|
tY = -4.0
|
|
tZ = 19.5
|
|
props = ['css',
|
|
'md',
|
|
'm',
|
|
'f',
|
|
9,
|
|
0,
|
|
9,
|
|
9,
|
|
13,
|
|
5,
|
|
11,
|
|
5,
|
|
8,
|
|
7]
|
|
leftToon = getIntroToon(props, treeNode, [-2.3, tY, tZ])
|
|
props = ['mss',
|
|
'ls',
|
|
'l',
|
|
'm',
|
|
6,
|
|
0,
|
|
6,
|
|
6,
|
|
3,
|
|
5,
|
|
3,
|
|
5,
|
|
5,
|
|
0]
|
|
rightToon = getIntroToon(props, treeNode, [1.8, tY, tZ])
|
|
fruit = self.getObjModel(self.fruitName)
|
|
if self.fruitName == 'pineapple':
|
|
fruit.setZ(0.42)
|
|
fruit.flattenMedium()
|
|
anvil = self.getObjModel('anvil')
|
|
anvil.setH(100)
|
|
anvil.setZ(0.42)
|
|
anvil.flattenMedium()
|
|
leftToonIval = getThrowIval(leftToon, leftToon.getRightHands()[0], fruit, leftToon=1)
|
|
rightToonIval = getThrowIval(rightToon, rightToon.getLeftHands()[0], anvil, leftToon=0, isAnvil=1)
|
|
animDur = leftToon.getNumFrames('catch-intro-throw') / leftToon.getFrameRate('catch-intro-throw')
|
|
toonIval = Sequence(Parallel(Sequence(leftToonIval, Func(leftToon.loop, 'neutral')), Sequence(Func(rightToon.loop, 'neutral'), WaitInterval(animDur / 2.0), rightToonIval, Func(rightToon.loop, 'neutral')), WaitInterval(cameraIval.getDuration())), Func(cleanupIntroToon, leftToon), Func(cleanupIntroToon, rightToon))
|
|
self.treeNode = treeNode
|
|
self.fruit = fruit
|
|
self.anvil = anvil
|
|
self.leftToon = leftToon
|
|
self.rightToon = rightToon
|
|
introMovie = Sequence(Parallel(cameraIval, toonIval), Func(cleanupTree))
|
|
return introMovie
|