oldschool-toontown/toontown/coghq/DistributedBanquetTable.py

1145 lines
45 KiB
Python
Raw Normal View History

2019-11-02 17:27:54 -05:00
import math
import random
from pandac.PandaModules import NodePath, Point3, VBase4, TextNode, Vec3, deg2Rad, CollisionSegment, CollisionHandlerQueue, CollisionNode, BitMask32, SmoothMover
from direct.fsm import FSM
from direct.distributed import DistributedObject
from direct.distributed.ClockDelta import globalClockDelta
from direct.directnotify import DirectNotifyGlobal
from direct.interval.IntervalGlobal import Sequence, ProjectileInterval, Parallel, LerpHprInterval, ActorInterval, Func, Wait, SoundInterval, LerpPosHprInterval, LerpScaleInterval
from direct.gui.DirectGui import DGG, DirectButton, DirectLabel, DirectWaitBar
from direct.task import Task
from toontown.suit import Suit
from toontown.suit import SuitDNA
from toontown.toonbase import ToontownGlobals
from toontown.toonbase import TTLocalizer
from toontown.coghq import BanquetTableBase
from toontown.coghq import DinerStatusIndicator
from toontown.battle import MovieUtil
class DistributedBanquetTable(DistributedObject.DistributedObject, FSM.FSM, BanquetTableBase.BanquetTableBase):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBanquetTable')
rotationsPerSeatIndex = [90,
90,
0,
0,
-90,
-90,
180,
180]
pitcherMinH = -360
pitcherMaxH = 360
rotateSpeed = 30
waterPowerSpeed = base.config.GetDouble('water-power-speed', 15)
waterPowerExponent = base.config.GetDouble('water-power-exponent', 0.75)
useNewAnimations = True
TugOfWarControls = False
OnlyUpArrow = True
if OnlyUpArrow:
BASELINE_KEY_RATE = 3
else:
BASELINE_KEY_RATE = 6
UPDATE_KEY_PRESS_RATE_TASK = 'BanquetTableUpdateKeyPressRateTask'
YELLOW_POWER_THRESHOLD = 0.75
RED_POWER_THRESHOLD = 0.97
def __init__(self, cr):
DistributedObject.DistributedObject.__init__(self, cr)
FSM.FSM.__init__(self, 'DistributedBanquetTable')
self.boss = None
self.index = -1
self.diners = {}
self.dinerStatus = {}
self.serviceLocs = {}
self.chairLocators = {}
self.sitLocators = {}
self.activeIntervals = {}
self.dinerStatusIndicators = {}
self.preparedForPhaseFour = False
self.avId = 0
self.toon = None
self.pitcherSmoother = SmoothMover()
self.pitcherSmoother.setSmoothMode(SmoothMover.SMOn)
self.smoothStarted = 0
self.__broadcastPeriod = 0.2
self.changeSeq = 0
self.lastChangeSeq = 0
self.pitcherAdviceLabel = None
self.fireLength = 250
self.fireTrack = None
self.hitObject = None
self.setupPowerBar()
self.aimStart = None
self.toonPitcherPosition = Point3(0, -2, 0)
self.allowLocalRequestControl = True
self.fadeTrack = None
self.grabTrack = None
self.gotHitByBoss = False
self.keyTTL = []
self.keyRate = 0
self.buttons = [0, 1]
self.lastPowerFired = 0
self.moveSound = None
self.releaseTrack = None
return
def disable(self):
DistributedObject.DistributedObject.disable(self)
taskMgr.remove(self.triggerName)
taskMgr.remove(self.smoothName)
taskMgr.remove(self.watchControlsName)
taskMgr.remove(self.pitcherAdviceName)
taskMgr.remove(self.posHprBroadcastName)
taskMgr.remove(self.waterPowerTaskName)
if self.releaseTrack:
self.releaseTrack.finish()
self.releaseTrack = None
if self.fireTrack:
self.fireTrack.finish()
self.fireTrack = None
self.cleanupIntervals()
return
def delete(self):
DistributedObject.DistributedObject.delete(self)
self.boss = None
self.ignoreAll()
for indicator in list(self.dinerStatusIndicators.values()):
2019-11-02 17:27:54 -05:00
indicator.delete()
self.dinerStatusIndicators = {}
for diner in list(self.diners.values()):
2019-11-02 17:27:54 -05:00
diner.delete()
self.diners = {}
self.powerBar.destroy()
self.powerBar = None
self.pitcherMoveSfx.stop()
return
def announceGenerate(self):
DistributedObject.DistributedObject.announceGenerate(self)
self.loadAssets()
self.smoothName = self.uniqueName('pitcherSmooth')
self.pitcherAdviceName = self.uniqueName('pitcherAdvice')
self.posHprBroadcastName = self.uniqueName('pitcherBroadcast')
self.waterPowerTaskName = self.uniqueName('updateWaterPower')
self.triggerName = self.uniqueName('trigger')
self.watchControlsName = self.uniqueName('watchControls')
def setBossCogId(self, bossCogId):
self.bossCogId = bossCogId
self.boss = base.cr.doId2do[bossCogId]
self.boss.setTable(self, self.index)
def setIndex(self, index):
self.index = index
def setState(self, state, avId, extraInfo):
self.gotHitByBoss = extraInfo
if state == 'F':
self.demand('Off')
elif state == 'N':
self.demand('On')
elif state == 'I':
self.demand('Inactive')
elif state == 'R':
self.demand('Free')
elif state == 'C':
self.demand('Controlled', avId)
elif state == 'L':
self.demand('Flat', avId)
else:
self.notify.error('Invalid state from AI: %s' % state)
def setNumDiners(self, numDiners):
self.numDiners = numDiners
def setDinerInfo(self, hungryDurations, eatingDurations, dinerLevels):
self.dinerInfo = {}
for i in range(len(hungryDurations)):
2019-11-02 17:27:54 -05:00
hungryDur = hungryDurations[i]
eatingDur = eatingDurations[i]
dinerLevel = dinerLevels[i]
self.dinerInfo[i] = (hungryDur, eatingDur, dinerLevel)
def loadAssets(self):
self.tableGroup = loader.loadModel('phase_12/models/bossbotHQ/BanquetTableChairs')
tableLocator = self.boss.geom.find('**/TableLocator_%d' % (self.index + 1))
if tableLocator.isEmpty():
self.tableGroup.reparentTo(render)
self.tableGroup.setPos(0, 75, 0)
else:
self.tableGroup.reparentTo(tableLocator)
self.tableGeom = self.tableGroup.find('**/Geometry')
self.setupDiners()
self.setupChairCols()
self.squirtSfx = loader.loadSfx('phase_4/audio/sfx/AA_squirt_seltzer_miss.mp3')
self.hitBossSfx = loader.loadSfx('phase_5/audio/sfx/SA_watercooler_spray_only.mp3')
self.hitBossSoundInterval = SoundInterval(self.hitBossSfx, node=self.boss, volume=1.0)
self.serveFoodSfx = loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_bell_for_trolley.mp3')
self.pitcherMoveSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3')
2019-11-02 17:27:54 -05:00
def setupDiners(self):
for i in range(self.numDiners):
2019-11-02 17:27:54 -05:00
newDiner = self.createDiner(i)
self.diners[i] = newDiner
self.dinerStatus[i] = self.HUNGRY
def createDiner(self, i):
diner = Suit.Suit()
diner.dna = SuitDNA.SuitDNA()
level = self.dinerInfo[i][2]
level -= 4
diner.dna.newSuitRandom(level=level, dept='c')
diner.setDNA(diner.dna)
if self.useNewAnimations:
diner.loop('sit', fromFrame=i)
else:
diner.pose('landing', 0)
locator = self.tableGroup.find('**/chair_%d' % (i + 1))
locatorScale = locator.getNetTransform().getScale()[0]
correctHeadingNp = locator.attachNewNode('correctHeading')
self.chairLocators[i] = correctHeadingNp
heading = self.rotationsPerSeatIndex[i]
correctHeadingNp.setH(heading)
sitLocator = correctHeadingNp.attachNewNode('sitLocator')
base.sitLocator = sitLocator
pos = correctHeadingNp.getPos(render)
if SuitDNA.getSuitBodyType(diner.dna.name) == 'c':
sitLocator.setPos(0.5, 3.65, -3.75)
else:
sitLocator.setZ(-2.4)
sitLocator.setY(2.5)
sitLocator.setX(0.5)
self.sitLocators[i] = sitLocator
diner.setScale(1.0 / locatorScale)
diner.reparentTo(sitLocator)
newLoc = NodePath('serviceLoc-%d-%d' % (self.index, i))
newLoc.reparentTo(correctHeadingNp)
newLoc.setPos(0, 3.0, 1)
self.serviceLocs[i] = newLoc
base.serviceLoc = newLoc
head = diner.find('**/joint_head')
newIndicator = DinerStatusIndicator.DinerStatusIndicator(parent=head, pos=Point3(0, 0, 3.5), scale=5.0)
newIndicator.wrtReparentTo(diner)
self.dinerStatusIndicators[i] = newIndicator
return diner
def setupChairCols(self):
for i in range(self.numDiners):
2019-11-02 17:27:54 -05:00
chairCol = self.tableGroup.find('**/collision_chair_%d' % (i + 1))
colName = 'ChairCol-%d-%d' % (self.index, i)
chairCol.setTag('chairIndex', str(i))
chairCol.setName(colName)
chairCol.setCollideMask(ToontownGlobals.WallBitmask)
self.accept('enter' + colName, self.touchedChair)
def touchedChair(self, colEntry):
chairIndex = int(colEntry.getIntoNodePath().getTag('chairIndex'))
if chairIndex in self.dinerStatus:
status = self.dinerStatus[chairIndex]
if status in (self.HUNGRY, self.ANGRY):
self.boss.localToonTouchedChair(self.index, chairIndex)
def serveFood(self, food, chairIndex):
self.removeFoodModel(chairIndex)
serviceLoc = self.serviceLocs.get(chairIndex)
if not food or food.isEmpty():
foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood')
foodModel.setScale(ToontownGlobals.BossbotFoodModelScale)
foodModel.reparentTo(serviceLoc)
else:
food.wrtReparentTo(serviceLoc)
tray = food.find('**/tray')
if not tray.isEmpty():
tray.hide()
ivalDuration = 1.5
foodMoveIval = Parallel(SoundInterval(self.serveFoodSfx, node=food), ProjectileInterval(food, duration=ivalDuration, startPos=food.getPos(serviceLoc), endPos=serviceLoc.getPos(serviceLoc)), LerpHprInterval(food, ivalDuration, Point3(0, -360, 0)))
intervalName = 'serveFood-%d-%d' % (self.index, chairIndex)
foodMoveIval.start()
self.activeIntervals[intervalName] = foodMoveIval
def setDinerStatus(self, chairIndex, status):
if chairIndex in self.dinerStatus:
oldStatus = self.dinerStatus[chairIndex]
self.dinerStatus[chairIndex] = status
if oldStatus != status:
if status == self.EATING:
self.changeDinerToEating(chairIndex)
elif status == self.HUNGRY:
self.changeDinerToHungry(chairIndex)
elif status == self.ANGRY:
self.changeDinerToAngry(chairIndex)
elif status == self.DEAD:
self.changeDinerToDead(chairIndex)
elif status == self.HIDDEN:
self.changeDinerToHidden(chairIndex)
def removeFoodModel(self, chairIndex):
serviceLoc = self.serviceLocs.get(chairIndex)
if serviceLoc:
for i in range(serviceLoc.getNumChildren()):
2019-11-02 17:27:54 -05:00
serviceLoc.getChild(0).removeNode()
def changeDinerToEating(self, chairIndex):
indicator = self.dinerStatusIndicators.get(chairIndex)
eatingDuration = self.dinerInfo[chairIndex][1]
if indicator:
indicator.request('Eating', eatingDuration)
diner = self.diners[chairIndex]
intervalName = 'eating-%d-%d' % (self.index, chairIndex)
eatInTime = 32.0 / 24.0
eatOutTime = 21.0 / 24.0
eatLoopTime = 19 / 24.0
rightHand = diner.getRightHand()
waitTime = 5
loopDuration = eatingDuration - eatInTime - eatOutTime - waitTime
serviceLoc = self.serviceLocs[chairIndex]
def foodAttach(self = self, diner = diner):
foodModel = self.serviceLocs[chairIndex].getChild(0)
(foodModel.reparentTo(diner.getRightHand()),)
(foodModel.setHpr(Point3(0, -94, 0)),)
(foodModel.setPos(Point3(-0.15, -0.7, -0.4)),)
scaleAdj = 1
if SuitDNA.getSuitBodyType(diner.dna.name) == 'c':
scaleAdj = 0.6
(foodModel.setPos(Point3(0.1, -0.25, -0.31)),)
else:
scaleAdj = 0.8
(foodModel.setPos(Point3(-0.25, -0.85, -0.34)),)
oldScale = foodModel.getScale()
newScale = oldScale * scaleAdj
foodModel.setScale(newScale)
def foodDetach(self = self, diner = diner):
foodModel = diner.getRightHand().getChild(0)
(foodModel.reparentTo(serviceLoc),)
(foodModel.setPosHpr(0, 0, 0, 0, 0, 0),)
scaleAdj = 1
if SuitDNA.getSuitBodyType(diner.dna.name) == 'c':
scaleAdj = 0.6
else:
scakeAdj = 0.8
oldScale = foodModel.getScale()
newScale = oldScale / scaleAdj
foodModel.setScale(newScale)
eatIval = Sequence(ActorInterval(diner, 'sit', duration=waitTime), ActorInterval(diner, 'sit-eat-in', startFrame=0, endFrame=6), Func(foodAttach), ActorInterval(diner, 'sit-eat-in', startFrame=6, endFrame=32), ActorInterval(diner, 'sit-eat-loop', duration=loopDuration, loop=1), ActorInterval(diner, 'sit-eat-out', startFrame=0, endFrame=12), Func(foodDetach), ActorInterval(diner, 'sit-eat-out', startFrame=12, endFrame=21))
eatIval.start()
self.activeIntervals[intervalName] = eatIval
def changeDinerToHungry(self, chairIndex):
intervalName = 'eating-%d-%d' % (self.index, chairIndex)
if intervalName in self.activeIntervals:
self.activeIntervals[intervalName].finish()
self.removeFoodModel(chairIndex)
indicator = self.dinerStatusIndicators.get(chairIndex)
if indicator:
indicator.request('Hungry', self.dinerInfo[chairIndex][0])
diner = self.diners[chairIndex]
if random.choice([0, 1]):
diner.loop('sit-hungry-left')
else:
diner.loop('sit-hungry-right')
def changeDinerToAngry(self, chairIndex):
self.removeFoodModel(chairIndex)
indicator = self.dinerStatusIndicators.get(chairIndex)
if indicator:
indicator.request('Angry')
diner = self.diners[chairIndex]
diner.loop('sit-angry')
def changeDinerToDead(self, chairIndex):
def removeDeathSuit(suit, deathSuit):
if not deathSuit.isEmpty():
deathSuit.detachNode()
suit.cleanupLoseActor()
self.removeFoodModel(chairIndex)
indicator = self.dinerStatusIndicators.get(chairIndex)
if indicator:
indicator.request('Dead')
diner = self.diners[chairIndex]
deathSuit = diner
locator = self.tableGroup.find('**/chair_%d' % (chairIndex + 1))
deathSuit = diner.getLoseActor()
ival = Sequence(Func(self.notify.debug, 'before actorinterval sit-lose'), ActorInterval(diner, 'sit-lose'), Func(self.notify.debug, 'before deathSuit.setHpr'), Func(deathSuit.setHpr, diner.getHpr()), Func(self.notify.debug, 'before diner.hide'), Func(diner.hide), Func(self.notify.debug, 'before deathSuit.reparentTo'), Func(deathSuit.reparentTo, self.chairLocators[chairIndex]), Func(self.notify.debug, 'befor ActorInterval lose'), ActorInterval(deathSuit, 'lose', duration=MovieUtil.SUIT_LOSE_DURATION), Func(self.notify.debug, 'before remove deathsuit'), Func(removeDeathSuit, diner, deathSuit, name='remove-death-suit-%d-%d' % (chairIndex, self.index)), Func(self.notify.debug, 'diner.stash'), Func(diner.stash))
spinningSound = base.loader.loadSfx('phase_3.5/audio/sfx/Cog_Death.mp3')
deathSound = base.loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3')
2019-11-02 17:27:54 -05:00
deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2, node=deathSuit), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8, node=deathSuit), SoundInterval(deathSound, volume=0.32, node=deathSuit))
intervalName = 'dinerDie-%d-%d' % (self.index, chairIndex)
deathIval = Parallel(ival, deathSoundTrack)
deathIval.start()
self.activeIntervals[intervalName] = deathIval
def changeDinerToHidden(self, chairIndex):
self.removeFoodModel(chairIndex)
indicator = self.dinerStatusIndicators.get(chairIndex)
if indicator:
indicator.request('Inactive')
diner = self.diners[chairIndex]
diner.hide()
def setAllDinersToSitNeutral(self):
startFrame = 0
for diner in list(self.diners.values()):
2019-11-02 17:27:54 -05:00
if not diner.isHidden():
diner.loop('sit', fromFrame=startFrame)
startFrame += 1
def cleanupIntervals(self):
for interval in list(self.activeIntervals.values()):
2019-11-02 17:27:54 -05:00
interval.finish()
self.activeIntervals = {}
def clearInterval(self, name, finish = 1):
if name in self.activeIntervals:
2019-11-02 17:27:54 -05:00
ival = self.activeIntervals[name]
if finish:
ival.finish()
else:
ival.pause()
if name in self.activeIntervals:
2019-11-02 17:27:54 -05:00
del self.activeIntervals[name]
else:
self.notify.debug('interval: %s already cleared' % name)
def finishInterval(self, name):
if name in self.activeIntervals:
2019-11-02 17:27:54 -05:00
interval = self.activeIntervals[name]
interval.finish()
def getNotDeadInfo(self):
notDeadList = []
for i in range(self.numDiners):
2019-11-02 17:27:54 -05:00
if self.dinerStatus[i] != self.DEAD:
notDeadList.append((self.index, i, 12))
return notDeadList
def enterOn(self):
pass
def exitOn(self):
pass
def enterInactive(self):
for chairIndex in range(self.numDiners):
2019-11-02 17:27:54 -05:00
indicator = self.dinerStatusIndicators.get(chairIndex)
if indicator:
indicator.request('Inactive')
self.removeFoodModel(chairIndex)
def exitInactive(self):
pass
def enterFree(self):
self.resetPowerBar()
if self.fadeTrack:
self.fadeTrack.finish()
self.fadeTrack = None
self.prepareForPhaseFour()
if self.avId == localAvatar.doId:
self.tableGroup.setAlphaScale(0.3)
self.tableGroup.setTransparency(1)
taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName)
self.fadeTrack = Sequence(Func(self.tableGroup.setTransparency, 1), self.tableGroup.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3)))
self.fadeTrack.start()
self.allowLocalRequestControl = False
else:
self.allowLocalRequestControl = True
self.avId = 0
return
def exitFree(self):
pass
def touchedTable(self, colEntry):
tableIndex = int(colEntry.getIntoNodePath().getTag('tableIndex'))
if self.state == 'Free' and self.avId == 0 and self.allowLocalRequestControl:
self.d_requestControl()
def prepareForPhaseFour(self):
if not self.preparedForPhaseFour:
for i in range(8):
2019-11-02 17:27:54 -05:00
chair = self.tableGroup.find('**/chair_%d' % (i + 1))
if not chair.isEmpty():
chair.hide()
colChairs = self.tableGroup.findAllMatches('**/ChairCol*')
for i in range(colChairs.getNumPaths()):
2019-11-02 17:27:54 -05:00
col = colChairs.getPath(i)
col.stash()
colChairs = self.tableGroup.findAllMatches('**/collision_chair*')
for i in range(colChairs.getNumPaths()):
2019-11-02 17:27:54 -05:00
col = colChairs.getPath(i)
col.stash()
tableCol = self.tableGroup.find('**/collision_table')
colName = 'TableCol-%d' % self.index
tableCol.setTag('tableIndex', str(self.index))
tableCol.setName(colName)
tableCol.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.BanquetTableBitmask)
self.accept('enter' + colName, self.touchedTable)
self.preparedForPhaseFour = True
self.waterPitcherModel = loader.loadModel('phase_12/models/bossbotHQ/tt_m_ara_bhq_seltzerBottle')
lampNode = self.tableGroup.find('**/lamp_med_5')
pos = lampNode.getPos(self.tableGroup)
lampNode.hide()
bottleLocator = self.tableGroup.find('**/bottle_locator')
pos = bottleLocator.getPos(self.tableGroup)
self.waterPitcherNode = self.tableGroup.attachNewNode('pitcherNode')
self.waterPitcherNode.setPos(pos)
self.waterPitcherModel.reparentTo(self.waterPitcherNode)
self.waterPitcherModel.ls()
self.nozzle = self.waterPitcherModel.find('**/nozzle_tip')
self.handLocator = self.waterPitcherModel.find('**/hand_locator')
self.handPos = self.handLocator.getPos()
def d_requestControl(self):
self.sendUpdate('requestControl')
def d_requestFree(self, gotHitByBoss):
self.sendUpdate('requestFree', [gotHitByBoss])
def enterControlled(self, avId):
self.prepareForPhaseFour()
self.avId = avId
toon = base.cr.doId2do.get(avId)
if not toon:
return
self.toon = toon
self.grabTrack = self.makeToonGrabInterval(toon)
self.notify.debug('grabTrack=%s' % self.grabTrack)
self.pitcherCamPos = Point3(0, -50, 40)
self.pitcherCamHpr = Point3(0, -21, 0)
if avId == localAvatar.doId:
self.boss.toMovieMode()
self.__enableControlInterface()
self.startPosHprBroadcast()
self.grabTrack = Sequence(self.grabTrack, Func(camera.wrtReparentTo, localAvatar), LerpPosHprInterval(camera, 1, self.pitcherCamPos, self.pitcherCamHpr), Func(self.boss.toCraneMode))
if self.TugOfWarControls:
self.__spawnUpdateKeyPressRateTask()
self.accept('exitCrane', self.gotBossZapped)
else:
self.startSmooth()
toon.stopSmooth()
self.grabTrack.start()
def exitControlled(self):
self.ignore('exitCrane')
if self.grabTrack:
self.grabTrack.finish()
self.grabTrack = None
nextState = self.getCurrentOrNextState()
self.notify.debug('nextState=%s' % nextState)
if nextState == 'Flat':
place = base.cr.playGame.getPlace()
self.notify.debug('%s' % place.fsm)
if self.avId == localAvatar.doId:
self.__disableControlInterface()
else:
if self.toon and not self.toon.isDisabled():
self.toon.loop('neutral')
self.toon.startSmooth()
self.releaseTrack = self.makeToonReleaseInterval(self.toon)
self.stopPosHprBroadcast()
self.stopSmooth()
if self.avId == localAvatar.doId:
localAvatar.wrtReparentTo(render)
self.__disableControlInterface()
camera.reparentTo(base.localAvatar)
camera.setPos(base.localAvatar.cameraPositions[0][0])
camera.setHpr(0, 0, 0)
self.goToFinalBattle()
self.safeBossToFinalBattleMode()
else:
toon = base.cr.doId2do.get(self.avId)
if toon:
toon.wrtReparentTo(render)
self.releaseTrack.start()
return
def safeBossToFinalBattleMode(self):
if self.boss:
self.boss.toFinalBattleMode()
def goToFinalBattle(self):
if self.cr:
place = self.cr.playGame.getPlace()
if place and hasattr(place, 'fsm'):
if place.fsm.getCurrentState().getName() == 'crane':
place.setState('finalBattle')
def makeToonGrabInterval(self, toon):
toon.pose('leverNeutral', 0)
toon.update()
rightHandPos = toon.rightHand.getPos(toon)
self.toonPitcherPosition = Point3(self.handPos[0] - rightHandPos[0], self.handPos[1] - rightHandPos[1], 0)
destZScale = rightHandPos[2] / self.handPos[2]
grabIval = Sequence(Func(toon.wrtReparentTo, self.waterPitcherNode), Func(toon.loop, 'neutral'), Parallel(ActorInterval(toon, 'jump'), Sequence(Wait(0.43), Parallel(ProjectileInterval(toon, duration=0.9, startPos=toon.getPos(self.waterPitcherNode), endPos=self.toonPitcherPosition), LerpHprInterval(toon, 0.9, Point3(0, 0, 0)), LerpScaleInterval(self.waterPitcherModel, 0.9, Point3(1, 1, destZScale))))), Func(toon.setPos, self.toonPitcherPosition), Func(toon.loop, 'leverNeutral'))
return grabIval
def makeToonReleaseInterval(self, toon):
temp1 = self.waterPitcherNode.attachNewNode('temp1')
temp1.setPos(self.toonPitcherPosition)
temp2 = self.waterPitcherNode.attachNewNode('temp2')
temp2.setPos(0, -10, -self.waterPitcherNode.getZ())
startPos = temp1.getPos(render)
endPos = temp2.getPos(render)
temp1.removeNode()
temp2.removeNode()
def getSlideToPos(toon = toon):
return render.getRelativePoint(toon, Point3(0, -10, 0))
if self.gotHitByBoss:
self.notify.debug('creating zap interval instead')
grabIval = Sequence(Func(toon.loop, 'neutral'), Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1)))
else:
grabIval = Sequence(Func(toon.loop, 'neutral'), Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'jump'), Sequence(Wait(0.43), ProjectileInterval(toon, duration=0.9, startPos=startPos, endPos=endPos))))
return grabIval
def b_clearSmoothing(self):
self.d_clearSmoothing()
self.clearSmoothing()
def d_clearSmoothing(self):
self.sendUpdate('clearSmoothing', [0])
def clearSmoothing(self, bogus = None):
self.pitcherSmoother.clearPositions(1)
def doSmoothTask(self, task):
self.pitcherSmoother.computeAndApplySmoothHpr(self.waterPitcherNode)
return Task.cont
def startSmooth(self):
if not self.smoothStarted:
taskName = self.smoothName
taskMgr.remove(taskName)
self.reloadPosition()
taskMgr.add(self.doSmoothTask, taskName)
self.smoothStarted = 1
def stopSmooth(self):
if self.smoothStarted:
taskName = self.smoothName
taskMgr.remove(taskName)
self.forceToTruePosition()
self.smoothStarted = 0
def __enableControlInterface(self):
gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui')
self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'),
gui.find('**/CloseBtn_DN'),
gui.find('**/CloseBtn_Rllvr'),
gui.find('**/CloseBtn_UP')), relief=None, scale=2, text=TTLocalizer.BossbotPitcherLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitPitcher)
self.accept('escape', self.__exitPitcher)
self.accept('control', self.__controlPressed)
self.accept('control-up', self.__controlReleased)
self.accept('InputState-forward', self.__upArrow)
self.accept('InputState-reverse', self.__downArrow)
self.accept('InputState-turnLeft', self.__leftArrow)
self.accept('InputState-turnRight', self.__rightArrow)
self.accept('arrow_up', self.__upArrowKeyPressed)
self.accept('arrow_down', self.__downArrowKeyPressed)
taskMgr.add(self.__watchControls, self.watchControlsName)
taskMgr.doMethodLater(5, self.__displayPitcherAdvice, self.pitcherAdviceName)
self.arrowVert = 0
self.arrowHorz = 0
self.powerBar.show()
return
def __disableControlInterface(self):
if self.closeButton:
self.closeButton.destroy()
self.closeButton = None
self.__cleanupPitcherAdvice()
self.ignore('escape')
self.ignore('control')
self.ignore('control-up')
self.ignore('InputState-forward')
self.ignore('InputState-reverse')
self.ignore('InputState-turnLeft')
self.ignore('InputState-turnRight')
self.ignore('arrow_up')
self.ignore('arrow_down')
self.arrowVert = 0
self.arrowHorz = 0
taskMgr.remove(self.watchControlsName)
taskMgr.remove(self.waterPowerTaskName)
self.resetPowerBar()
self.aimStart = None
self.powerBar.hide()
if self.TugOfWarControls:
self.__killUpdateKeyPressRateTask()
self.keyTTL = []
self.__setMoveSound(None)
return
def __displayPitcherAdvice(self, task):
if self.pitcherAdviceLabel == None:
self.pitcherAdviceLabel = DirectLabel(text=TTLocalizer.BossbotPitcherAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1)
return
def __cleanupPitcherAdvice(self):
if self.pitcherAdviceLabel:
self.pitcherAdviceLabel.destroy()
self.pitcherAdviceLabel = None
taskMgr.remove(self.pitcherAdviceName)
return
def showExiting(self):
if self.closeButton:
self.closeButton.destroy()
self.closeButton = DirectLabel(relief=None, text=TTLocalizer.BossbotPitcherLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1))
self.__cleanupPitcherAdvice()
return
def __exitPitcher(self):
self.showExiting()
self.d_requestFree(False)
def __controlPressed(self):
self.__cleanupPitcherAdvice()
if self.TugOfWarControls:
if self.power:
self.aimStart = 1
self.__endFireWater()
elif self.state == 'Controlled':
self.__beginFireWater()
def __controlReleased(self):
if self.TugOfWarControls:
pass
elif self.state == 'Controlled':
self.__endFireWater()
def __upArrow(self, pressed):
self.__incrementChangeSeq()
self.__cleanupPitcherAdvice()
if pressed:
self.arrowVert = 1
elif self.arrowVert > 0:
self.arrowVert = 0
def __downArrow(self, pressed):
self.__incrementChangeSeq()
self.__cleanupPitcherAdvice()
if pressed:
self.arrowVert = -1
elif self.arrowVert < 0:
self.arrowVert = 0
def __rightArrow(self, pressed):
self.__incrementChangeSeq()
self.__cleanupPitcherAdvice()
if pressed:
self.arrowHorz = 1
elif self.arrowHorz > 0:
self.arrowHorz = 0
def __leftArrow(self, pressed):
self.__incrementChangeSeq()
self.__cleanupPitcherAdvice()
if pressed:
self.arrowHorz = -1
elif self.arrowHorz < 0:
self.arrowHorz = 0
def __incrementChangeSeq(self):
self.changeSeq = self.changeSeq + 1 & 255
def stopPosHprBroadcast(self):
taskName = self.posHprBroadcastName
taskMgr.remove(taskName)
def startPosHprBroadcast(self):
taskName = self.posHprBroadcastName
self.b_clearSmoothing()
self.d_sendPitcherPos()
taskMgr.remove(taskName)
taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName)
def __posHprBroadcast(self, task):
self.d_sendPitcherPos()
taskName = self.posHprBroadcastName
taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName)
return Task.done
def d_sendPitcherPos(self):
timestamp = globalClockDelta.getFrameNetworkTime()
self.sendUpdate('setPitcherPos', [self.changeSeq, self.waterPitcherNode.getH(), timestamp])
def setPitcherPos(self, changeSeq, h, timestamp):
self.changeSeq = changeSeq
if self.smoothStarted:
now = globalClock.getFrameTime()
local = globalClockDelta.networkToLocalTime(timestamp, now)
self.pitcherSmoother.setH(h)
self.pitcherSmoother.setTimestamp(local)
self.pitcherSmoother.markPosition()
else:
self.waterPitcherNode.setH(h)
def __watchControls(self, task):
if self.arrowHorz:
self.__movePitcher(self.arrowHorz)
else:
self.__setMoveSound(None)
return Task.cont
def __movePitcher(self, xd):
dt = globalClock.getDt()
h = self.waterPitcherNode.getH() - xd * self.rotateSpeed * dt
h %= 360
self.notify.debug('rotSpeed=%.2f curH=%.2f xd =%.2f, dt = %.2f, h=%.2f' % (self.rotateSpeed,
self.waterPitcherNode.getH(),
xd,
dt,
h))
limitH = h
self.waterPitcherNode.setH(limitH)
if xd:
self.__setMoveSound(self.pitcherMoveSfx)
def reloadPosition(self):
self.pitcherSmoother.clearPositions(0)
self.pitcherSmoother.setHpr(self.waterPitcherNode.getHpr())
self.pitcherSmoother.setPhonyTimestamp()
def forceToTruePosition(self):
if self.pitcherSmoother.getLatestPosition():
self.pitcherSmoother.applySmoothHpr(self.waterPitcherNode)
self.pitcherSmoother.clearPositions(1)
def getSprayTrack(self, color, origin, target, dScaleUp, dHold, dScaleDown, horizScale = 1.0, vertScale = 1.0, parent = render):
track = Sequence()
SPRAY_LEN = 1.5
sprayProp = MovieUtil.globalPropPool.getProp('spray')
sprayScale = hidden.attachNewNode('spray-parent')
sprayRot = hidden.attachNewNode('spray-rotate')
spray = sprayRot
spray.setColor(color)
if color[3] < 1.0:
spray.setTransparency(1)
def showSpray(sprayScale, sprayRot, sprayProp, origin, target, parent):
if callable(origin):
origin = origin()
if callable(target):
target = target()
sprayRot.reparentTo(parent)
sprayRot.clearMat()
sprayScale.reparentTo(sprayRot)
sprayScale.clearMat()
sprayProp.reparentTo(sprayScale)
sprayProp.clearMat()
sprayRot.setPos(origin)
sprayRot.lookAt(Point3(target))
track.append(Func(showSpray, sprayScale, sprayRot, sprayProp, origin, target, parent))
def calcTargetScale(target = target, origin = origin, horizScale = horizScale, vertScale = vertScale):
if callable(target):
target = target()
if callable(origin):
origin = origin()
distance = Vec3(target - origin).length()
yScale = distance / SPRAY_LEN
targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale)
return targetScale
track.append(LerpScaleInterval(sprayScale, dScaleUp, calcTargetScale, startScale=Point3(0.01, 0.01, 0.01)))
track.append(Func(self.checkHitObject))
track.append(Wait(dHold))
def prepareToShrinkSpray(spray, sprayProp, origin, target):
if callable(target):
target = target()
if callable(origin):
origin = origin()
sprayProp.setPos(Point3(0.0, -SPRAY_LEN, 0.0))
spray.setPos(target)
track.append(Func(prepareToShrinkSpray, spray, sprayProp, origin, target))
track.append(LerpScaleInterval(sprayScale, dScaleDown, Point3(0.01, 0.01, 0.01)))
def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool):
sprayProp.detachNode()
MovieUtil.removeProp(sprayProp)
sprayRot.removeNode()
sprayScale.removeNode()
track.append(Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, MovieUtil.globalPropPool))
return track
def checkHitObject(self):
if not self.hitObject:
return
if self.avId != base.localAvatar.doId:
return
tag = self.hitObject.getNetTag('pieCode')
pieCode = int(tag)
if pieCode == ToontownGlobals.PieCodeBossCog:
self.hitBossSoundInterval.start()
self.sendUpdate('waterHitBoss', [self.index])
if self.TugOfWarControls:
damage = 1
if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD:
damage = 1
elif self.lastPowerFired < self.RED_POWER_THRESHOLD:
damage = 2
else:
damage = 3
self.boss.d_hitBoss(damage)
else:
damage = 1
if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD:
damage = 1
elif self.lastPowerFired < self.RED_POWER_THRESHOLD:
damage = 2
else:
damage = 3
self.boss.d_hitBoss(damage)
def waterHitBoss(self, tableIndex):
if self.index == tableIndex:
self.hitBossSoundInterval.start()
def setupPowerBar(self):
self.powerBar = DirectWaitBar(pos=(0.0, 0, -0.94), relief=DGG.SUNKEN, frameSize=(-2.0,
2.0,
-0.2,
0.2), borderWidth=(0.02, 0.02), scale=0.25, range=1, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(0.75, 0.75, 1.0, 0.8), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05))
self.power = 0
self.powerBar['value'] = self.power
self.powerBar.hide()
def resetPowerBar(self):
self.power = 0
self.powerBar['value'] = self.power
self.powerBar['text'] = ''
self.keyTTL = []
def __beginFireWater(self):
if self.fireTrack and self.fireTrack.isPlaying():
return
if self.aimStart != None:
return
if not self.state == 'Controlled':
return
if not self.avId == localAvatar.doId:
return
time = globalClock.getFrameTime()
self.aimStart = time
messenger.send('wakeup')
taskMgr.add(self.__updateWaterPower, self.waterPowerTaskName)
return
def __endFireWater(self):
if self.aimStart == None:
return
if not self.state == 'Controlled':
return
if not self.avId == localAvatar.doId:
return
taskMgr.remove(self.waterPowerTaskName)
messenger.send('wakeup')
self.aimStart = None
origin = self.nozzle.getPos(render)
target = self.boss.getPos(render)
angle = deg2Rad(self.waterPitcherNode.getH() + 90)
x = math.cos(angle)
y = math.sin(angle)
fireVector = Point3(x, y, 0)
if self.power < 0.001:
self.power = 0.001
self.lastPowerFired = self.power
fireVector *= self.fireLength * self.power
target = origin + fireVector
segment = CollisionSegment(origin[0], origin[1], origin[2], target[0], target[1], target[2])
fromObject = render.attachNewNode(CollisionNode('pitcherColNode'))
fromObject.node().addSolid(segment)
fromObject.node().setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask)
fromObject.node().setIntoCollideMask(BitMask32.allOff())
queue = CollisionHandlerQueue()
base.cTrav.addCollider(fromObject, queue)
base.cTrav.traverse(render)
queue.sortEntries()
self.hitObject = None
if queue.getNumEntries():
entry = queue.getEntry(0)
target = entry.getSurfacePoint(render)
self.hitObject = entry.getIntoNodePath()
base.cTrav.removeCollider(fromObject)
fromObject.removeNode()
self.d_firingWater(origin, target)
self.fireWater(origin, target)
self.resetPowerBar()
return
def __updateWaterPower(self, task):
if not self.powerBar:
print('### no power bar!!!')
2019-11-02 17:27:54 -05:00
return task.done
newPower = self.__getWaterPower(globalClock.getFrameTime())
self.power = newPower
self.powerBar['value'] = newPower
if self.power < self.YELLOW_POWER_THRESHOLD:
self.powerBar['barColor'] = VBase4(0.75, 0.75, 1.0, 0.8)
elif self.power < self.RED_POWER_THRESHOLD:
self.powerBar['barColor'] = VBase4(1.0, 1.0, 0.0, 0.8)
else:
self.powerBar['barColor'] = VBase4(1.0, 0.0, 0.0, 0.8)
return task.cont
def __getWaterPower(self, time):
elapsed = max(time - self.aimStart, 0.0)
t = elapsed / self.waterPowerSpeed
exponent = self.waterPowerExponent
if t > 1:
t = t % 1
power = 1 - math.pow(1 - t, exponent)
if power > 1.0:
power = 1.0
return power
def d_firingWater(self, origin, target):
self.sendUpdate('firingWater', [origin[0],
origin[1],
origin[2],
target[0],
target[1],
target[2]])
def firingWater(self, startX, startY, startZ, endX, endY, endZ):
origin = Point3(startX, startY, startZ)
target = Point3(endX, endY, endZ)
self.fireWater(origin, target)
def fireWater(self, origin, target):
color = VBase4(0.75, 0.75, 1, 0.8)
dScaleUp = 0.1
dHold = 0.3
dScaleDown = 0.1
horizScale = 0.1
vertScale = 0.1
sprayTrack = self.getSprayTrack(color, origin, target, dScaleUp, dHold, dScaleDown, horizScale, vertScale)
duration = self.squirtSfx.length()
if sprayTrack.getDuration() < duration:
duration = sprayTrack.getDuration()
soundTrack = SoundInterval(self.squirtSfx, node=self.waterPitcherModel, duration=duration)
self.fireTrack = Parallel(sprayTrack, soundTrack)
self.fireTrack.start()
def getPos(self, wrt = render):
return self.tableGroup.getPos(wrt)
def getLocator(self):
return self.tableGroup
def enterFlat(self, avId):
self.prepareForPhaseFour()
self.resetPowerBar()
self.notify.debug('enterFlat %d' % self.index)
if self.avId:
toon = base.cr.doId2do.get(self.avId)
if toon:
toon.wrtReparentTo(render)
toon.setZ(0)
self.tableGroup.setScale(1, 1, 0.01)
if self.avId and self.avId == localAvatar.doId:
localAvatar.b_squish(ToontownGlobals.BossCogDamageLevels[ToontownGlobals.BossCogMoveAttack])
def exitFlat(self):
self.tableGroup.setScale(1.0)
if self.avId:
toon = base.cr.doId2do.get(self.avId)
if toon:
if toon == localAvatar:
self.boss.toCraneMode()
toon.b_setAnimState('neutral')
toon.setAnimState('neutral')
toon.loop('leverNeutral')
def __allowDetect(self, task):
if self.fadeTrack:
self.fadeTrack.finish()
self.fadeTrack = Sequence(self.tableGroup.colorScaleInterval(0.2, VBase4(1, 1, 1, 1)), Func(self.tableGroup.clearColorScale), Func(self.tableGroup.clearTransparency))
self.fadeTrack.start()
self.allowLocalRequestControl = True
def gotBossZapped(self):
self.showExiting()
self.d_requestFree(True)
def __upArrowKeyPressed(self):
if self.TugOfWarControls:
self.__pressHandler(0)
def __downArrowKeyPressed(self):
if self.TugOfWarControls:
self.__pressHandler(1)
def __pressHandler(self, index):
if index == self.buttons[0]:
self.keyTTL.insert(0, 1.0)
if not self.OnlyUpArrow:
self.buttons.reverse()
def __spawnUpdateKeyPressRateTask(self):
taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))
taskMgr.doMethodLater(0.1, self.__updateKeyPressRateTask, self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))
def __killUpdateKeyPressRateTask(self):
taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))
def __updateKeyPressRateTask(self, task):
if self.state not in 'Controlled':
return Task.done
for i in range(len(self.keyTTL)):
self.keyTTL[i] -= 0.1
for i in range(len(self.keyTTL)):
if self.keyTTL[i] <= 0:
a = self.keyTTL[0:i]
del self.keyTTL
self.keyTTL = a
break
self.keyRate = len(self.keyTTL)
keyRateDiff = self.keyRate - self.BASELINE_KEY_RATE
diffPower = keyRateDiff / 300.0
if self.power < 1 and diffPower > 0:
diffPower = diffPower * math.pow(1 - self.power, 1.25)
newPower = self.power + diffPower
if newPower > 1:
newPower = 1
elif newPower < 0:
newPower = 0
self.notify.debug('diffPower=%.2f keyRate = %d, newPower=%.2f' % (diffPower, self.keyRate, newPower))
self.power = newPower
self.powerBar['value'] = newPower
if self.power < self.YELLOW_POWER_THRESHOLD:
self.powerBar['barColor'] = VBase4(0.75, 0.75, 1.0, 0.8)
elif self.power < self.RED_POWER_THRESHOLD:
self.powerBar['barColor'] = VBase4(1.0, 1.0, 0.0, 0.8)
else:
self.powerBar['barColor'] = VBase4(1.0, 0.0, 0.0, 0.8)
self.__spawnUpdateKeyPressRateTask()
return Task.done
def __setMoveSound(self, sfx):
if sfx != self.moveSound:
if self.moveSound:
self.moveSound.stop()
self.moveSound = sfx
if self.moveSound:
base.playSfx(self.moveSound, looping=1, volume=0.5)