toontown-just-works/toontown/coghq/DistributedMoleField.py
2024-07-07 18:08:39 -05:00

472 lines
18 KiB
Python

from panda3d.core import *
from otp.level.BasicEntities import DistributedNodePathEntity
from direct.directnotify import DirectNotifyGlobal
from toontown.coghq import MoleHill
from toontown.coghq import MoleFieldBase
from direct.distributed.ClockDelta import globalClockDelta
from toontown.toonbase import ToontownTimer
from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel
from toontown.toonbase import ToontownGlobals
from toontown.toonbase import TTLocalizer
from direct.task import Task
import random
from toontown.minigame import Trajectory
from direct.interval.IntervalGlobal import *
from toontown.battle import MovieUtil
class DistributedMoleField(DistributedNodePathEntity, MoleFieldBase.MoleFieldBase):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMoleField')
ScheduleTaskName = 'moleFieldScheduler'
def __init__(self, cr):
DistributedNodePathEntity.__init__(self, cr)
self.gameStarted = False
self.moleHills = []
self.numMolesWhacked = 0
self.timer = None
self.frame2D = None
self.isToonInRange = 0
self.detectCount = 0
self.cameraHold = None
self.activeField = 1
self.dimensionX = 0.0
self.dimensionY = 0.0
self.gameText = TTLocalizer.MolesInstruction
self.winText = TTLocalizer.MolesFinished
self.pityWinText = TTLocalizer.MolesPityWin
self.restartedText = TTLocalizer.MolesRestarted
self.toonHitTracks = {}
self.hasRestarted = 0
self.hasEntered = 0
self.MolesWhackedTarget = 1000
self.GameDuration = 1000
return
def disable(self):
self.cleanupTimer()
for ival in self.toonHitTracks.values():
ival.finish()
self.toonHitTracks = {}
DistributedNodePathEntity.disable(self)
taskMgr.remove(self.detectName)
self.ignoreAll()
def delete(self):
self.soundBomb = None
self.soundBomb2 = None
self.soundCog = None
DistributedNodePathEntity.delete(self)
self.stopScheduleTask()
for mole in self.moleHills:
mole.destroy()
self.moleHills = []
self.cleanupTimer()
self.unloadGui()
return
def announceGenerate(self):
DistributedNodePathEntity.announceGenerate(self)
self.loadModel()
self.loadGui()
self.detectName = 'moleField %s' % self.doId
taskMgr.doMethodLater(0.1, self.__detect, self.detectName)
self.calcDimensions()
self.notify.debug('announceGenerate doId=%d entId=%d' % (self.doId, self.entId))
def setNumSquaresX(self, num):
self.numSquaresX = num
self.calcDimensions()
def setNumSquaresY(self, num):
self.numSquaresY = num
self.calcDimensions()
def setSpacingX(self, num):
self.spacingX = num
self.calcDimensions()
def setSpacingY(self, num):
self.spacingY = num
self.calcDimensions()
def calcDimensions(self):
self.dimensionX = self.numSquaresX * self.spacingX
self.dimensionY = self.numSquaresY * self.spacingY
self.centerCenterNode()
def loadModel(self):
moleIndex = 0
self.moleHills = []
for indexY in xrange(self.numSquaresY):
for indexX in xrange(self.numSquaresX):
xPos = indexX * self.spacingX
yPos = indexY * self.spacingY
newMoleHill = MoleHill.MoleHill(xPos, yPos, 0, self, moleIndex)
newMoleHill.reparentTo(self)
self.moleHills.append(newMoleHill)
moleIndex += 1
self.numMoles = len(self.moleHills)
self.centerNode = self.attachNewNode('center')
self.centerCenterNode()
self.soundBomb = base.loadSfx('phase_12/audio/sfx/Mole_Surprise.ogg')
self.soundBomb2 = base.loadSfx('phase_3.5/audio/dial/AV_pig_howl.ogg')
self.soundCog = base.loadSfx('phase_12/audio/sfx/Mole_Stomp.ogg')
self.soundUp = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg')
self.soundDown = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg')
upInterval = SoundInterval(self.soundUp, loop=0)
downInterval = SoundInterval(self.soundDown, loop=0)
self.soundIUpDown = Sequence(upInterval, downInterval)
def centerCenterNode(self):
self.centerNode.setPos(self.dimensionX * 0.5, self.dimensionY * 0.5, 0.0)
def loadGui(self):
self.frame2D = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.3,
0.3,
-0.05,
0.05), frameColor=(0.737, 0.573, 0.345, 0.3))
self.scoreLabel = DirectLabel(parent=self.frame2D, relief=None, pos=(0, 0, 0), scale=1.0, text='', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.075, text_pos=(0, -0.02))
self.updateGuiScore()
self.frame2D.hide()
return
def unloadGui(self):
self.frame2D.destroy()
self.frame2D = None
return
def setGameStart(self, timestamp, molesWhackTarget, totalTime):
self.GameDuration = totalTime
self.MolesWhackedTarget = molesWhackTarget
self.activeField = 1
self.isToonInRange = 0
self.scheduleMoles()
self.notify.debug('%d setGameStart: Starting game' % self.doId)
self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp)
self.gameStarted = True
for hill in self.moleHills:
hill.setGameStartTime(self.gameStartTime)
curGameTime = self.getCurrentGameTime()
timeLeft = self.GameDuration - curGameTime
self.cleanupTimer()
self.timer = ToontownTimer.ToontownTimer()
self.timer.posBelowTopRightCorner()
self.timer.setTime(timeLeft)
self.timer.countdown(timeLeft, self.timerExpired)
self.startScheduleTask()
self.frame2D.show()
if self.hasRestarted:
self.level.countryClub.showInfoText(self.restartedText)
self.sendUpdate('damageMe', [])
else:
self.hasRestarted = 1
self.updateGuiScore()
def local2GameTime(self, timestamp):
return timestamp - self.gameStartTime
def game2LocalTime(self, timestamp):
return timestamp + self.gameStartTime
def getCurrentGameTime(self):
return self.local2GameTime(globalClock.getFrameTime())
def startScheduleTask(self):
taskMgr.add(self.scheduleTask, self.ScheduleTaskName)
def stopScheduleTask(self):
taskMgr.remove(self.ScheduleTaskName)
def scheduleTask(self, task):
curTime = self.getCurrentGameTime()
while self.schedule and self.schedule[0][0] <= curTime and self.activeField:
popupInfo = self.schedule[0]
self.schedule = self.schedule[1:]
startTime, moleIndex, curMoveUpTime, curStayUpTime, curMoveDownTime, moleType = popupInfo
hill = self.moleHills[moleIndex]
hill.doMolePop(startTime, curMoveUpTime, curStayUpTime, curMoveDownTime, moleType)
if self.schedule:
return task.cont
else:
return task.done
def handleEnterHill(self, colEntry):
if not self.gameStarted:
self.notify.debug('sending clientTriggered for %d' % self.doId)
self.sendUpdate('setClientTriggered', [])
def handleEnterMole(self, colEntry):
if not self.gameStarted:
self.notify.debug('sending clientTriggered for %d' % self.doId)
self.sendUpdate('setClientTriggered', [])
surfaceNormal = colEntry.getSurfaceNormal(render)
self.notify.debug('surfaceNormal=%s' % surfaceNormal)
into = colEntry.getIntoNodePath()
moleIndex = int(into.getName().split('-')[-1])
self.notify.debug('hit mole %d' % moleIndex)
moleHill = self.moleHills[moleIndex]
moleHill.stashMoleCollision()
popupNum = moleHill.getPopupNum()
if moleHill.hillType == MoleFieldBase.HILL_MOLE:
timestamp = globalClockDelta.getFrameNetworkTime()
moleHill.setHillType(MoleFieldBase.HILL_WHACKED)
self.sendUpdate('whackedBomb', [moleIndex, popupNum, timestamp])
self.__showToonHitByBomb(localAvatar.doId, moleIndex, timestamp)
elif moleHill.hillType == MoleFieldBase.HILL_BOMB:
moleHill.setHillType(MoleFieldBase.HILL_COGWHACKED)
self.soundCog.play()
self.sendUpdate('whackedMole', [moleIndex, popupNum])
def updateMole(self, moleIndex, status):
if status == self.WHACKED:
moleHill = self.moleHills[moleIndex]
if not moleHill.hillType == MoleFieldBase.HILL_COGWHACKED:
moleHill.setHillType(MoleFieldBase.HILL_COGWHACKED)
self.soundCog.play()
moleHill.doMoleDown()
def updateGuiScore(self):
molesLeft = self.MolesWhackedTarget - self.numMolesWhacked
if self.frame2D and hasattr(self, 'scoreLabel') and molesLeft >= 0:
newText = TTLocalizer.MolesLeft % molesLeft
self.scoreLabel['text'] = newText
def setScore(self, score):
self.notify.debug('score=%d' % score)
self.numMolesWhacked = score
self.updateGuiScore()
molesLeft = self.MolesWhackedTarget - self.numMolesWhacked
if molesLeft == 0:
self.gameWon()
def cleanupTimer(self):
if self.timer:
self.timer.stop()
self.timer.destroy()
self.timer = None
return
def timerExpired(self):
self.cleanupTimer()
self.cleanDetect()
def gameWon(self):
for hill in self.moleHills:
hill.forceMoleDown()
self.cleanupTimer()
self.frame2D.hide()
self.level.countryClub.showInfoText(self.winText)
self.cleanDetect()
def setPityWin(self):
for hill in self.moleHills:
hill.forceMoleDown()
self.cleanupTimer()
self.frame2D.hide()
self.level.countryClub.showInfoText(self.pityWinText)
self.cleanDetect()
def cleanDetect(self):
self.activeField = 0
self.doToonOutOfRange()
def __detect(self, task):
distance = self.centerNode.getDistance(localAvatar)
greaterDim = self.dimensionX
if self.dimensionY > self.dimensionX:
greaterDim = self.gridScaleY
self.detectCount += 1
if self.detectCount > 5:
self.detectCount = 0
if distance < greaterDim * 0.75:
if not self.isToonInRange:
self.doToonInRange()
elif self.isToonInRange:
self.doToonOutOfRange()
taskMgr.doMethodLater(0.1, self.__detect, self.detectName)
return Task.done
def doToonInRange(self):
if not self.gameStarted:
self.notify.debug('sending clientTriggered for %d' % self.doId)
self.sendUpdate('setClientTriggered', [])
self.isToonInRange = 1
if self.activeField:
self.setUpCamera()
if not self.hasEntered:
self.level.countryClub.showInfoText(self.gameText)
self.hasEntered = 1
def setUpCamera(self):
camHeight = base.localAvatar.getClampedAvatarHeight()
heightScaleFactor = camHeight * 0.3333333333
defLookAt = Point3(0.0, 1.5, camHeight)
cameraPoint = Point3(0.0, -22.0 * heightScaleFactor, camHeight + 12.0)
base.localAvatar.setIdealCameraPos(cameraPoint)
def doToonOutOfRange(self):
self.isToonInRange = 0
base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
self.cameraHold = None
return
def reportToonHitByBomb(self, avId, moleIndex, timestamp):
if avId != localAvatar.doId:
self.__showToonHitByBomb(avId, moleIndex, timestamp)
moleHill = self.moleHills[moleIndex]
if not moleHill.hillType == MoleFieldBase.HILL_WHACKED:
moleHill.setHillType(MoleFieldBase.HILL_WHACKED)
self.soundCog.play()
moleHill.doMoleDown()
def __showToonHitByBomb(self, avId, moleIndex, timestamp = 0):
toon = base.cr.doId2do.get(avId)
moleHill = self.moleHills[moleIndex]
if toon == None:
return
rng = random.Random(timestamp)
curPos = toon.getPos(render)
oldTrack = self.toonHitTracks.get(avId)
if oldTrack:
if oldTrack.isPlaying():
oldTrack.finish()
toon.setPos(curPos)
toon.setZ(self.getZ())
parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`)
parentNode.setPos(toon.getPos(render))
toon.reparentTo(parentNode)
toon.setPos(0, 0, 0)
startPos = parentNode.getPos()
dropShadow = toon.dropShadow.copyTo(parentNode)
dropShadow.setScale(toon.dropShadow.getScale(render))
trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=1.0)
flyDur = trajectory.calcTimeOfImpactOnPlane(0.0)
endTile = [rng.randint(0, self.numSquaresX - 1), rng.randint(0, self.numSquaresY - 1)]
endWorldCoords = (self.getX(render) + endTile[0] * self.spacingX, self.getY(render) + endTile[1] * self.spacingY)
endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2])
def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon):
u = t / dur
moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0]))
moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1]))
if flyNode and not flyNode.isEmpty():
flyNode.setPos(trajectory.getPos(t))
def safeSetHpr(node, hpr):
if node and not node.isEmpty():
node.setHpr(hpr)
flyTrack = Sequence(LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly'))
if avId != localAvatar.doId:
cameraTrack = Sequence()
else:
base.localAvatar.stopUpdateSmartCamera()
self.camParentHold = camera.getParent()
self.camParent = base.localAvatar.attachNewNode('iCamParent')
self.camParent.setPos(self.camParentHold.getPos())
self.camParent.setHpr(self.camParentHold.getHpr())
camera.reparentTo(self.camParent)
self.camParent.reparentTo(parentNode)
startCamPos = camera.getPos()
destCamPos = camera.getPos()
zenith = trajectory.getPos(flyDur / 2.0)[2]
destCamPos.setZ(zenith * 1.3)
destCamPos.setY(destCamPos[1] * 0.3)
def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos):
u = flyNode.getZ() / zenith
camera.lookAt(toon)
return Task.cont
camTaskName = 'mazeToonFlyCam-' + `avId`
taskMgr.add(camTask, camTaskName, priority=20)
def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos):
taskMgr.remove(camTaskName)
self.camParent.reparentTo(toon)
camera.setPos(startCamPos)
camera.lookAt(toon)
camera.reparentTo(self.camParentHold)
base.localAvatar.startUpdateSmartCamera()
self.setUpCamera()
cameraTrack = Sequence(Wait(flyDur), Func(cleanupCamTask), name='hitBySuit-cameraLerp')
geomNode = toon.getGeomNode()
startHpr = geomNode.getHpr()
destHpr = Point3(startHpr)
hRot = rng.randrange(1, 8)
if rng.choice([0, 1]):
hRot = -hRot
destHpr.setX(destHpr[0] + hRot * 360)
spinHTrack = Sequence(LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(safeSetHpr, geomNode, startHpr), name=toon.uniqueName('hitBySuit-spinH'))
parent = geomNode.getParent()
rotNode = parent.attachNewNode('rotNode')
geomNode.reparentTo(rotNode)
rotNode.setZ(toon.getHeight() / 2.0)
oldGeomNodeZ = geomNode.getZ()
geomNode.setZ(-toon.getHeight() / 2.0)
startHpr = rotNode.getHpr()
destHpr = Point3(startHpr)
pRot = rng.randrange(1, 3)
if rng.choice([0, 1]):
pRot = -pRot
destHpr.setY(destHpr[1] + pRot * 360)
spinPTrack = Sequence(LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(safeSetHpr, rotNode, startHpr), name=toon.uniqueName('hitBySuit-spinP'))
soundTrack = Sequence()
def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow):
forwardSpeed = toon.forwardSpeed
rotateSpeed = toon.rotateSpeed
if avId == localAvatar.doId:
toon.stopSmooth()
base.cr.playGame.getPlace().fsm.request('stopped')
else:
toon.stopSmooth()
if forwardSpeed or rotateSpeed:
toon.setSpeed(forwardSpeed, rotateSpeed)
toon.dropShadow.hide()
def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode):
if avId == localAvatar.doId:
base.localAvatar.setPos(endPos)
if hasattr(self, 'orthoWalk'):
self.orthoWalk.start()
dropShadow.removeNode()
del dropShadow
if toon and toon.dropShadow:
toon.dropShadow.show()
geomNode = toon.getGeomNode()
rotNode = geomNode.getParent()
baseNode = rotNode.getParent()
geomNode.reparentTo(baseNode)
rotNode.removeNode()
del rotNode
geomNode.setZ(oldGeomNodeZ)
toon.reparentTo(render)
toon.setPos(endPos)
parentNode.removeNode()
del parentNode
if avId == localAvatar.doId:
toon.startSmooth()
place = base.cr.playGame.getPlace()
if place and hasattr(place, 'fsm'):
place.fsm.request('walk')
else:
toon.startSmooth()
preFunc()
hitTrack = Sequence(Func(toon.setPos, Point3(0.0, 0.0, 0.0)), Wait(0.25), Parallel(flyTrack, cameraTrack, self.soundIUpDown, spinHTrack, spinPTrack, soundTrack), Func(postFunc), name=toon.uniqueName('hitBySuit'))
self.toonHitTracks[avId] = hitTrack
hitTrack.start()
posM = moleHill.getPos(render)
posN = Point3(posM[0], posM[1], posM[2] + 4.0)
self.soundBomb.play()
self.soundBomb2.play()
return