toontown-just-works/toontown/building/DistributedElevator.py
2024-07-07 18:08:39 -05:00

520 lines
21 KiB
Python

from panda3d.core import *
from direct.distributed.ClockDelta import *
from direct.interval.IntervalGlobal import *
from ElevatorConstants import *
from ElevatorUtils import *
from direct.showbase import PythonUtil
from direct.directnotify import DirectNotifyGlobal
from direct.fsm import ClassicFSM, State
from direct.distributed import DistributedObject
from direct.fsm import State
from toontown.toonbase import TTLocalizer, ToontownGlobals
from direct.task.Task import Task
from toontown.distributed import DelayDelete
from toontown.hood import ZoneUtil
from toontown.building import BoardingGroupShow
class DistributedElevator(DistributedObject.DistributedObject):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevator')
JumpOutOffsets = JumpOutOffsets
def __init__(self, cr):
DistributedObject.DistributedObject.__init__(self, cr)
self.bldgRequest = None
self.toonRequests = {}
self.deferredSlots = []
self.localToonOnBoard = 0
self.boardedAvIds = {}
self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg')
self.finalOpenSfx = None
self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg')
self.elevatorFSM = None
self.finalCloseSfx = None
self.elevatorPoints = ElevatorPoints
self.fillSlotTrack = None
self.type = ELEVATOR_NORMAL
self.countdownTime = ElevatorData[self.type]['countdown']
self.__toonTracks = {}
self.fsm = ClassicFSM.ClassicFSM('DistributedElevator', [State.State('off', self.enterOff, self.exitOff, ['opening',
'waitEmpty',
'waitCountdown',
'closing',
'closed']),
State.State('opening', self.enterOpening, self.exitOpening, ['waitEmpty', 'waitCountdown']),
State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']),
State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'closing']),
State.State('closing', self.enterClosing, self.exitClosing, ['closed', 'waitEmpty']),
State.State('closed', self.enterClosed, self.exitClosed, ['opening'])], 'off', 'off')
self.fsm.enterInitialState()
self.isSetup = 0
self.__preSetupState = None
self.bigElevator = 0
self.offsetNp = None
return
def generate(self):
DistributedObject.DistributedObject.generate(self)
def setupElevator(self):
collisionRadius = ElevatorData[self.type]['collRadius']
self.elevatorSphere = CollisionSphere(0, 5, 0, collisionRadius)
self.elevatorSphere.setTangible(0)
self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere'))
self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
self.elevatorSphereNode.addSolid(self.elevatorSphere)
self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode)
self.elevatorSphereNodePath.reparentTo(self.getElevatorModel())
self.elevatorSphereNodePath.stash()
self.boardedAvIds = {}
self.openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.type)
self.closeDoors = getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.type)
self.closeDoors = Sequence(self.closeDoors, Func(self.onDoorCloseFinish))
self.finishSetup()
def finishSetup(self):
self.isSetup = 1
self.offsetNP = self.getElevatorModel().attachNewNode('dummyNP')
if self.__preSetupState:
self.fsm.request(self.__preSetupState, [0])
self.__preSetupState = None
for slot in self.deferredSlots:
self.fillSlot(*slot)
self.deferredSlots = []
return
def disable(self):
if self.bldgRequest:
self.cr.relatedObjectMgr.abortRequest(self.bldgRequest)
self.bldgRequest = None
for request in self.toonRequests.values():
self.cr.relatedObjectMgr.abortRequest(request)
self.toonRequests = {}
if hasattr(self, 'openDoors'):
self.openDoors.pause()
if hasattr(self, 'closeDoors'):
self.closeDoors.pause()
self.clearToonTracks()
self.fsm.request('off')
DistributedObject.DistributedObject.disable(self)
return
def delete(self):
if self.isSetup:
self.elevatorSphereNodePath.removeNode()
del self.elevatorSphereNodePath
del self.elevatorSphereNode
del self.elevatorSphere
del self.bldg
if self.leftDoor:
del self.leftDoor
if self.rightDoor:
del self.rightDoor
if hasattr(self, 'openDoors'):
del self.openDoors
if hasattr(self, 'closeDoors'):
del self.closeDoors
del self.fsm
del self.openSfx
del self.closeSfx
self.isSetup = 0
self.fillSlotTrack = None
if not self.offsetNp:
return
self.offsetNP.removeNode()
if hasattr(base.localAvatar, 'elevatorNotifier'):
base.localAvatar.elevatorNotifier.cleanup()
DistributedObject.DistributedObject.delete(self)
return
def setBldgDoId(self, bldgDoId):
self.bldgDoId = bldgDoId
self.bldgRequest = self.cr.relatedObjectMgr.requestObjects([bldgDoId], allCallback=self.gotBldg, timeout=2)
def gotBldg(self, buildingList):
self.bldgRequest = None
self.bldg = buildingList[0]
if not self.bldg:
self.notify.error('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId))
return
self.setupElevator()
return
def gotToon(self, index, avId, toonList):
request = self.toonRequests.get(index)
if request:
del self.toonRequests[index]
self.fillSlot(index, avId)
else:
self.notify.error('gotToon: already had got toon in slot %s.' % index)
def setState(self, state, timestamp):
if self.isSetup:
self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
else:
self.__preSetupState = state
def fillSlot0(self, avId, wantBoardingShow):
self.fillSlot(0, avId, wantBoardingShow)
def fillSlot1(self, avId, wantBoardingShow):
self.fillSlot(1, avId, wantBoardingShow)
def fillSlot2(self, avId, wantBoardingShow):
self.fillSlot(2, avId, wantBoardingShow)
def fillSlot3(self, avId, wantBoardingShow):
self.fillSlot(3, avId, wantBoardingShow)
def fillSlot4(self, avId, wantBoardingShow):
self.fillSlot(4, avId, wantBoardingShow)
def fillSlot5(self, avId, wantBoardingShow):
self.fillSlot(5, avId, wantBoardingShow)
def fillSlot6(self, avId, wantBoardingShow):
self.fillSlot(6, avId, wantBoardingShow)
def fillSlot7(self, avId, wantBoardingShow):
self.fillSlot(7, avId, wantBoardingShow)
def fillSlot(self, index, avId, wantBoardingShow = 0):
self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId))
request = self.toonRequests.get(index)
if request:
self.cr.relatedObjectMgr.abortRequest(request)
del self.toonRequests[index]
if avId == 0:
pass
elif avId not in self.cr.doId2do:
func = PythonUtil.Functor(self.gotToon, index, avId)
self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func)
elif not self.isSetup:
self.deferredSlots.append((index, avId, wantBoardingShow))
else:
if avId == base.localAvatar.getDoId():
place = base.cr.playGame.getPlace()
if not place:
return
place.detectedElevatorCollision(self)
elevator = self.getPlaceElevator()
if elevator == None:
if place.fsm.hasStateNamed('elevator'):
place.fsm.request('elevator')
elif place.fsm.hasStateNamed('Elevator'):
place.fsm.request('Elevator')
elevator = self.getPlaceElevator()
if not elevator:
return
self.localToonOnBoard = 1
if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty:
localAvatar.boardingParty.forceCleanupInviteePanel()
localAvatar.boardingParty.forceCleanupInviterPanels()
if hasattr(base.localAvatar, 'elevatorNotifier'):
base.localAvatar.elevatorNotifier.cleanup()
cameraTrack = Sequence()
cameraTrack.append(Func(elevator.fsm.request, 'boarding', [self.getElevatorModel()]))
cameraTrack.append(Func(elevator.fsm.request, 'boarded'))
toon = self.cr.doId2do[avId]
toon.stopSmooth()
if not wantBoardingShow:
toon.setZ(self.getElevatorModel(), self.elevatorPoints[index][2])
toon.setShadowHeight(0)
if toon.isDisguised:
animInFunc = Sequence(Func(toon.suit.loop, 'walk'))
animFunc = Sequence(Func(toon.setAnimState, 'neutral', 1.0), Func(toon.suit.loop, 'neutral'))
else:
animInFunc = Sequence(Func(toon.setAnimState, 'run', 1.0))
animFunc = Func(toon.setAnimState, 'neutral', 1.0)
toon.headsUp(self.getElevatorModel(), apply(Point3, self.elevatorPoints[index]))
track = Sequence(animInFunc, LerpPosInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.75, apply(Point3, self.elevatorPoints[index]), other=self.getElevatorModel()), LerpHprInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.25, Point3(180, 0, 0), other=self.getElevatorModel()), Func(self.clearToonTrack, avId), animFunc, name=toon.uniqueName('fillElevator'), autoPause=1)
if wantBoardingShow:
boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, False)
track = Sequence(boardingTrack, track)
if avId == base.localAvatar.getDoId():
cameraWaitTime = 2.5
if boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN:
cameraWaitTime = 0.5
elif boardingTrackType == BoardingGroupShow.TRACK_TYPE_POOF:
cameraWaitTime = 1
cameraTrack = Sequence(Wait(cameraWaitTime), cameraTrack)
if self.canHideBoardingQuitBtn(avId):
track = Sequence(Func(localAvatar.boardingParty.groupPanel.disableQuitButton), track)
if avId == base.localAvatar.getDoId():
track = Parallel(cameraTrack, track)
track.delayDelete = DelayDelete.DelayDelete(toon, 'Elevator.fillSlot')
self.storeToonTrack(avId, track)
track.start()
self.fillSlotTrack = track
self.boardedAvIds[avId] = None
return
def emptySlot0(self, avId, timestamp, time):
self.emptySlot(0, avId, timestamp, time)
def emptySlot1(self, avId, timestamp, time):
self.emptySlot(1, avId, timestamp, time)
def emptySlot2(self, avId, timestamp, time):
self.emptySlot(2, avId, timestamp, time)
def emptySlot3(self, avId, timestamp, time):
self.emptySlot(3, avId, timestamp, time)
def emptySlot4(self, avId, timestamp, time):
self.emptySlot(4, avId, timestamp, time)
def emptySlot5(self, avId, timestamp, time):
self.emptySlot(5, avId, timestamp)
def emptySlot6(self, avId, timestamp, time):
self.emptySlot(6, avId, timestamp, time)
def emptySlot7(self, avId, timestamp, time):
self.emptySlot(7, avId, timestamp, time)
def notifyToonOffElevator(self, toon):
toon.setAnimState('neutral', 1.0)
if toon == base.localAvatar:
doneStatus = {'where': 'exit'}
elevator = self.getPlaceElevator()
if elevator:
elevator.signalDone(doneStatus)
self.localToonOnBoard = 0
else:
toon.startSmooth()
def emptySlot(self, index, avId, timestamp, timeSent = 0):
if self.fillSlotTrack:
self.fillSlotTrack.finish()
self.fillSlotTrack = None
if avId == 0:
pass
elif not self.isSetup:
newSlots = []
for slot in self.deferredSlots:
if slot[0] != index:
newSlots.append(slot)
self.deferredSlots = newSlots
else:
timeToSet = self.countdownTime
if timeSent > 0:
timeToSet = timeSent
if avId in self.cr.doId2do:
if hasattr(self, 'clockNode'):
if timestamp < timeToSet and timestamp >= 0:
self.countdown(timeToSet - timestamp)
else:
self.countdown(timeToSet)
toon = self.cr.doId2do[avId]
toon.stopSmooth()
if toon.isDisguised:
toon.suit.loop('walk')
animFunc = Func(toon.suit.loop, 'neutral')
else:
toon.setAnimState('run', 1.0)
animFunc = Func(toon.setAnimState, 'neutral', 1.0)
track = Sequence(LerpPosInterval(toon, TOON_EXIT_ELEVATOR_TIME, Point3(*self.JumpOutOffsets[index]), other=self.getElevatorModel()), animFunc, Func(self.notifyToonOffElevator, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyElevator'), autoPause=1)
if self.canHideBoardingQuitBtn(avId):
track.append(Func(localAvatar.boardingParty.groupPanel.enableQuitButton))
track.append(Func(localAvatar.boardingParty.enableGoButton))
track.delayDelete = DelayDelete.DelayDelete(toon, 'Elevator.emptySlot')
self.storeToonTrack(avId, track)
track.start()
if avId == base.localAvatar.getDoId():
messenger.send('exitElevator')
if avId in self.boardedAvIds:
del self.boardedAvIds[avId]
else:
self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!')
return
def handleEnterSphere(self, collEntry):
self.notify.debug('Entering Elevator Sphere....')
if base.localAvatar.hp > 0:
self.cr.playGame.getPlace().detectedElevatorCollision(self)
toon = base.localAvatar
self.sendUpdate('requestBoard', [])
def rejectBoard(self, avId, reason = 0):
if hasattr(base.localAvatar, 'elevatorNotifier'):
if reason == REJECT_PROMOTION:
base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage)
doneStatus = {'where': 'reject'}
elevator = self.getPlaceElevator()
if elevator:
elevator.signalDone(doneStatus)
def timerTask(self, task):
countdownTime = int(task.duration - task.time)
timeStr = str(countdownTime)
if self.clockNode.getText() != timeStr:
self.clockNode.setText(timeStr)
if task.time >= task.duration:
return Task.done
else:
return Task.cont
def countdown(self, duration):
countdownTask = Task(self.timerTask)
countdownTask.duration = duration
taskMgr.remove(self.uniqueName('elevatorTimerTask'))
return taskMgr.add(countdownTask, self.uniqueName('elevatorTimerTask'))
def handleExitButton(self):
self.sendUpdate('requestExit')
def enterWaitCountdown(self, ts):
self.elevatorSphereNodePath.unstash()
self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere)
self.accept('elevatorExitButton', self.handleExitButton)
def exitWaitCountdown(self):
self.elevatorSphereNodePath.stash()
self.ignore(self.uniqueName('enterelevatorSphere'))
self.ignore('elevatorExitButton')
self.ignore('localToonLeft')
taskMgr.remove(self.uniqueName('elevatorTimerTask'))
self.clock.removeNode()
del self.clock
del self.clockNode
def enterClosing(self, ts):
if self.localToonOnBoard:
elevator = self.getPlaceElevator()
if elevator:
elevator.fsm.request('elevatorClosing')
self.closeDoors.start(ts)
def exitClosing(self):
pass
def onDoorCloseFinish(self):
for avId in self.boardedAvIds.keys():
av = self.cr.doId2do.get(avId)
if av is not None:
if av.getParent().compareTo(self.getElevatorModel()) == 0:
av.detachNode()
self.boardedAvIds = {}
return
def enterClosed(self, ts):
self.forceDoorsClosed()
self.__doorsClosed(self.getZoneId())
def exitClosed(self):
pass
def forceDoorsOpen(self):
openDoors(self.leftDoor, self.rightDoor)
def forceDoorsClosed(self):
self.closeDoors.finish()
closeDoors(self.leftDoor, self.rightDoor)
def enterOff(self):
pass
def exitOff(self):
pass
def enterWaitEmpty(self, ts):
pass
def exitWaitEmpty(self):
pass
def enterOpening(self, ts):
self.openDoors.start(ts)
def exitOpening(self):
pass
def startCountdownClock(self, countdownTime, ts):
self.clockNode = TextNode('elevatorClock')
self.clockNode.setFont(ToontownGlobals.getSignFont())
self.clockNode.setAlign(TextNode.ACenter)
self.clockNode.setTextColor(0.5, 0.5, 0.5, 1)
self.clockNode.setText(str(int(countdownTime)))
self.clock = self.getElevatorModel().attachNewNode(self.clockNode)
self.clock.setPosHprScale(0, 2.0, 7.5, 0, 0, 0, 2.0, 2.0, 2.0)
if ts < countdownTime:
self.countdown(countdownTime - ts)
def _getDoorsClosedInfo(self):
return ('suitInterior', 'suitInterior')
def __doorsClosed(self, zoneId):
if self.localToonOnBoard:
hoodId = ZoneUtil.getHoodId(zoneId)
loader, where = self._getDoorsClosedInfo()
doneStatus = {'loader': loader,
'where': where,
'hoodId': hoodId,
'zoneId': zoneId,
'shardId': None}
elevator = self.elevatorFSM
del self.elevatorFSM
elevator.signalDone(doneStatus)
base.camLens.setMinFov(ToontownGlobals.CBElevatorFov/(4./3.))
def getElevatorModel(self):
self.notify.error('getElevatorModel: pure virtual -- inheritors must override')
def getPlaceElevator(self):
place = self.cr.playGame.getPlace()
if place:
if hasattr(place, 'elevator'):
return place.elevator
else:
self.notify.warning("Place was in state '%s' instead of Elevator." % place.fsm.getCurrentState().getName())
place.detectedElevatorCollision(self)
else:
self.notify.warning("Place didn't exist")
return None
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()
if self.__toonTracks.get(avId):
DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId])
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 getDestName(self):
return None
def getOffsetPos(self, seatIndex = 0):
return self.JumpOutOffsets[seatIndex]
def getOffsetPosWrtToonParent(self, toon, seatIndex = 0):
self.offsetNP.setPos(apply(Point3, self.getOffsetPos(seatIndex)))
return self.offsetNP.getPos(toon.getParent())
def getOffsetPosWrtRender(self, seatIndex = 0):
self.offsetNP.setPos(apply(Point3, self.getOffsetPos(seatIndex)))
return self.offsetNP.getPos(render)
def canHideBoardingQuitBtn(self, avId):
return avId == localAvatar.doId and hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.groupPanel
def getBoardingTrack(self, toon, seatIndex, wantToonRotation):
self.boardingGroupShow = BoardingGroupShow.BoardingGroupShow(toon)
track, trackType = self.boardingGroupShow.getBoardingTrack(self.getElevatorModel(), self.getOffsetPosWrtToonParent(toon, seatIndex), self.getOffsetPosWrtRender(seatIndex), wantToonRotation)
return (track, trackType)