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

460 lines
17 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
from direct.distributed import DistributedObject
from direct.fsm import State
from toontown.toonbase import TTLocalizer
from toontown.toonbase import ToontownGlobals
from direct.task.Task import Task
from toontown.hood import ZoneUtil
from direct.fsm.FSM import FSM
class DistributedElevatorFSM(DistributedObject.DistributedObject, FSM):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevator')
defaultTransitions = {'Off': ['Opening', 'Closed', 'Off'],
'Opening': ['WaitEmpty',
'WaitCountdown',
'Opening',
'Closing'],
'WaitEmpty': ['WaitCountdown', 'Closing', 'Off'],
'WaitCountdown': ['WaitEmpty', 'AllAboard', 'Closing'],
'AllAboard': ['WaitEmpty', 'Closing'],
'Closing': ['Closed',
'WaitEmpty',
'Closing',
'Opening'],
'Closed': ['Opening']}
id = 0
def __init__(self, cr):
DistributedObject.DistributedObject.__init__(self, cr)
FSM.__init__(self, 'Elevator_%s_FSM' % self.id)
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.type = ELEVATOR_NORMAL
self.countdownTime = ElevatorData[self.type]['countdown']
self.isSetup = 0
self.__preSetupState = None
self.bigElevator = 0
self.offTrack = [None,
None,
None,
None]
self.boardingParty = None
return
def generate(self):
DistributedObject.DistributedObject.generate(self)
def setBoardingParty(self, party):
self.boardingParty = party
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.hide()
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.openDoors = Sequence(self.openDoors, Func(self.onDoorOpenFinish))
self.closeDoors = Sequence(self.closeDoors, Func(self.onDoorCloseFinish))
self.finishSetup()
def finishSetup(self):
self.isSetup = 1
if self.__preSetupState:
self.request(self.__preSetupState, 0)
self.__preSetupState = None
for slot in self.deferredSlots:
self.fillSlot(*slot)
self.deferredSlots = []
return
def disable(self):
for track in self.offTrack:
if track:
if track.isPlaying():
track.pause()
track = None
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.request('off')
DistributedObject.DistributedObject.disable(self)
return
def delete(self):
for track in self.offTrack:
if track:
if track.isPlaying():
track.pause()
track = None
self.ignoreAll()
if self.isSetup:
self.elevatorSphereNodePath.removeNode()
del self.elevatorSphereNodePath
del self.elevatorSphereNode
del self.elevatorSphere
del self.bldg
del self.leftDoor
del self.rightDoor
del self.openDoors
del self.closeDoors
del self.openSfx
del self.closeSfx
self.isSetup = 0
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.request(state, globalClockDelta.localElapsedTime(timestamp))
else:
self.__preSetupState = state
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 fillSlot4(self, avId):
self.fillSlot(4, avId)
def fillSlot5(self, avId):
self.fillSlot(5, avId)
def fillSlot6(self, avId):
self.fillSlot(6, avId)
def fillSlot7(self, avId):
self.fillSlot(7, avId)
def fillSlot(self, index, avId):
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))
else:
if avId == base.localAvatar.getDoId():
self.localToonOnBoard = 1
elevator = self.getPlaceElevator()
elevator.fsm.request('boarding', [self.getElevatorModel()])
elevator.fsm.request('boarded')
toon = self.cr.doId2do[avId]
toon.stopSmooth()
toon.setZ(self.getElevatorModel(), self.getScaledPoint(index)[2])
toon.setShadowHeight(0)
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)
toon.headsUp(self.getElevatorModel(), apply(Point3, self.getScaledPoint(index)))
track = Sequence(LerpPosInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.75, apply(Point3, self.getScaledPoint(index)), other=self.getElevatorModel()), LerpHprInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.25, Point3(180, 0, 0), other=self.getElevatorModel()), animFunc, name=toon.uniqueName('fillElevator'), autoPause=1)
track.start()
self.boardedAvIds[avId] = index
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 emptySlot4(self, avId, timestamp):
self.emptySlot(4, avId, timestamp)
def emptySlot5(self, avId, timestamp):
self.emptySlot(5, avId, timestamp)
def emptySlot6(self, avId, timestamp):
self.emptySlot(6, avId, timestamp)
def emptySlot7(self, avId, timestamp):
self.emptySlot(7, avId, timestamp)
def notifyToonOffElevator(self, toon):
if self.cr:
toon.setAnimState('neutral', 1.0)
if toon == base.localAvatar:
print 'moving the local toon off the elevator'
doneStatus = {'where': 'exit'}
elevator = self.getPlaceElevator()
elevator.signalDone(doneStatus)
self.localToonOnBoard = 0
else:
toon.startSmooth()
return
def emptySlot(self, index, avId, timestamp):
print 'Emptying slot: %d for %d' % (index, avId)
if avId == 0:
pass
elif not self.isSetup:
newSlots = []
for slot in self.deferredSlots:
if slot[0] != index:
newSlots.append(slot)
self.deferredSlots = newSlots
elif avId in self.cr.doId2do:
if hasattr(self, 'clockNode'):
if timestamp < self.countdownTime and timestamp >= 0:
self.countdown(self.countdownTime - timestamp)
else:
self.countdown(self.countdownTime)
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)
if self.offTrack[index]:
if self.offTrack[index].isPlaying():
self.offTrack[index].finish()
self.offTrack[index] = None
self.offTrack[index] = Sequence(LerpPosInterval(toon, TOON_EXIT_ELEVATOR_TIME, Point3(0, -ElevatorData[self.type]['collRadius'], 0), startPos=apply(Point3, self.getScaledPoint(index)), other=self.getElevatorModel()), animFunc, Func(self.notifyToonOffElevator, toon), name=toon.uniqueName('emptyElevator'), autoPause=1)
if avId == base.localAvatar.getDoId():
messenger.send('exitElevator')
scale = base.localAvatar.getScale()
self.offTrack[index].append(Func(base.camera.setScale, scale))
self.offTrack[index].start()
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):
print 'rejectBoard %s' % reason
if hasattr(base.localAvatar, 'elevatorNotifier'):
if reason == REJECT_PROMOTION:
base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage)
elif reason == REJECT_BLOCKED_ROOM:
base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorBlockedRoom)
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)
self.lastState = self.state
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 self.closeDoors.isPlaying() or self.lastState == 'closed' or self.openDoors.isPlaying():
self.doorsNeedToClose = 1
else:
self.doorsNeedToClose = 0
self.closeDoors.start(ts)
def exitClosing(self):
pass
def onDoorOpenFinish(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()
return
def enterClosed(self, ts):
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):
self.lastState = self.state
def exitOff(self):
pass
def enterWaitEmpty(self, ts):
self.lastState = self.state
def exitWaitEmpty(self):
pass
def enterOpening(self, ts):
self.openDoors.start(ts)
self.lastState = self.state
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, 4.4, 6.0, 0, 0, 0, 2.0, 2.0, 2.0)
if ts < countdownTime:
self.countdown(countdownTime - ts)
def __doorsClosed(self, zoneId):
if self.localToonOnBoard:
self.localAvatar.stopGlitchKiller()
hoodId = ZoneUtil.getHoodId(zoneId)
loader = 'suitInterior'
where = 'suitInterior'
if base.cr.wantCogdominiums:
loader = 'cogdoInterior'
where = 'cogdoInterior'
doneStatus = {'loader': loader,
'where': where,
'hoodId': hoodId,
'zoneId': zoneId,
'shardId': None}
elevator = self.elevatorFSM
del self.elevatorFSM
elevator.signalDone(doneStatus)
return
def getElevatorModel(self):
self.notify.error('getElevatorModel: pure virtual -- inheritors must override')
def getPlaceElevator(self):
place = self.cr.playGame.getPlace()
if not hasattr(place, 'elevator'):
self.notify.warning("Place was in state '%s' instead of Elevator." % place.state)
place.detectedElevatorCollision(self)
return None
return place.elevator
def getScaledPoint(self, index):
point = self.elevatorPoints[index]
return point
def getDestName(self):
return None