378 lines
No EOL
17 KiB
Python
378 lines
No EOL
17 KiB
Python
from toontown.toonbase import ToontownGlobals
|
|
import PhoneGlobals
|
|
from toontown.catalog import CatalogScreen
|
|
from toontown.catalog import CatalogItem
|
|
from toontown.catalog import GiftAvatar
|
|
from toontown.toontowngui import TTDialog
|
|
from toontown.toonbase import TTLocalizer
|
|
import DistributedHouseInterior
|
|
from direct.actor import Actor
|
|
import DistributedFurnitureItem
|
|
from direct.distributed import ClockDelta
|
|
from direct.showbase import PythonUtil
|
|
from direct.showutil import Rope
|
|
from direct.directnotify.DirectNotifyGlobal import *
|
|
from panda3d.core import *
|
|
from direct.interval.IntervalGlobal import *
|
|
from toontown.quest import Quests
|
|
from direct.task import Task
|
|
|
|
class DistributedPhone(DistributedFurnitureItem.DistributedFurnitureItem):
|
|
notify = directNotify.newCategory('DistributedPhone')
|
|
movieDelay = 0.5
|
|
|
|
def __init__(self, cr):
|
|
DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr)
|
|
self.lastAvId = 0
|
|
self.hasLocalAvatar = 0
|
|
self.lastTime = 0
|
|
self.initialScale = None
|
|
self.usedInitialScale = 0
|
|
self.toonScale = None
|
|
self.phoneGui = None
|
|
self.phoneDialog = None
|
|
self.model = None
|
|
self.cord = None
|
|
self.receiverGeom = None
|
|
self.receiverJoint = None
|
|
self.phoneSphereEvent = 'phoneSphere'
|
|
self.phoneSphereEnterEvent = 'enter' + self.phoneSphereEvent
|
|
self.phoneGuiDoneEvent = 'phoneGuiDone'
|
|
self.pickupMovieDoneEvent = 'phonePickupDone'
|
|
self.numHouseItems = None
|
|
self.interval = None
|
|
self.intervalAvatar = None
|
|
self.phoneInUse = 0
|
|
self.origToonHpr = None
|
|
|
|
def announceGenerate(self):
|
|
self.notify.debug('announceGenerate')
|
|
DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self)
|
|
self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere)
|
|
self.load()
|
|
taskMgr.doMethodLater(6, self.ringIfHasPhoneQuest, self.uniqueName('ringDoLater'))
|
|
|
|
def loadModel(self):
|
|
self.model = Actor.Actor('phase_5.5/models/estate/prop_phone-mod', {'SS_phoneOut': 'phase_5.5/models/estate/prop_phone-SS_phoneOut',
|
|
'SS_takePhone': 'phase_5.5/models/estate/prop_phone-SS_takePhone',
|
|
'SS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SS_phoneNeutral',
|
|
'SS_phoneBack': 'phase_5.5/models/estate/prop_phone-SS_phoneBack',
|
|
'SM_phoneOut': 'phase_5.5/models/estate/prop_phone-SM_phoneOut',
|
|
'SM_takePhone': 'phase_5.5/models/estate/prop_phone-SM_takePhone',
|
|
'SM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SM_phoneNeutral',
|
|
'SM_phoneBack': 'phase_5.5/models/estate/prop_phone-SM_phoneBack',
|
|
'SL_phoneOut': 'phase_5.5/models/estate/prop_phone-SL_phoneOut',
|
|
'SL_takePhone': 'phase_5.5/models/estate/prop_phone-SL_takePhone',
|
|
'SL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SL_phoneNeutral',
|
|
'SL_phoneBack': 'phase_5.5/models/estate/prop_phone-SL_phoneBack',
|
|
'MS_phoneOut': 'phase_5.5/models/estate/prop_phone-MS_phoneOut',
|
|
'MS_takePhone': 'phase_5.5/models/estate/prop_phone-MS_takePhone',
|
|
'MS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MS_phoneNeutral',
|
|
'MS_phoneBack': 'phase_5.5/models/estate/prop_phone-MS_phoneBack',
|
|
'MM_phoneOut': 'phase_5.5/models/estate/prop_phone-MM_phoneOut',
|
|
'MM_takePhone': 'phase_5.5/models/estate/prop_phone-MM_takePhone',
|
|
'MM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MM_phoneNeutral',
|
|
'MM_phoneBack': 'phase_5.5/models/estate/prop_phone-MM_phoneBack',
|
|
'ML_phoneOut': 'phase_5.5/models/estate/prop_phone-ML_phoneOut',
|
|
'ML_takePhone': 'phase_5.5/models/estate/prop_phone-ML_takePhone',
|
|
'ML_phoneNeutral': 'phase_5.5/models/estate/prop_phone-ML_phoneNeutral',
|
|
'ML_phoneBack': 'phase_5.5/models/estate/prop_phone-ML_phoneBack',
|
|
'LS_phoneOut': 'phase_5.5/models/estate/prop_phone-LS_phoneOut',
|
|
'LS_takePhone': 'phase_5.5/models/estate/prop_phone-LS_takePhone',
|
|
'LS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LS_phoneNeutral',
|
|
'LS_phoneBack': 'phase_5.5/models/estate/prop_phone-LS_phoneBack',
|
|
'LM_phoneOut': 'phase_5.5/models/estate/prop_phone-LM_phoneOut',
|
|
'LM_takePhone': 'phase_5.5/models/estate/prop_phone-LM_takePhone',
|
|
'LM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LM_phoneNeutral',
|
|
'LM_phoneBack': 'phase_5.5/models/estate/prop_phone-LM_phoneBack',
|
|
'LL_phoneOut': 'phase_5.5/models/estate/prop_phone-LL_phoneOut',
|
|
'LL_takePhone': 'phase_5.5/models/estate/prop_phone-LL_takePhone',
|
|
'LL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LL_phoneNeutral',
|
|
'LL_phoneBack': 'phase_5.5/models/estate/prop_phone-LL_phoneBack'})
|
|
self.model.pose('SS_phoneOut', 0)
|
|
self.receiverJoint = self.model.find('**/joint_receiver')
|
|
self.receiverGeom = self.receiverJoint.getChild(0)
|
|
mount = loader.loadModel('phase_5.5/models/estate/phoneMount-mod')
|
|
mount.setTransparency(0, 1)
|
|
self.model.reparentTo(mount)
|
|
self.ringSfx = loader.loadSfx('phase_3.5/audio/sfx/telephone_ring.ogg')
|
|
self.handleSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_handle2.ogg')
|
|
self.hangUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_hang_up.ogg')
|
|
self.pickUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_pickup1.ogg')
|
|
if self.initialScale:
|
|
mount.setScale(*self.initialScale)
|
|
self.usedInitialScale = 1
|
|
phoneSphere = CollisionSphere(0, -0.88, 0, 0.2)
|
|
phoneSphere.setTangible(0)
|
|
phoneSphereNode = CollisionNode(self.phoneSphereEvent)
|
|
phoneSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
|
|
phoneSphereNode.addSolid(phoneSphere)
|
|
mount.attachNewNode(phoneSphereNode)
|
|
if not self.model.find('**/CurveNode7').isEmpty():
|
|
self.setupCord()
|
|
return mount
|
|
|
|
def setupCamera(self, mode):
|
|
camera.wrtReparentTo(render)
|
|
if mode == PhoneGlobals.PHONE_MOVIE_PICKUP:
|
|
quat = Quat()
|
|
quat.setHpr((35, -8, 0))
|
|
LerpPosQuatInterval(camera, 1, (4, -4, base.localAvatar.getHeight() - 0.5), quat, blendType='easeOut', other=base.localAvatar).start()
|
|
|
|
def setupCord(self):
|
|
if self.cord:
|
|
self.cord.detachNode()
|
|
self.cord = None
|
|
self.cord = Rope.Rope(self.uniqueName('phoneCord'))
|
|
self.cord.setColor(0, 0, 0, 1)
|
|
self.cord.setup(4, ((self.receiverGeom, (0, 0, 0)),
|
|
(self.model.find('**/joint_curveNode1'), (0, 0, 0)),
|
|
(self.model.find('**/joint_curveNode2'), (0, 0, 0)),
|
|
(self.model.find('**/joint_curveNode3'), (0, 0, 0)),
|
|
(self.model.find('**/joint_curveNode4'), (0, 0, 0)),
|
|
(self.model.find('**/joint_curveNode5'), (0, 0, 0)),
|
|
(self.model.find('**/joint_curveNode6'), (0, 0, 0)),
|
|
(self.model.find('**/CurveNode7'), (0, 0, 0))))
|
|
self.cord.reparentTo(self.model)
|
|
self.cord.node().setBounds(BoundingSphere(Point3(-1.0, -3.2, 2.6), 2.0))
|
|
|
|
def disable(self):
|
|
self.notify.debug('disable')
|
|
taskMgr.remove(self.uniqueName('ringDoLater'))
|
|
self.clearInterval()
|
|
if self.phoneGui:
|
|
self.phoneGui.hide()
|
|
self.phoneGui.unload()
|
|
self.phoneGui = None
|
|
if self.phoneDialog:
|
|
self.phoneDialog.cleanup()
|
|
self.phoneDialog = None
|
|
self.__receiverToPhone()
|
|
if self.hasLocalAvatar:
|
|
self.freeAvatar()
|
|
self.ignoreAll()
|
|
DistributedFurnitureItem.DistributedFurnitureItem.disable(self)
|
|
|
|
def delete(self):
|
|
self.notify.debug('delete')
|
|
self.model.cleanup()
|
|
DistributedFurnitureItem.DistributedFurnitureItem.delete(self)
|
|
|
|
def setInitialScale(self, sx, sy, sz):
|
|
self.initialScale = (sx, sy, sz)
|
|
if not self.usedInitialScale and self.model:
|
|
self.setScale(*self.initialScale)
|
|
self.usedInitialScale = 1
|
|
|
|
def __handleEnterSphere(self, collEntry):
|
|
if self.phoneGui or self.smoothStarted:
|
|
return
|
|
|
|
if base.localAvatar.doId == self.lastAvId and globalClock.getFrameTime() <= self.lastTime + 0.5:
|
|
self.notify.debug('Ignoring duplicate entry for avatar.')
|
|
return
|
|
|
|
if self.hasLocalAvatar:
|
|
self.freeAvatar()
|
|
|
|
self.notify.debug('Entering Phone Sphere....')
|
|
taskMgr.remove(self.uniqueName('ringDoLater'))
|
|
base.localAvatar.obscureMoveFurnitureButton(1)
|
|
self.cr.playGame.getPlace().detectedPhoneCollision()
|
|
self.hasLocalAvatar = 1
|
|
self.sendUpdate('avatarEnter', [])
|
|
|
|
def __handlePhoneDone(self):
|
|
self.sendUpdate('avatarExit', [])
|
|
self.ignore(self.phoneGuiDoneEvent)
|
|
base.localAvatar.obscureMoveFurnitureButton(0)
|
|
self.setPos(self.getPos())
|
|
self.phoneGui = None
|
|
|
|
def freeAvatar(self):
|
|
if self.hasLocalAvatar:
|
|
base.localAvatar.speed = 0
|
|
taskMgr.remove(self.uniqueName('lerpCamera'))
|
|
base.localAvatar.posCamera(0, 0)
|
|
if base.cr.playGame.place != None:
|
|
base.cr.playGame.getPlace().setState('walk')
|
|
self.hasLocalAvatar = 0
|
|
self.ignore(self.pickupMovieDoneEvent)
|
|
self.lastTime = globalClock.getFrameTime()
|
|
|
|
def setLimits(self, numHouseItems):
|
|
self.numHouseItems = numHouseItems
|
|
|
|
def setMovie(self, mode, avId, timestamp):
|
|
elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp, bits=32)
|
|
elapsed = max(elapsed - self.movieDelay, 0)
|
|
self.ignore(self.pickupMovieDoneEvent)
|
|
if avId != 0:
|
|
self.lastAvId = avId
|
|
|
|
self.lastTime = globalClock.getFrameTime()
|
|
isLocalToon = avId == base.localAvatar.doId
|
|
avatar = self.cr.doId2do.get(avId)
|
|
self.notify.debug('setMovie: %s %s %s' % (mode, avId, isLocalToon))
|
|
|
|
if mode == PhoneGlobals.PHONE_MOVIE_CLEAR:
|
|
self.notify.debug('setMovie: clear')
|
|
self.numHouseItems = None
|
|
if self.phoneInUse:
|
|
self.clearInterval()
|
|
self.phoneInUse = 0
|
|
elif mode == PhoneGlobals.PHONE_MOVIE_PICKUP:
|
|
self.notify.debug('setMovie: gui')
|
|
if avatar:
|
|
interval = self.takePhoneInterval(avatar)
|
|
if isLocalToon:
|
|
self.setupCamera(mode)
|
|
interval.setDoneEvent(self.pickupMovieDoneEvent)
|
|
self.acceptOnce(self.pickupMovieDoneEvent, self.__showPhoneGui)
|
|
self.playInterval(interval, elapsed, avatar)
|
|
self.phoneInUse = 1
|
|
elif mode == PhoneGlobals.PHONE_MOVIE_HANGUP:
|
|
self.notify.debug('setMovie: gui')
|
|
if avatar:
|
|
interval = self.replacePhoneInterval(avatar)
|
|
self.playInterval(interval, elapsed, avatar)
|
|
self.numHouseItems = None
|
|
self.phoneInUse = 0
|
|
else:
|
|
self.notify.warning('unknown mode in setMovie: %s' % mode)
|
|
|
|
def __showPhoneGui(self):
|
|
if self.toonScale:
|
|
self.sendUpdate('setNewScale', [self.toonScale[0], self.toonScale[1], self.toonScale[2]])
|
|
self.phoneGui = CatalogScreen.CatalogScreen(phone=self, doneEvent=self.phoneGuiDoneEvent)
|
|
self.phoneGui.show()
|
|
self.accept(self.phoneGuiDoneEvent, self.__handlePhoneDone)
|
|
self.accept('phoneAsleep', self.__handlePhoneAsleep)
|
|
|
|
def __handlePhoneAsleep(self):
|
|
self.ignore('phoneAsleep')
|
|
if self.phoneGui:
|
|
self.phoneGui.unload()
|
|
self.__handlePhoneDone()
|
|
|
|
def requestPurchase(self, item, callback, optional = -1):
|
|
blob = item.getBlob(store=CatalogItem.Customization)
|
|
context = self.getCallbackContext(callback, [item])
|
|
self.sendUpdate('requestPurchaseMessage', [context, blob, optional])
|
|
|
|
def requestGiftPurchase(self, item, targetDoID, callback, optional = -1):
|
|
blob = item.getBlob(store=CatalogItem.Customization)
|
|
context = self.getCallbackContext(callback, [item])
|
|
self.sendUpdate('requestGiftPurchaseMessage', [context, targetDoID, blob, optional])
|
|
|
|
def requestPurchaseResponse(self, context, retcode):
|
|
self.doCallbackContext(context, [retcode])
|
|
|
|
def requestGiftPurchaseResponse(self, context, retcode):
|
|
self.doCallbackContext(context, [retcode])
|
|
|
|
def __clearDialog(self, event):
|
|
self.phoneDialog.cleanup()
|
|
self.phoneDialog = None
|
|
self.freeAvatar()
|
|
|
|
def takePhoneInterval(self, toon):
|
|
torso = TextEncoder.upper(toon.style.torso[0])
|
|
legs = TextEncoder.upper(toon.style.legs[0])
|
|
phoneOutAnim = '%s%s_phoneOut' % (torso, legs)
|
|
takePhoneAnim = '%s%s_takePhone' % (torso, legs)
|
|
phoneNeutralAnim = '%s%s_phoneNeutral' % (torso, legs)
|
|
self.toonScale = toon.getGeomNode().getChild(0).getScale(self.getParent())
|
|
walkTime = 1.0
|
|
scaleTime = 1.0
|
|
origScale = self.getScale()
|
|
origToonPos = toon.getPos()
|
|
origToonHpr = toon.getHpr()
|
|
self.origToonHpr = origToonHpr
|
|
self.setScale(self.toonScale)
|
|
toon.setPosHpr(self, 0, -4.5, 0, 0, 0, 0)
|
|
destToonPos = toon.getPos()
|
|
destToonHpr = toon.getHpr()
|
|
destToonHpr = VBase3(PythonUtil.fitSrcAngle2Dest(destToonHpr[0], origToonHpr[0]), destToonHpr[1], destToonHpr[2])
|
|
self.setScale(origScale)
|
|
toon.setPos(origToonPos)
|
|
toon.setHpr(origToonHpr)
|
|
walkToPhone = Sequence(Func(toon.stopSmooth), Func(toon.loop, 'walk'), Func(base.playSfx, base.localAvatar.soundWalk), toon.posHprInterval(walkTime, destToonPos, destToonHpr, blendType='easeInOut'), Func(toon.loop, 'neutral'), Func(toon.startSmooth))
|
|
interval = Sequence(Parallel(walkToPhone, ActorInterval(self.model, phoneOutAnim), self.scaleInterval(scaleTime, self.toonScale, blendType='easeInOut')), Parallel(ActorInterval(self.model, takePhoneAnim), ActorInterval(toon, 'takePhone'), Sequence(Wait(0.625), Func(base.playSfx, self.pickUpSfx), Func(self.__receiverToHand, toon), Wait(1), Func(base.playSfx, self.handleSfx))), Func(self.model.loop, phoneNeutralAnim), Func(toon.loop, 'phoneNeutral'), Func(base.playSfx, self.ringSfx))
|
|
return interval
|
|
|
|
def replacePhoneInterval(self, toon):
|
|
torso = TextEncoder.upper(toon.style.torso[0])
|
|
legs = TextEncoder.upper(toon.style.legs[0])
|
|
phoneBackAnim = '%s%s_phoneBack' % (torso, legs)
|
|
scaleTime = 1.0
|
|
interval = Sequence(Parallel(ActorInterval(self.model, phoneBackAnim), ActorInterval(toon, 'phoneBack'), Sequence(Wait(1.0), Func(self.__receiverToPhone), Func(base.playSfx, self.hangUpSfx))), self.scaleInterval(scaleTime, localAvatar.getGeomNode().getScale()[2], blendType='easeInOut'), Func(toon.loop, 'neutral'))
|
|
if self.origToonHpr:
|
|
interval.append(Func(toon.setHpr, self.origToonHpr))
|
|
self.origToonHpr = None
|
|
if toon == base.localAvatar:
|
|
interval.append(Func(self.freeAvatar))
|
|
return interval
|
|
|
|
def __receiverToHand(self, toon):
|
|
self.receiverGeom.reparentTo(toon.leftHand)
|
|
self.receiverGeom.setPosHpr(0.0906813, 0.380375, 0.1, 32.41, 70.68, 137.04)
|
|
|
|
def __receiverToPhone(self):
|
|
self.receiverGeom.reparentTo(self.receiverJoint)
|
|
self.receiverGeom.setPosHpr(0, 0, 0, 0, 0, 0)
|
|
|
|
def playInterval(self, interval, elapsed, avatar):
|
|
if self.interval != None:
|
|
self.interval.finish()
|
|
self.interval = None
|
|
self.interval = interval
|
|
self.interval.start(elapsed)
|
|
if self.intervalAvatar != avatar:
|
|
if self.intervalAvatar:
|
|
self.ignore(self.intervalAvatar.uniqueName('disable'))
|
|
if avatar:
|
|
self.accept(avatar.uniqueName('disable'), self.clearInterval)
|
|
self.intervalAvatar = avatar
|
|
|
|
def clearInterval(self):
|
|
if self.interval != None:
|
|
self.interval.finish()
|
|
self.interval = None
|
|
if self.intervalAvatar:
|
|
self.ignore(self.intervalAvatar.uniqueName('disable'))
|
|
self.intervalAvatar = None
|
|
self.__receiverToPhone()
|
|
self.model.pose('SS_phoneOut', 0)
|
|
self.phoneInUse = 0
|
|
|
|
def ringIfHasPhoneQuest(self, task):
|
|
if Quests.avatarHasPhoneQuest(base.localAvatar) and not Quests.avatarHasCompletedPhoneQuest(base.localAvatar):
|
|
self.ring()
|
|
return Task.done
|
|
|
|
def ring(self):
|
|
if self.phoneInUse:
|
|
return 0
|
|
phone = self.find('**/prop_phone')
|
|
r = 2.0
|
|
w = 0.05
|
|
shakeOnce = Sequence(Func(phone.setR, r), Wait(w), Func(phone.setR, -r), Wait(w))
|
|
shakeSeq = Sequence()
|
|
for i in xrange(16):
|
|
shakeSeq.append(shakeOnce)
|
|
|
|
ringIval = Parallel(Func(base.playSfx, self.ringSfx), shakeSeq, Func(phone.setR, 0))
|
|
self.playInterval(ringIval, 0.0, None)
|
|
|
|
def requestGiftAvatar(self, doId):
|
|
if not self.phoneGui:
|
|
return
|
|
|
|
self.sendUpdate('requestGiftAvatar', [doId])
|
|
|
|
def setGiftAvatar(self, fields):
|
|
if not self.phoneGui:
|
|
return
|
|
|
|
self.phoneGui.setFriendReady(GiftAvatar.createFromJson(fields)) |