class DistributedPicnicBasket(DistributedObject.DistributedObject): seatState = Enum('Empty, Full, Eating') notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPicnicBasket') def __init__(self, cr): DistributedObject.DistributedObject.__init__(self, cr) self.localToonOnBoard = 0 self.seed = 0 self.random = None self.picnicCountdownTime = base.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) self.picnicBasketTrack = None self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [ State.State('off', self.enterOff, self.exitOff, [ 'waitEmpty', 'waitCountdown']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, [ 'waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, [ 'waitEmpty'])], 'off', 'off') self.fsm.enterInitialState() self._DistributedPicnicBasket__toonTracks = { } def generate(self): DistributedObject.DistributedObject.generate(self) self.loader = self.cr.playGame.hood.loader self.foodLoader = [ 'phase_6/models/golf/picnic_sandwich.bam', 'phase_6/models/golf/picnic_apple.bam', 'phase_6/models/golf/picnic_cupcake.bam', 'phase_6/models/golf/picnic_chocolate_cake.bam'] self.fullSeat = [] self.food = [] for i in range(4): self.food.append(None) self.fullSeat.append(self.seatState.Empty) self.picnicItem = 0 def announceGenerate(self): self.picnicTable = self.loader.geom.find('**/*picnic_table_' + str(self.tableNumber)) self.picnicTableSphereNodes = [] self.numSeats = 4 self.seats = [] self.jumpOffsets = [] self.basket = None for i in range(self.numSeats): self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1))) self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1))) self.tablecloth = self.picnicTable.find('**/basket_locator') DistributedObject.DistributedObject.announceGenerate(self) for i in range(self.numSeats): self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2)) self.tableclothSphereNode = self.tablecloth.attachNewNode(CollisionNode('tablecloth_sphere')) self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4)) angle = self.startingHpr[0] angle -= 90 radAngle = deg2Rad(angle) unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) unitVec *= 30.0 self.endPos = self.startingPos + unitVec dist = Vec3(self.endPos - self.enteringPos).length() wheelAngle = (dist / 0.5 * 1.3999999999999999 * math.pi) * 360 self.seatNumber = 0 self.clockNode = ToontownTimer() self.clockNode.setPos(1.1599999999999999, 0, -0.82999999999999996) self.clockNode.setScale(0.29999999999999999) self.clockNode.hide() def disable(self): DistributedObject.DistributedObject.disable(self) self.fsm.request('off') self.clearToonTracks() for i in range(self.numSeats): del self.picnicTableSphereNodes[0] del self.picnicTableSphereNodes self.notify.debug('Deleted self loader ' + str(self.getDoId())) self.picnicTable.removeNode() self.picnicBasketTrack = None def delete(self): self.notify.debug('Golf kart getting deleted: %s' % self.getDoId()) DistributedObject.DistributedObject.delete(self) del self.fsm def setState(self, state, seed, timestamp): self.seed = seed if not self.random: self.random = RandomNumGen.RandomNumGen(seed) self.fsm.request(state, [ globalClockDelta.localElapsedTime(timestamp)]) def handleEnterPicnicTableSphere(self, i, collEntry): self.seatNumber = i self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId()) self.loader.place.detectedPicnicTableSphereCollision(self) def handleEnterPicnicTable(self, i): toon = base.localAvatar self.sendUpdate('requestBoard', [ i]) def fillSlot0(self, avId): self.fillSlot(0, avId) def fillSlot1(self, avId): self.fillSlot(1, avId) def fillSlot2(self, avId): self.fillSlot(2, avId) def fillSlot3(self, avId): self.fillSlot(3, avId) def fillSlot(self, index, avId): self.notify.debug('fill Slot: %d for %d' % (index, avId)) if avId == 0: pass 1 self.fullSeat[index] = self.seatState.Full if avId == base.localAvatar.getDoId(): self.clockNode.show() if index == 0 or index == 3: side = -1 else: side = 1 if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.fsm.request('boarding', [ self.tablecloth, side]) else: self.notify.warning('fillSlot no trolley in place') self.localToonOnBoard = 1 if avId == base.localAvatar.getDoId(): if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.fsm.request('boarded') self.loader.place.trolley.exitButton.hide() if self.cr.doId2do.has_key(avId): toon = self.cr.doId2do[avId] toon.stopSmooth() toon.wrtReparentTo(self.tablecloth) sitStartDuration = toon.getDuration('sit-start') jumpTrack = self.generateToonJumpTrack(toon, index) track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0)) self.notify.debug('### fillSlot: fullSeat = %s' % self.fullSeat) if self.fullSeat.count(0) == 3: self.notify.debug('### fillSlot: adding basketAppear') if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketAppearTrack()) self.picnicBasketTrack.start() track.append(self.generateFoodAppearTrack(index)) track.append(Sequence(Func(self.clearToonTrack, avId), name = toon.uniqueName('fillTrolley'), autoPause = 1)) if avId == base.localAvatar.getDoId(): if hasattr(self.loader.place, 'trolley'): track.append(Func(self.loader.place.trolley.exitButton.show)) track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.fillSlot') self.storeToonTrack(avId, track) track.start() def emptySlot0(self, avId, timestamp): self.emptySlot(0, avId, timestamp) def emptySlot1(self, avId, timestamp): self.emptySlot(1, avId, timestamp) def emptySlot2(self, avId, timestamp): self.emptySlot(2, avId, timestamp) def emptySlot3(self, avId, timestamp): self.emptySlot(3, avId, timestamp) def notifyToonOffTrolley(self, toon): toon.setAnimState('neutral', 1.0) if hasattr(base, 'localAvatar') and toon == base.localAvatar: if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.handleOffTrolley() self.localToonOnBoard = 0 else: toon.startSmooth() def emptySlot(self, index, avId, timestamp): def emptySeat(index): self.notify.debug('### seat %s now empty' % index) self.fullSeat[index] = self.seatState.Empty if avId == 0: pass 1 if avId == 1: self.fullSeat[index] = self.seatState.Empty track = Sequence(self.generateFoodDisappearTrack(index)) self.notify.debug('### empty slot - unexpetected: fullSeat = %s' % self.fullSeat) if self.fullSeat.count(0) == 4: self.notify.debug('### empty slot - unexpected: losing basket') if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack()) self.picnicBasketTrack.start() track.start() else: self.fullSeat[index] = self.seatState.Empty if self.cr.doId2do.has_key(avId): if avId == base.localAvatar.getDoId(): if self.clockNode: self.clockNode.hide() toon = self.cr.doId2do[avId] toon.stopSmooth() sitStartDuration = toon.getDuration('sit-start') jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) track = Sequence(jumpOutTrack) track.append(self.generateFoodDisappearTrack(index)) self.notify.debug('### empty slot: fullSeat = %s' % self.fullSeat) if self.fullSeat.count(0) == 4: self.notify.debug('### empty slot: losing basket') if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack()) self.picnicBasketTrack.start() track.append(Sequence(Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), Func(self.doneExit, avId), Func(emptySeat, index), name = toon.uniqueName('emptyTrolley'), autoPause = 1)) track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.emptySlot') self.storeToonTrack(avId, track) track.start() def rejectBoard(self, avId): self.loader.place.trolley.handleRejectBoard() def _DistributedPicnicBasket__enableCollisions(self): for i in range(self.numSeats): self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [ i]) self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [ i]) self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask) def _DistributedPicnicBasket__disableCollisions(self): for i in range(self.numSeats): self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) for i in range(self.numSeats): self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) def enterOff(self): pass def exitOff(self): pass def enterWaitEmpty(self, ts): self._DistributedPicnicBasket__enableCollisions() def exitWaitEmpty(self): self._DistributedPicnicBasket__disableCollisions() def enterWaitCountdown(self, ts): self._DistributedPicnicBasket__enableCollisions() self.accept('trolleyExitButton', self.handleExitButton) self.clockNode.countdown(self.picnicCountdownTime, self.handleExitButton) def handleExitButton(self): self.sendUpdate('requestExit') self.clockNode.hide() def exitWaitCountdown(self): self._DistributedPicnicBasket__disableCollisions() self.ignore('trolleyExitButton') self.clockNode.reset() def getStareAtNodeAndOffset(self): return (self.tablecloth, Point3(0, 0, 4)) def storeToonTrack(self, avId, track): self.clearToonTrack(avId) self._DistributedPicnicBasket__toonTracks[avId] = track def clearToonTrack(self, avId): oldTrack = self._DistributedPicnicBasket__toonTracks.get(avId) if oldTrack: oldTrack.pause() DelayDelete.cleanupDelayDeletes(oldTrack) del self._DistributedPicnicBasket__toonTracks[avId] def clearToonTracks(self): keyList = [] for key in self._DistributedPicnicBasket__toonTracks: keyList.append(key) for key in keyList: if self._DistributedPicnicBasket__toonTracks.has_key(key): self.clearToonTrack(key) continue def doneExit(self, avId): if avId == base.localAvatar.getDoId(): self.sendUpdate('doneExit') def setPosHpr(self, x, y, z, h, p, r): self.startingPos = Vec3(x, y, z) self.enteringPos = Vec3(x, y, z - 10) self.startingHpr = Vec3(h, 0, 0) def setTableNumber(self, tn): self.tableNumber = tn def generateToonJumpTrack(self, av, seatIndex): av.pose('sit', 47) hipOffset = av.getHipsParts()[2].getPos(av) def getToonJumpTrack(av, seatIndex): def getJumpDest(av = av, node = self.tablecloth): dest = Vec3(self.tablecloth.getPos(av.getParent())) seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1)) dest += seatNode.getPos(self.tablecloth) dna = av.getStyle() dest -= hipOffset if seatIndex == 2 or seatIndex == 3: dest.setY(dest.getY() + 2 * hipOffset.getY()) dest.setZ(dest.getZ() + 0.20000000000000001) return dest def getJumpHpr(av = av, node = self.tablecloth): hpr = self.seats[seatIndex].getHpr(av.getParent()) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.42999999999999999), Parallel(LerpHprInterval(av, hpr = getJumpHpr, duration = 0.90000000000000002), ProjectileInterval(av, endPos = getJumpDest, duration = 0.90000000000000002)))) return toonJumpTrack def getToonSitTrack(av): toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) return toonSitTrack toonJumpTrack = getToonJumpTrack(av, seatIndex) toonSitTrack = getToonSitTrack(av) jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tablecloth)) return jumpTrack def generateToonReverseJumpTrack(self, av, seatIndex): self.notify.debug('av.getH() = %s' % av.getH()) def getToonJumpTrack(av, destNode): def getJumpDest(av = av, node = destNode): dest = node.getPos(self.tablecloth) dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth) return dest def getJumpHpr(av = av, node = destNode): hpr = node.getHpr(av.getParent()) hpr.setX(hpr.getX() + 180) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.10000000000000001), Parallel(ProjectileInterval(av, endPos = getJumpDest, duration = 0.90000000000000002)))) return toonJumpTrack toonJumpTrack = getToonJumpTrack(av, self.tablecloth) jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) return jumpTrack def generateBasketAppearTrack(self): if self.basket == None: self.basket = loader.loadModel('phase_6/models/golf/picnic_basket.bam') self.basket.setScale(0.10000000000000001) basketTrack = Sequence(Func(self.basket.show), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.basket), Func(self.basket.reparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0.20000000000000001), Func(self.basket.setHpr, 45, 0, 0), Func(self.basket.wrtReparentTo, render), Func(self.basket.setShear, 0, 0, 0), Sequence(LerpScaleInterval(self.basket, scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.20000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.6000000000000001, 1.6000000000000001, 0.20000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.0, 1.0, 0.40000000000000002), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.5, 1.5, 2.5), duration = 0.20000000000000001), LerpScaleInterval(self.basket, scale = Point3(2.5, 2.5, 1.5), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(2.0, 2.0, 2.0), duration = 0.10000000000000001), Func(self.basket.wrtReparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0))) return basketTrack def generateBasketDisappearTrack(self): if not self.basket: return Sequence() pos = self.basket.getPos() pos.addZ(-1) basketTrack = Sequence(LerpScaleInterval(self.basket, scale = Point3(2.0, 2.0, 1.8), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.0, 1.0, 2.5), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(2.0, 2.0, 0.5), duration = 0.20000000000000001), LerpScaleInterval(self.basket, scale = Point3(0.5, 0.5, 1.0), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(0.10000000000000001, 0.10000000000000001, 0.10000000000000001), duration = 0.20000000000000001), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.basket), Wait(0.20000000000000001), LerpPosInterval(self.basket, pos = pos, duration = 0.20000000000000001), Func(self.basket.hide)) return basketTrack def generateFoodAppearTrack(self, seat): if self.fullSeat[seat] == self.seatState.Full: self.notify.debug('### food appear: self.fullSeat = %s' % self.fullSeat) if not self.food[seat]: self.food[seat] = loader.loadModel(self.random.choice(self.foodLoader)) self.notify.debug('### food appear: self.food = %s' % self.food) self.food[seat].setScale(0.10000000000000001) self.food[seat].reparentTo(self.tablecloth) self.food[seat].setPos(self.seats[seat].getPos(self.tablecloth)[0] / 2, self.seats[seat].getPos(self.tablecloth)[1] / 2, 0) foodTrack = Sequence(Func(self.food[seat].show), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.food[seat]), Func(self.food[seat].reparentTo, self.tablecloth), Func(self.food[seat].setHpr, 45, 0, 0), Func(self.food[seat].wrtReparentTo, render), Func(self.food[seat].setShear, 0, 0, 0), Sequence(LerpScaleInterval(self.food[seat], scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.20000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.6000000000000001, 1.6000000000000001, 0.20000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.0, 1.0, 0.40000000000000002), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.5, 1.5, 2.5), duration = 0.20000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(2.5, 2.5, 1.5), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(2.0, 2.0, 2.0), duration = 0.10000000000000001), Func(self.food[seat].wrtReparentTo, self.tablecloth))) return foodTrack else: return Sequence() def generateFoodDisappearTrack(self, seat): if not self.food[seat]: return Sequence() pos = self.food[seat].getPos() pos.addZ(-1.0) foodTrack = Sequence(LerpScaleInterval(self.food[seat], scale = Point3(2.0, 2.0, 1.8), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.0, 1.0, 2.5), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(2.0, 2.0, 0.5), duration = 0.20000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(0.5, 0.5, 1.0), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(0.10000000000000001, 0.10000000000000001, 0.10000000000000001), duration = 0.20000000000000001), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.food[seat]), Wait(0.20000000000000001), LerpPosInterval(self.food[seat], pos = pos, duration = 0.20000000000000001), Func(self.food[seat].hide)) return foodTrack def destroy(self, node): node.removeNode() node = None self.basket.removeNode() self.basket = None for food in self.food: food.removeNode() self.food = None self.clockNode.removeNode() del self.clockNode self.clockNode = None def setPicnicDone(self): if self.localToonOnBoard: if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.fsm.request('final') self.loader.place.trolley.fsm.request('start') self.localToonOnBoard = 0 messenger.send('picnicDone')
class DistributedPicnicBasket(DistributedObject.DistributedObject): seatState = Enum('Empty, Full, Eating') notify = DirectNotifyGlobal.directNotify.newCategory( 'DistributedPicnicBasket') def __init__(self, cr): DistributedObject.DistributedObject.__init__(self, cr) self.localToonOnBoard = 0 self.seed = 0 self.random = None self.picnicCountdownTime = base.config.GetFloat( 'picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) self.picnicBasketTrack = None self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [ State.State('off', self.enterOff, self.exitOff, ['waitEmpty', 'waitCountdown']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty']) ], 'off', 'off') self.fsm.enterInitialState() self.__toonTracks = {} return def generate(self): DistributedObject.DistributedObject.generate(self) self.loader = self.cr.playGame.hood.loader self.foodLoader = [ 'phase_6/models/golf/picnic_sandwich.bam', 'phase_6/models/golf/picnic_apple.bam', 'phase_6/models/golf/picnic_cupcake.bam', 'phase_6/models/golf/picnic_chocolate_cake.bam' ] self.fullSeat = [] self.food = [] for i in range(4): self.food.append(None) self.fullSeat.append(self.seatState.Empty) self.picnicItem = 0 return def announceGenerate(self): self.picnicTable = self.loader.geom.find('**/*picnic_table_' + str(self.tableNumber)) self.picnicTableSphereNodes = [] self.numSeats = 4 self.seats = [] self.jumpOffsets = [] self.basket = None for i in range(self.numSeats): self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1))) self.jumpOffsets.append( self.picnicTable.find('**/*jumpOut%d' % (i + 1))) self.tablecloth = self.picnicTable.find('**/basket_locator') DistributedObject.DistributedObject.announceGenerate(self) for i in range(self.numSeats): self.picnicTableSphereNodes.append(self.seats[i].attachNewNode( CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) self.picnicTableSphereNodes[i].node().addSolid( CollisionSphere(0, 0, 0, 2)) self.tableclothSphereNode = self.tablecloth.attachNewNode( CollisionNode('tablecloth_sphere')) self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4)) angle = self.startingHpr[0] angle -= 90 radAngle = deg2Rad(angle) unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) unitVec *= 30.0 self.endPos = self.startingPos + unitVec dist = Vec3(self.endPos - self.enteringPos).length() wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360 self.seatNumber = 0 self.clockNode = ToontownTimer() self.clockNode.setPos(1.16, 0, -0.83) self.clockNode.setScale(0.3) self.clockNode.hide() return def disable(self): DistributedObject.DistributedObject.disable(self) self.fsm.request('off') self.clearToonTracks() for i in range(self.numSeats): del self.picnicTableSphereNodes[0] del self.picnicTableSphereNodes self.notify.debug('Deleted self loader ' + str(self.getDoId())) self.picnicTable.removeNode() self.picnicBasketTrack = None return def delete(self): self.notify.debug('Golf kart getting deleted: %s' % self.getDoId()) DistributedObject.DistributedObject.delete(self) del self.fsm def setState(self, state, seed, timestamp): self.seed = seed if not self.random: self.random = RandomNumGen.RandomNumGen(seed) self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) def handleEnterPicnicTableSphere(self, i, collEntry): self.seatNumber = i self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId()) self.loader.place.detectedPicnicTableSphereCollision(self) def handleEnterPicnicTable(self, i): toon = base.localAvatar self.sendUpdate('requestBoard', [i]) def fillSlot0(self, avId): self.fillSlot(0, avId) def fillSlot1(self, avId): self.fillSlot(1, avId) def fillSlot2(self, avId): self.fillSlot(2, avId) def fillSlot3(self, avId): self.fillSlot(3, avId) def fillSlot(self, index, avId): self.notify.debug('fill Slot: %d for %d' % (index, avId)) if avId == 0: pass else: self.fullSeat[index] = self.seatState.Full if avId == base.localAvatar.getDoId(): self.clockNode.show() if index == 0 or index == 3: side = -1 else: side = 1 if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.fsm.request( 'boarding', [self.tablecloth, side]) else: self.notify.warning('fillSlot no trolley in place') self.localToonOnBoard = 1 if avId == base.localAvatar.getDoId(): if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.fsm.request('boarded') self.loader.place.trolley.exitButton.hide() if avId in self.cr.doId2do: toon = self.cr.doId2do[avId] toon.stopSmooth() toon.wrtReparentTo(self.tablecloth) sitStartDuration = toon.getDuration('sit-start') jumpTrack = self.generateToonJumpTrack(toon, index) track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0)) self.notify.debug('### fillSlot: fullSeat = %s' % self.fullSeat) if self.fullSeat.count(0) == 3: self.notify.debug('### fillSlot: adding basketAppear') if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence( Wait(waitDuration), self.generateBasketAppearTrack()) self.picnicBasketTrack.start() track.append(self.generateFoodAppearTrack(index)) track.append( Sequence(Func(self.clearToonTrack, avId), name=toon.uniqueName('fillTrolley'), autoPause=1)) if avId == base.localAvatar.getDoId(): if hasattr(self.loader.place, 'trolley'): track.append( Func(self.loader.place.trolley.exitButton.show)) track.delayDelete = DelayDelete.DelayDelete( toon, 'PicnicBasket.fillSlot') self.storeToonTrack(avId, track) track.start() def emptySlot0(self, avId, timestamp): self.emptySlot(0, avId, timestamp) def emptySlot1(self, avId, timestamp): self.emptySlot(1, avId, timestamp) def emptySlot2(self, avId, timestamp): self.emptySlot(2, avId, timestamp) def emptySlot3(self, avId, timestamp): self.emptySlot(3, avId, timestamp) def notifyToonOffTrolley(self, toon): toon.setAnimState('neutral', 1.0) if hasattr(base, 'localAvatar') and toon == base.localAvatar: if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.handleOffTrolley() self.localToonOnBoard = 0 else: toon.startSmooth() def emptySlot(self, index, avId, timestamp): def emptySeat(index): self.notify.debug('### seat %s now empty' % index) self.fullSeat[index] = self.seatState.Empty if avId == 0: pass elif avId == 1: self.fullSeat[index] = self.seatState.Empty track = Sequence(self.generateFoodDisappearTrack(index)) self.notify.debug('### empty slot - unexpetected: fullSeat = %s' % self.fullSeat) if self.fullSeat.count(0) == 4: self.notify.debug('### empty slot - unexpected: losing basket') if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence( Wait(waitDuration), self.generateBasketDisappearTrack()) self.picnicBasketTrack.start() track.start() else: self.fullSeat[index] = self.seatState.Empty if avId in self.cr.doId2do: if avId == base.localAvatar.getDoId(): if self.clockNode: self.clockNode.hide() toon = self.cr.doId2do[avId] toon.stopSmooth() sitStartDuration = toon.getDuration('sit-start') jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) track = Sequence(jumpOutTrack) track.append(self.generateFoodDisappearTrack(index)) self.notify.debug('### empty slot: fullSeat = %s' % self.fullSeat) if self.fullSeat.count(0) == 4: self.notify.debug('### empty slot: losing basket') if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence( Wait(waitDuration), self.generateBasketDisappearTrack()) self.picnicBasketTrack.start() track.append( Sequence(Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), Func(self.doneExit, avId), Func(emptySeat, index), name=toon.uniqueName('emptyTrolley'), autoPause=1)) track.delayDelete = DelayDelete.DelayDelete( toon, 'PicnicBasket.emptySlot') self.storeToonTrack(avId, track) track.start() def rejectBoard(self, avId): self.loader.place.trolley.handleRejectBoard() def __enableCollisions(self): for i in range(self.numSeats): self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i]) self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [i]) self.picnicTableSphereNodes[i].setCollideMask( ToontownGlobals.WallBitmask) def __disableCollisions(self): for i in range(self.numSeats): self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) for i in range(self.numSeats): self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) def enterOff(self): return None def exitOff(self): return None def enterWaitEmpty(self, ts): self.__enableCollisions() def exitWaitEmpty(self): self.__disableCollisions() def enterWaitCountdown(self, ts): self.__enableCollisions() self.accept('trolleyExitButton', self.handleExitButton) self.clockNode.countdown(self.picnicCountdownTime, self.handleExitButton) def handleExitButton(self): self.sendUpdate('requestExit') self.clockNode.hide() def exitWaitCountdown(self): self.__disableCollisions() self.ignore('trolleyExitButton') self.clockNode.reset() def getStareAtNodeAndOffset(self): return (self.tablecloth, Point3(0, 0, 4)) def storeToonTrack(self, avId, track): self.clearToonTrack(avId) self.__toonTracks[avId] = track def clearToonTrack(self, avId): oldTrack = self.__toonTracks.get(avId) if oldTrack: oldTrack.pause() DelayDelete.cleanupDelayDeletes(oldTrack) del self.__toonTracks[avId] def clearToonTracks(self): keyList = [] for key in self.__toonTracks: keyList.append(key) for key in keyList: if key in self.__toonTracks: self.clearToonTrack(key) def doneExit(self, avId): if avId == base.localAvatar.getDoId(): self.sendUpdate('doneExit') def setPosHpr(self, x, y, z, h, p, r): self.startingPos = Vec3(x, y, z) self.enteringPos = Vec3(x, y, z - 10) self.startingHpr = Vec3(h, 0, 0) def setTableNumber(self, tn): self.tableNumber = tn def generateToonJumpTrack(self, av, seatIndex): av.pose('sit', 47) hipOffset = av.getHipsParts()[2].getPos(av) def getToonJumpTrack(av, seatIndex): def getJumpDest(av=av, node=self.tablecloth): dest = Vec3(self.tablecloth.getPos(av.getParent())) seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1)) dest += seatNode.getPos(self.tablecloth) dna = av.getStyle() dest -= hipOffset if seatIndex == 2 or seatIndex == 3: dest.setY(dest.getY() + 2 * hipOffset.getY()) dest.setZ(dest.getZ() + 0.2) return dest def getJumpHpr(av=av, node=self.tablecloth): hpr = self.seats[seatIndex].getHpr(av.getParent()) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel( ActorInterval(av, 'jump'), Sequence( Wait(0.43), Parallel( LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) return toonJumpTrack def getToonSitTrack(av): toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) return toonSitTrack toonJumpTrack = getToonJumpTrack(av, seatIndex) toonSitTrack = getToonSitTrack(av) jumpTrack = Sequence( Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tablecloth)) return jumpTrack def generateToonReverseJumpTrack(self, av, seatIndex): self.notify.debug('av.getH() = %s' % av.getH()) def getToonJumpTrack(av, destNode): def getJumpDest(av=av, node=destNode): dest = node.getPos(self.tablecloth) dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth) return dest def getJumpHpr(av=av, node=destNode): hpr = node.getHpr(av.getParent()) hpr.setX(hpr.getX() + 180) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel( ActorInterval(av, 'jump'), Sequence( Wait(0.1), Parallel( ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) return toonJumpTrack toonJumpTrack = getToonJumpTrack(av, self.tablecloth) jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) return jumpTrack def generateBasketAppearTrack(self): if self.basket == None: self.basket = loader.loadModel( 'phase_6/models/golf/picnic_basket.bam') self.basket.setScale(0.1) basketTrack = Sequence( Func(self.basket.show), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), node=self.basket), Func(self.basket.reparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0.2), Func(self.basket.setHpr, 45, 0, 0), Func(self.basket.wrtReparentTo, render), Func(self.basket.setShear, 0, 0, 0), Sequence( LerpScaleInterval(self.basket, scale=Point3(1.1, 1.1, 0.1), duration=0.2), LerpScaleInterval(self.basket, scale=Point3(1.6, 1.6, 0.2), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1.0, 1.0, 0.4), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1.5, 1.5, 2.5), duration=0.2), LerpScaleInterval(self.basket, scale=Point3(2.5, 2.5, 1.5), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(2.0, 2.0, 2.0), duration=0.1), Func(self.basket.wrtReparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0))) return basketTrack def generateBasketDisappearTrack(self): if not self.basket: return Sequence() pos = self.basket.getPos() pos.addZ(-1) basketTrack = Sequence( LerpScaleInterval(self.basket, scale=Point3(2.0, 2.0, 1.8), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1.0, 1.0, 2.5), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(2.0, 2.0, 0.5), duration=0.2), LerpScaleInterval(self.basket, scale=Point3(0.5, 0.5, 1.0), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1.1, 1.1, 0.1), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(0.1, 0.1, 0.1), duration=0.2), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), node=self.basket), Wait(0.2), LerpPosInterval(self.basket, pos=pos, duration=0.2), Func(self.basket.hide)) return basketTrack def generateFoodAppearTrack(self, seat): if self.fullSeat[seat] == self.seatState.Full: self.notify.debug('### food appear: self.fullSeat = %s' % self.fullSeat) if not self.food[seat]: self.food[seat] = loader.loadModel( self.random.choice(self.foodLoader)) self.notify.debug('### food appear: self.food = %s' % self.food) self.food[seat].setScale(0.1) self.food[seat].reparentTo(self.tablecloth) self.food[seat].setPos( self.seats[seat].getPos(self.tablecloth)[0] / 2, self.seats[seat].getPos(self.tablecloth)[1] / 2, 0) foodTrack = Sequence( Func(self.food[seat].show), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), node=self.food[seat]), Func(self.food[seat].reparentTo, self.tablecloth), Func(self.food[seat].setHpr, 45, 0, 0), Func(self.food[seat].wrtReparentTo, render), Func(self.food[seat].setShear, 0, 0, 0), Sequence( LerpScaleInterval(self.food[seat], scale=Point3(1.1, 1.1, 0.1), duration=0.2), LerpScaleInterval(self.food[seat], scale=Point3(1.6, 1.6, 0.2), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1.0, 1.0, 0.4), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1.5, 1.5, 2.5), duration=0.2), LerpScaleInterval(self.food[seat], scale=Point3(2.5, 2.5, 1.5), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(2.0, 2.0, 2.0), duration=0.1), Func(self.food[seat].wrtReparentTo, self.tablecloth))) return foodTrack else: return Sequence() def generateFoodDisappearTrack(self, seat): if not self.food[seat]: return Sequence() pos = self.food[seat].getPos() pos.addZ(-1.0) foodTrack = Sequence( LerpScaleInterval(self.food[seat], scale=Point3(2.0, 2.0, 1.8), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1.0, 1.0, 2.5), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(2.0, 2.0, 0.5), duration=0.2), LerpScaleInterval(self.food[seat], scale=Point3(0.5, 0.5, 1.0), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1.1, 1.1, 0.1), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(0.1, 0.1, 0.1), duration=0.2), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), node=self.food[seat]), Wait(0.2), LerpPosInterval(self.food[seat], pos=pos, duration=0.2), Func(self.food[seat].hide)) return foodTrack def destroy(self, node): node.removeNode() node = None self.basket.removeNode() self.basket = None for food in self.food: food.removeNode() self.food = None self.clockNode.removeNode() del self.clockNode self.clockNode = None return def setPicnicDone(self): if self.localToonOnBoard: if hasattr(self.loader.place, 'trolley'): self.loader.place.trolley.fsm.request('final') self.loader.place.trolley.fsm.request('start') self.localToonOnBoard = 0 messenger.send('picnicDone')
class DistributedPicnicBasket(DistributedObject.DistributedObject): seatState = Enum("Empty, Full, Eating") notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedPicnicBasket") def __init__(self, cr): """__init__(cr) """ DistributedObject.DistributedObject.__init__(self, cr) self.localToonOnBoard = 0 self.seed = 0 self.random = None self.picnicCountdownTime = \ ConfigVariableDouble("picnic-countdown-time", ToontownGlobals.PICNIC_COUNTDOWN_TIME).getValue() self.picnicBasketTrack = None # only one track contains the picnic basket shrink/grow self.fsm = ClassicFSM.ClassicFSM( 'DistributedTrolley', [ State.State('off', self.enterOff, self.exitOff, ['waitEmpty', 'waitCountdown']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty']) ], # Initial State 'off', # Final State 'off', ) self.fsm.enterInitialState() # Tracks on toons, for starting and stopping # stored by avId : track. There is only a need for one at a time, # in fact the point of the dict is to ensure only one is playing at a time self.__toonTracks = {} def generate(self): """generate(self) This method is called when the DistributedObject is reintroduced to the world, either for the first time or from the cache. """ DistributedObject.DistributedObject.generate(self) # Get the state machine stuff for playGame self.loader = self.cr.playGame.hood.loader self.foodLoader = [ 'phase_6/models/golf/picnic_sandwich.bam', 'phase_6/models/golf/picnic_apple.bam', 'phase_6/models/golf/picnic_cupcake.bam', 'phase_6/models/golf/picnic_chocolate_cake.bam' ] self.fullSeat = [] self.food = [] for i in range(4): self.food.append(None) self.fullSeat.append(self.seatState.Empty) self.picnicItem = 0 def announceGenerate(self): """Setup other fields dependent on the required fields.""" self.picnicTable = self.loader.geom.find("**/*picnic_table_" + str(self.tableNumber)) self.picnicTableSphereNodes = [] self.numSeats = 4 self.seats = [] self.jumpOffsets = [] self.basket = None for i in range(self.numSeats): self.seats.append(self.picnicTable.find("**/*seat%d" % (i + 1))) self.jumpOffsets.append( self.picnicTable.find("**/*jumpOut%d" % (i + 1))) #debugAxis = loader.loadModel('models/misc/xyzAxis') #debugAxis.setColorScale(1.0, 1.0, 1.0, 0.25) #debugAxis.setTransparency(True) #debugAxis.reparentTo(self.seats[i]) self.tablecloth = self.picnicTable.find("**/basket_locator") DistributedObject.DistributedObject.announceGenerate(self) for i in range(self.numSeats): self.picnicTableSphereNodes.append(self.seats[i].attachNewNode( CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) self.picnicTableSphereNodes[i].node().addSolid( CollisionSphere(0, 0, 0, 2)) self.tableclothSphereNode = self.tablecloth.attachNewNode( CollisionNode('tablecloth_sphere')) self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4)) angle = self.startingHpr[0] angle -= 90 radAngle = deg2Rad(angle) unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) unitVec *= 30.0 self.endPos = self.startingPos + unitVec dist = Vec3(self.endPos - self.enteringPos).length() wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360 self.seatNumber = 0 self.clockNode = ToontownTimer() self.clockNode.reparentTo(base.a2dBottomRight) self.clockNode.setPos(-0.173, 0, 0.17) self.clockNode.setScale(0.3) self.clockNode.hide() def disable(self): DistributedObject.DistributedObject.disable(self) # Go to the off state when the object is put in the cache self.fsm.request("off") # No more toon animating self.clearToonTracks() for i in range(self.numSeats): del self.picnicTableSphereNodes[0] del self.picnicTableSphereNodes self.notify.debug("Deleted self loader " + str(self.getDoId())) self.picnicTable.removeNode() self.picnicBasketTrack = None #self.kart.removeNode() #del self.kart #import pdb; #pdb.set_trace() def delete(self): self.notify.debug("Golf kart getting deleted: %s" % self.getDoId()) DistributedObject.DistributedObject.delete(self) del self.fsm def setState(self, state, seed, timestamp): self.seed = seed if not self.random: self.random = RandomNumGen.RandomNumGen(seed) self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) def handleEnterPicnicTableSphere(self, i, collEntry): # collEntry): assert self.notify.debugStateCall(self) self.seatNumber = i self.notify.debug("Entering Picnic Table Sphere.... %s" % self.getDoId()) # Put localToon into requestBoard mode. #import pdb; pdb.set_trace() self.loader.place.detectedPicnicTableSphereCollision(self) def handleEnterPicnicTable(self, i): # Tell the server that this avatar wants to board. assert self.notify.debugStateCall(self) toon = base.localAvatar self.sendUpdate("requestBoard", [i]) def fillSlot0(self, avId): self.fillSlot(0, avId) def fillSlot1(self, avId): self.fillSlot(1, avId) def fillSlot2(self, avId): self.fillSlot(2, avId) def fillSlot3(self, avId): self.fillSlot(3, avId) def fillSlot(self, index, avId): assert self.notify.debugStateCall(self) self.notify.debug("fill Slot: %d for %d" % (index, avId)) if avId == 0: # This means that the slot is now empty, and no action should # be taken. pass else: self.fullSeat[index] = self.seatState.Full # If localToon is boarding, he needs to change state if avId == base.localAvatar.getDoId(): # Start the countdown clock... self.clockNode.show() if index == 0 or index == 3: side = -1 else: side = 1 if hasattr(self.loader.place, "trolley"): self.loader.place.trolley.fsm.request( "boarding", [self.tablecloth, side]) else: self.notify.warning('fillSlot no trolley in place') self.localToonOnBoard = 1 # Put that toon on the table # If it's localToon, tell him he's on the trolley now if avId == base.localAvatar.getDoId(): if hasattr(self.loader.place, "trolley"): self.loader.place.trolley.fsm.request("boarded") # hide the exit button until basket interval is over self.loader.place.trolley.exitButton.hide() if avId in self.cr.doId2do: # If the toon exists, look it up toon = self.cr.doId2do[avId] # Parent it to the trolley toon.stopSmooth() toon.wrtReparentTo(self.tablecloth) sitStartDuration = toon.getDuration("sit-start") jumpTrack = self.generateToonJumpTrack(toon, index) track = Sequence(jumpTrack, Func(toon.setAnimState, "Sit", 1.0)) # only add basket appear if there is no toons are already sitting self.notify.debug("### fillSlot: fullSeat = %s" % self.fullSeat) if self.fullSeat.count(0) == 3: self.notify.debug("### fillSlot: adding basketAppear") #track.append(self.generateBasketAppearTrack()) if self.picnicBasketTrack: self.picnicBasketTrack.finish() waitDuration = track.getDuration() self.picnicBasketTrack = Sequence( Wait(waitDuration), self.generateBasketAppearTrack()) self.picnicBasketTrack.start() # make a random food appear track.append(self.generateFoodAppearTrack(index)) # finish the rest of the staging track.append( Sequence(Func(self.clearToonTrack, avId), name=toon.uniqueName("fillTrolley"), autoPause=1)) if avId == base.localAvatar.getDoId(): if hasattr(self.loader.place, "trolley"): track.append( Func(self.loader.place.trolley.exitButton.show)) track.delayDelete = DelayDelete.DelayDelete( toon, 'PicnicBasket.fillSlot') self.storeToonTrack(avId, track) track.start() def emptySlot0(self, avId, timestamp): self.emptySlot(0, avId, timestamp) def emptySlot1(self, avId, timestamp): self.emptySlot(1, avId, timestamp) def emptySlot2(self, avId, timestamp): self.emptySlot(2, avId, timestamp) def emptySlot3(self, avId, timestamp): self.emptySlot(3, avId, timestamp) def notifyToonOffTrolley(self, toon): toon.setAnimState("neutral", 1.0) if hasattr(base, 'localAvatar') and toon == base.localAvatar: if hasattr(self.loader.place, "trolley"): self.loader.place.trolley.handleOffTrolley() self.localToonOnBoard = 0 else: toon.startSmooth() return def emptySlot(self, index, avId, timestamp): def emptySeat(index): # If localToon is exiting, he needs to change state self.notify.debug("### seat %s now empty" % index) self.fullSeat[index] = self.seatState.Empty if avId == 0: # This means that the slot is now empty, and no action should # be taken. pass elif avId == 1: # Special cardinal value for unexpected exit. # The toon is gone, but we may still need to clean up his food self.fullSeat[index] = self.seatState.Empty track = Sequence(self.generateFoodDisappearTrack(index)) # if no toons left, make the basket go away self.notify.debug("### empty slot - unexpetected: fullSeat = %s" % self.fullSeat) if self.fullSeat.count(0) == 4: self.notify.debug("### empty slot - unexpected: losing basket") if self.picnicBasketTrack: self.picnicBasketTrack.finish() #track.append(self.generateBasketDisappearTrack()) waitDuration = track.getDuration() self.picnicBasketTrack = Sequence( Wait(waitDuration), self.generateBasketDisappearTrack()) self.picnicBasketTrack.start() track.start() else: self.fullSeat[index] = self.seatState.Empty if avId in self.cr.doId2do: if avId == base.localAvatar.getDoId(): # Stop the countdown clock.. if (self.clockNode): self.clockNode.hide() # If the toon exists, look it up toon = self.cr.doId2do[avId] toon.stopSmooth() sitStartDuration = toon.getDuration("sit-start") jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) track = Sequence(jumpOutTrack) # make the food go away track.append(self.generateFoodDisappearTrack(index)) # if no toons left, make the basket go away self.notify.debug("### empty slot: fullSeat = %s" % self.fullSeat) if self.fullSeat.count(0) == 4: self.notify.debug("### empty slot: losing basket") if self.picnicBasketTrack: self.picnicBasketTrack.finish() #track.append(self.generateBasketDisappearTrack()) waitDuration = track.getDuration() self.picnicBasketTrack = Sequence( Wait(waitDuration), self.generateBasketDisappearTrack()) self.picnicBasketTrack.start() # let the toon loose track.append( Sequence( # Tell the toon he is free to roam now Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), Func(self.doneExit, avId), Func(emptySeat, index), name=toon.uniqueName("emptyTrolley"), autoPause=1)) track.delayDelete = DelayDelete.DelayDelete( toon, 'PicnicBasket.emptySlot') self.storeToonTrack(avId, track) track.start() def rejectBoard(self, avId): # This should only be sent to us if our localToon requested # permission to board the trolley. assert (base.localAvatar.getDoId() == avId) self.loader.place.trolley.handleRejectBoard() def __enableCollisions(self): # start listening for toons to enter. assert self.notify.debugStateCall(self) for i in range(self.numSeats): self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i]) self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [i]) self.picnicTableSphereNodes[i].setCollideMask( ToontownGlobals.WallBitmask) def __disableCollisions(self): assert self.notify.debugStateCall(self) for i in range(self.numSeats): self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) for i in range(self.numSeats): self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) ##### Off state ##### def enterOff(self): return None def exitOff(self): return None ##### WaitEmpty state ##### def enterWaitEmpty(self, ts): # Toons may now try to board the trolley self.__enableCollisions() def exitWaitEmpty(self): # Toons may not attempt to board the trolley if it isn't waiting self.__disableCollisions() ##### WaitCountdown state ##### def enterWaitCountdown(self, ts): # Toons may now try to board the trolley self.__enableCollisions() self.accept("trolleyExitButton", self.handleExitButton) #self.clockNode.countdown(self.picnicCountdownTime - ts, self.handleExitButton) self.clockNode.countdown(self.picnicCountdownTime, self.handleExitButton) def handleExitButton(self): # This gets called when the exit button gets pushed. self.sendUpdate("requestExit") self.clockNode.hide() #import pdb; pdb.set_trace() def exitWaitCountdown(self): # Toons may not attempt to board the trolley if it isn't waiting self.__disableCollisions() self.ignore("trolleyExitButton") self.clockNode.reset() def getStareAtNodeAndOffset(self): return self.tablecloth, Point3(0, 0, 4) def storeToonTrack(self, avId, track): # Clear out any currently playing tracks on this toon self.clearToonTrack(avId) # Store this new one self.__toonTracks[avId] = track def clearToonTrack(self, avId): # Clear out any currently playing tracks on this toon oldTrack = self.__toonTracks.get(avId) if oldTrack: oldTrack.pause() DelayDelete.cleanupDelayDeletes(oldTrack) del self.__toonTracks[avId] def clearToonTracks(self): #We can't use an iter because we are deleting keys keyList = [] for key in self.__toonTracks: keyList.append(key) for key in keyList: if key in self.__toonTracks: self.clearToonTrack(key) def doneExit(self, avId): if (avId == base.localAvatar.getDoId()): self.sendUpdate("doneExit") def setPosHpr(self, x, y, z, h, p, r): """Set the pos hpr as dictated by the AI.""" self.startingPos = Vec3(x, y, z) self.enteringPos = Vec3(x, y, z - 10) self.startingHpr = Vec3(h, 0, 0) #self.golfKart.setPosHpr( x, y, z, h, 0, 0 ) def setTableNumber(self, tn): self.tableNumber = tn def generateToonJumpTrack(self, av, seatIndex): """Return an interval of the toon jumping into the golf kart.""" # Maintain a reference to Parent and Scale of avatar in case they # exit from the kart. #base.sb = self av.pose('sit', 47) hipOffset = av.getHipsParts()[2].getPos(av) def getToonJumpTrack(av, seatIndex): # using a local func allows the ProjectileInterval to # calculate this pos at run-time def getJumpDest(av=av, node=self.tablecloth): dest = Vec3(self.tablecloth.getPos(av.getParent())) seatNode = self.picnicTable.find("**/seat" + str(seatIndex + 1)) dest += seatNode.getPos(self.tablecloth) dna = av.getStyle() dest -= hipOffset if (seatIndex == 2 or seatIndex == 3): dest.setY(dest.getY() + 2 * hipOffset.getY()) dest.setZ(dest.getZ() + 0.2) return dest def getJumpHpr(av=av, node=self.tablecloth): hpr = self.seats[seatIndex].getHpr(av.getParent()) #if(seatIndex < 2): #hpr.setX( hpr.getX() + 180) #else: # hpr.setX( hpr.getX() ) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel( ActorInterval(av, 'jump'), Sequence( Wait(0.43), Parallel( LerpHprInterval(av, hpr=getJumpHpr, duration=.9), ProjectileInterval(av, endPos=getJumpDest, duration=.9)), )) return toonJumpTrack def getToonSitTrack(av): toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) return toonSitTrack toonJumpTrack = getToonJumpTrack(av, seatIndex) toonSitTrack = getToonSitTrack(av) jumpTrack = Sequence( Parallel( toonJumpTrack, Sequence( Wait(1), toonSitTrack, ), ), Func(av.wrtReparentTo, self.tablecloth), ) return jumpTrack def generateToonReverseJumpTrack(self, av, seatIndex): """Return an interval of the toon jumping out of the golf kart.""" self.notify.debug("av.getH() = %s" % av.getH()) def getToonJumpTrack(av, destNode): # using a local func allows the ProjectileInterval to # calculate this pos at run-time def getJumpDest(av=av, node=destNode): dest = node.getPos(self.tablecloth) dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth) return dest def getJumpHpr(av=av, node=destNode): hpr = node.getHpr(av.getParent()) hpr.setX(hpr.getX() + 180) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel( ActorInterval(av, 'jump'), Sequence( Wait(0.1), #43 ), Parallel( #LerpHprInterval( av, # hpr = getJumpHpr, # duration = .9 ), ProjectileInterval(av, endPos=getJumpDest, duration=.9)))) return toonJumpTrack toonJumpTrack = getToonJumpTrack(av, self.tablecloth) #self.seats[seatIndex]) jumpTrack = Sequence( toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render), #Func( self.av.setPosHpr, self.exitMovieNode, 0,0,0,0,0,0 ), ) return jumpTrack def generateBasketAppearTrack(self): """ """ if (self.basket == None): self.basket = loader.loadModel( 'phase_6/models/golf/picnic_basket.bam') self.basket.setScale(0.1) basketTrack = Sequence( Func(self.basket.show), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node=self.basket), Func(self.basket.reparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, .2), Func(self.basket.setHpr, 45, 0, 0), Func(self.basket.wrtReparentTo, render), Func(self.basket.setShear, 0, 0, 0), #Func( self.basket.setActiveShadow, True ), # Must be a cleaner way to do this. Sequence( LerpScaleInterval(self.basket, scale=Point3(1.1, 1.1, .1), duration=0.2), LerpScaleInterval(self.basket, scale=Point3(1.6, 1.6, 0.2), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1., 1., 0.4), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1.5, 1.5, 2.5), duration=0.2), LerpScaleInterval(self.basket, scale=Point3(2.5, 2.5, 1.5), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(2., 2., 2.), duration=0.1), Func(self.basket.wrtReparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0)), ) return basketTrack def generateBasketDisappearTrack(self): if not self.basket: return Sequence() pos = self.basket.getPos() pos.addZ(-1) basketTrack = Sequence( LerpScaleInterval(self.basket, scale=Point3(2., 2., 1.8), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1., 1., 2.5), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(2., 2., 0.5), duration=0.2), LerpScaleInterval(self.basket, scale=Point3(0.5, 0.5, 1.0), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(1.1, 1.1, .1), duration=0.1), LerpScaleInterval(self.basket, scale=Point3(.1, .1, .1), duration=0.2), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node=self.basket), Wait(0.2), LerpPosInterval(self.basket, pos=pos, duration=0.2), Func(self.basket.hide), ) return basketTrack def generateFoodAppearTrack(self, seat): """ """ if (self.fullSeat[seat] == self.seatState.Full): self.notify.debug("### food appear: self.fullSeat = %s" % self.fullSeat) if not self.food[seat]: self.food[seat] = loader.loadModel( self.random.choice(self.foodLoader)) self.notify.debug("### food appear: self.food = %s" % self.food) self.food[seat].setScale(0.1) self.food[seat].reparentTo(self.tablecloth) self.food[seat].setPos( self.seats[seat].getPos(self.tablecloth)[0] / 2, self.seats[seat].getPos(self.tablecloth)[1] / 2, 0) # Func( self.food[seat].setActiveShadow, False ), foodTrack = Sequence( Func(self.food[seat].show), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node=self.food[seat]), Func(self.food[seat].reparentTo, self.tablecloth), Func(self.food[seat].setHpr, 45, 0, 0), Func(self.food[seat].wrtReparentTo, render), Func(self.food[seat].setShear, 0, 0, 0), #Func( self.food[seat].setActiveShadow, True ), # Must be a cleaner way to do this. Sequence( LerpScaleInterval(self.food[seat], scale=Point3(1.1, 1.1, .1), duration=0.2), LerpScaleInterval(self.food[seat], scale=Point3(1.6, 1.6, 0.2), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1., 1., 0.4), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1.5, 1.5, 2.5), duration=0.2), LerpScaleInterval(self.food[seat], scale=Point3(2.5, 2.5, 1.5), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(2., 2., 2.), duration=0.1), Func(self.food[seat].wrtReparentTo, self.tablecloth)), ) return foodTrack else: return Sequence() def generateFoodDisappearTrack(self, seat): if not self.food[seat]: return Sequence() pos = self.food[seat].getPos() pos.addZ(-1.) foodTrack = Sequence( LerpScaleInterval(self.food[seat], scale=Point3(2., 2., 1.8), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1., 1., 2.5), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(2., 2., 0.5), duration=0.2), LerpScaleInterval(self.food[seat], scale=Point3(0.5, 0.5, 1.0), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(1.1, 1.1, .1), duration=0.1), LerpScaleInterval(self.food[seat], scale=Point3(.1, .1, .1), duration=0.2), SoundInterval( globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node=self.food[seat]), Wait(0.2), LerpPosInterval(self.food[seat], pos=pos, duration=0.2), Func(self.food[seat].hide), ) return foodTrack def destroy(self, node): node.removeNode() node = None self.basket.removeNode() self.basket = None for food in self.food: food.removeNode() self.food = None self.clockNode.removeNode() del self.clockNode self.clockNode = None def setPicnicDone(self): if self.localToonOnBoard: if hasattr(self.loader.place, "trolley"): self.loader.place.trolley.fsm.request("final") self.loader.place.trolley.fsm.request("start") self.localToonOnBoard = 0 messenger.send("picnicDone")