from toontown.toonbase.ToonBaseGlobal import *
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.distributed.ClockDelta import *
from toontown.toonbase import ToontownGlobals
from direct.directnotify import DirectNotifyGlobal
from direct.fsm import ClassicFSM, State
from direct.distributed import DistributedObject
from toontown.hood import ZoneUtil
from toontown.suit import Suit
from toontown.distributed import DelayDelete
import FADoorCodes
from direct.task.Task import Task
import DoorTypes
from toontown.toontowngui import TTDialog
from toontown.toonbase import TTLocalizer
from toontown.toontowngui import TeaserPanel
from toontown.distributed.DelayDeletable import DelayDeletable
if (__debug__):
    import pdb

class DistributedDoor(DistributedObject.DistributedObject, DelayDeletable):

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.openSfx = base.loader.loadSfx('phase_3.5/audio/sfx/Door_Open_1.mp3')
        self.closeSfx = base.loader.loadSfx('phase_3.5/audio/sfx/Door_Close_1.mp3')
        self.nametag = None
        self.fsm = ClassicFSM.ClassicFSM('DistributedDoor_right', [State.State('off', self.enterOff, self.exitOff, ['closing',
          'closed',
          'opening',
          'open']),
         State.State('closing', self.enterClosing, self.exitClosing, ['closed', 'opening']),
         State.State('closed', self.enterClosed, self.exitClosed, ['opening']),
         State.State('opening', self.enterOpening, self.exitOpening, ['open']),
         State.State('open', self.enterOpen, self.exitOpen, ['closing', 'open'])], 'off', 'off')
        self.fsm.enterInitialState()
        self.exitDoorFSM = ClassicFSM.ClassicFSM('DistributedDoor_left', [State.State('off', self.exitDoorEnterOff, self.exitDoorExitOff, ['closing',
          'closed',
          'opening',
          'open']),
         State.State('closing', self.exitDoorEnterClosing, self.exitDoorExitClosing, ['closed', 'opening']),
         State.State('closed', self.exitDoorEnterClosed, self.exitDoorExitClosed, ['opening']),
         State.State('opening', self.exitDoorEnterOpening, self.exitDoorExitOpening, ['open']),
         State.State('open', self.exitDoorEnterOpen, self.exitDoorExitOpen, ['closing', 'open'])], 'off', 'off')
        self.exitDoorFSM.enterInitialState()
        self.specialDoorTypes = {DoorTypes.EXT_HQ: 0,
         DoorTypes.EXT_COGHQ: 0,
         DoorTypes.INT_COGHQ: 0,
         DoorTypes.EXT_KS: 0,
         DoorTypes.INT_KS: 0}
        self.doorX = 1.5
        return

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        self.avatarTracks = []
        self.avatarExitTracks = []
        self.avatarIDList = []
        self.avatarExitIDList = []
        self.doorTrack = None
        self.doorExitTrack = None
        return

    def disable(self):
        self.clearNametag()
        taskMgr.remove(self.checkIsDoorHitTaskName())
        self.ignore(self.getEnterTriggerEvent())
        self.ignore(self.getExitTriggerEvent())
        self.ignore('clearOutToonInterior')
        self.fsm.request('off')
        self.exitDoorFSM.request('off')
        if self.__dict__.has_key('building'):
            del self.building
        self.finishAllTracks()
        self.avatarIDList = []
        self.avatarExitIDList = []
        if hasattr(self, 'tempDoorNodePath'):
            self.tempDoorNodePath.removeNode()
            del self.tempDoorNodePath
        DistributedObject.DistributedObject.disable(self)

    def delete(self):
        del self.fsm
        del self.exitDoorFSM
        del self.openSfx
        del self.closeSfx
        DistributedObject.DistributedObject.delete(self)

    def wantsNametag(self):
        return not ZoneUtil.isInterior(self.zoneId)

    def setupNametag(self):
        if not self.wantsNametag():
            return
        if self.nametag == None:
            self.nametag = NametagGroup()
            self.nametag.setFont(ToontownGlobals.getBuildingNametagFont())
            if TTLocalizer.BuildingNametagShadow:
                self.nametag.setShadow(*TTLocalizer.BuildingNametagShadow)
            self.nametag.setContents(Nametag.CName)
            self.nametag.setColorCode(NametagGroup.CCToonBuilding)
            self.nametag.setActive(0)
            self.nametag.setAvatar(self.getDoorNodePath())
            self.nametag.setObjectCode(self.block)
            name = self.cr.playGame.dnaStore.getTitleFromBlockNumber(self.block)
            self.nametag.setName(name)
            self.nametag.manage(base.marginManager)
        return

    def clearNametag(self):
        if self.nametag != None:
            self.nametag.unmanage(base.marginManager)
            self.nametag.setAvatar(NodePath())
            self.nametag = None
        return

    def getTriggerName(self):
        if self.doorType == DoorTypes.INT_HQ or self.specialDoorTypes.has_key(self.doorType):
            return 'door_trigger_' + str(self.block) + '_' + str(self.doorIndex)
        else:
            return 'door_trigger_' + str(self.block)

    def getTriggerName_wip(self):
        name = 'door_trigger_%d' % (self.doId,)
        return name

    def getEnterTriggerEvent(self):
        return 'enter' + self.getTriggerName()

    def getExitTriggerEvent(self):
        return 'exit' + self.getTriggerName()

    def hideDoorParts(self):
        if self.specialDoorTypes.has_key(self.doorType):
            self.hideIfHasFlat(self.findDoorNode('rightDoor'))
            self.hideIfHasFlat(self.findDoorNode('leftDoor'))
            self.findDoorNode('doorFrameHoleRight').hide()
            self.findDoorNode('doorFrameHoleLeft').hide()
        else:
            return

    def setTriggerName(self):
        if self.specialDoorTypes.has_key(self.doorType):
            building = self.getBuilding()
            doorTrigger = building.find('**/door_' + str(self.doorIndex) + '/**/door_trigger*')
            doorTrigger.node().setName(self.getTriggerName())
        else:
            return

    def setTriggerName_wip(self):
        building = self.getBuilding()
        doorTrigger = building.find('**/door_%d/**/door_trigger_%d' % (self.doorIndex, self.block))
        if doorTrigger.isEmpty():
            doorTrigger = building.find('**/door_trigger_%d' % (self.block,))
        if doorTrigger.isEmpty():
            doorTrigger = building.find('**/door_%d/**/door_trigger_*' % (self.doorIndex,))
        if doorTrigger.isEmpty():
            doorTrigger = building.find('**/door_trigger_*')
        doorTrigger.node().setName(self.getTriggerName())

    def setZoneIdAndBlock(self, zoneId, block):
        self.zoneId = zoneId
        self.block = block

    def setDoorType(self, doorType):
        self.notify.debug('Door type = ' + str(doorType) + ' on door #' + str(self.doId))
        self.doorType = doorType

    def setDoorIndex(self, doorIndex):
        self.doorIndex = doorIndex

    def setSwing(self, flags):
        self.leftSwing = flags & 1 != 0
        self.rightSwing = flags & 2 != 0

    def setOtherZoneIdAndDoId(self, zoneId, distributedObjectID):
        self.otherZoneId = zoneId
        self.otherDoId = distributedObjectID

    def setState(self, state, timestamp):
        self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])

    def setExitDoorState(self, state, timestamp):
        self.exitDoorFSM.request(state, [globalClockDelta.localElapsedTime(timestamp)])

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)
        self.doPostAnnounceGenerate()

    def doPostAnnounceGenerate(self):
        if self.doorType == DoorTypes.INT_STANDARD:
            self.bHasFlat = True
        else:
            self.bHasFlat = not self.findDoorNode('door*flat', True).isEmpty()
        self.hideDoorParts()
        self.setTriggerName()
        self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
        self.acceptOnce('clearOutToonInterior', self.doorTrigger)
        self.setupNametag()

    def getBuilding(self):
        if not self.__dict__.has_key('building'):
            if self.doorType == DoorTypes.INT_STANDARD:
                door = render.find('**/leftDoor;+s')
                self.building = door.getParent()
            elif self.doorType == DoorTypes.INT_HQ:
                door = render.find('**/door_0')
                self.building = door.getParent()
            elif self.doorType == DoorTypes.INT_KS:
                self.building = render.find('**/KartShop_Interior*')
            elif self.doorType == DoorTypes.EXT_STANDARD or self.doorType == DoorTypes.EXT_HQ or self.doorType == DoorTypes.EXT_KS:
                self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':*_landmark_*_DNARoot;+s')
                if self.building.isEmpty():
                    self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':animated_building_*_DNARoot;+s')
            elif self.doorType == DoorTypes.EXT_COGHQ or self.doorType == DoorTypes.INT_COGHQ:
                self.building = self.cr.playGame.hood.loader.geom
            else:
                self.notify.error('No such door type as ' + str(self.doorType))
        return self.building

    def getBuilding_wip(self):
        if not self.__dict__.has_key('building'):
            if self.__dict__.has_key('block'):
                self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':*_landmark_*_DNARoot;+s')
            else:
                self.building = self.cr.playGame.hood.loader.geom
                print '---------------- door is interior -------'
        return self.building

    def readyToExit(self):
        base.transitions.fadeScreen(1.0)
        self.sendUpdate('requestExit')

    def avatarEnterDoorTrack(self, avatar, duration):
        trackName = 'avatarEnterDoor-%d-%d' % (self.doId, avatar.doId)
        track = Parallel(name=trackName)
        otherNP = self.getDoorNodePath()
        if hasattr(avatar, 'stopSmooth'):
            avatar.stopSmooth()
        if avatar.doId == base.localAvatar.doId:
            track.append(LerpPosHprInterval(nodePath=camera, other=avatar, duration=duration, pos=Point3(0, -8, avatar.getHeight()), hpr=VBase3(0, 0, 0), blendType='easeInOut'))
        finalPos = avatar.getParent().getRelativePoint(otherNP, Point3(self.doorX, 2, ToontownGlobals.FloorOffset))
        moveHere = Sequence(self.getAnimStateInterval(avatar, 'walk'), LerpPosInterval(nodePath=avatar, duration=duration, pos=finalPos, blendType='easeIn'))
        track.append(moveHere)
        if avatar.doId == base.localAvatar.doId:
            track.append(Sequence(Wait(duration * 0.5), Func(base.transitions.irisOut, duration * 0.5), Wait(duration * 0.5), Func(avatar.b_setParent, ToontownGlobals.SPHidden)))
        track.delayDelete = DelayDelete.DelayDelete(avatar, 'avatarEnterDoorTrack')
        return track

    def avatarEnqueueTrack(self, avatar, duration):
        if hasattr(avatar, 'stopSmooth'):
            avatar.stopSmooth()
        back = -5.0 - 2.0 * len(self.avatarIDList)
        if back < -9.0:
            back = -9.0
        offset = Point3(self.doorX, back, ToontownGlobals.FloorOffset)
        otherNP = self.getDoorNodePath()
        walkLike = ActorInterval(avatar, 'walk', startTime=1, duration=duration, endTime=0.0001)
        standHere = Sequence(LerpPosHprInterval(nodePath=avatar, other=otherNP, duration=duration, pos=offset, hpr=VBase3(0, 0, 0), blendType='easeInOut'), self.getAnimStateInterval(avatar, 'neutral'))
        trackName = 'avatarEnqueueDoor-%d-%d' % (self.doId, avatar.doId)
        track = Parallel(walkLike, standHere, name=trackName)
        track.delayDelete = DelayDelete.DelayDelete(avatar, 'avatarEnqueueTrack')
        return track

    def getAnimStateInterval(self, avatar, animName):
        isSuit = isinstance(avatar, Suit.Suit)
        if isSuit:
            return Func(avatar.loop, animName, 0)
        else:
            return Func(avatar.setAnimState, animName)

    def isDoorHit(self):
        vec = base.localAvatar.getRelativeVector(self.currentDoorNp, self.currentDoorVec)
        netScale = self.currentDoorNp.getNetTransform().getScale()
        yToTest = vec.getY() / netScale[1]
        return yToTest < -0.5

    def enterDoor(self):
        if self.allowedToEnter():
            messenger.send('DistributedDoor_doorTrigger')
            self.sendUpdate('requestEnter')
        else:
            place = base.cr.playGame.getPlace()
            if place:
                place.fsm.request('stopped')
            self.dialog = TeaserPanel.TeaserPanel(pageName='otherHoods', doneFunc=self.handleOkTeaser)

    def handleOkTeaser(self):
        self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
        self.dialog.destroy()
        del self.dialog
        place = base.cr.playGame.getPlace()
        if place:
            place.fsm.request('walk')

    def allowedToEnter(self, zoneId = None):
        allowed = False
        if hasattr(base, 'ttAccess') and base.ttAccess:
            if zoneId:
                allowed = base.ttAccess.canAccess(zoneId)
            else:
                allowed = base.ttAccess.canAccess()
        return allowed

    def checkIsDoorHitTaskName(self):
        return 'checkIsDoorHit' + self.getTriggerName()

    def checkIsDoorHitTask(self, task):
        if self.isDoorHit():
            self.ignore(self.checkIsDoorHitTaskName())
            self.ignore(self.getExitTriggerEvent())
            self.enterDoor()
            return Task.done
        return Task.cont

    def cancelCheckIsDoorHitTask(self, args):
        taskMgr.remove(self.checkIsDoorHitTaskName())
        del self.currentDoorNp
        del self.currentDoorVec
        self.ignore(self.getExitTriggerEvent())
        self.accept(self.getEnterTriggerEvent(), self.doorTrigger)

    def doorTrigger(self, args = None):
        self.ignore(self.getEnterTriggerEvent())
        if args == None:
            self.enterDoor()
        else:
            self.currentDoorNp = NodePath(args.getIntoNodePath())
            self.currentDoorVec = Vec3(args.getSurfaceNormal(self.currentDoorNp))
            if self.isDoorHit():
                self.enterDoor()
            else:
                self.accept(self.getExitTriggerEvent(), self.cancelCheckIsDoorHitTask)
                taskMgr.add(self.checkIsDoorHitTask, self.checkIsDoorHitTaskName())
        return

    def avatarEnter(self, avatarID):
        avatar = self.cr.doId2do.get(avatarID, None)
        if avatar:
            avatar.setAnimState('neutral')
            track = self.avatarEnqueueTrack(avatar, 0.5)
            track.start()
            self.avatarTracks.append(track)
            self.avatarIDList.append(avatarID)
        return

    def rejectEnter(self, reason):
        message = FADoorCodes.reasonDict[reason]
        if message:
            self.__faRejectEnter(message)
        else:
            self.__basicRejectEnter()

    def __basicRejectEnter(self):
        self.accept(self.getEnterTriggerEvent(), self.doorTrigger)
        if self.cr.playGame.getPlace():
            self.cr.playGame.getPlace().setState('walk')

    def __faRejectEnter(self, message):
        self.rejectDialog = TTDialog.TTGlobalDialog(message=message, doneEvent='doorRejectAck', style=TTDialog.Acknowledge)
        self.rejectDialog.show()
        self.rejectDialog.delayDelete = DelayDelete.DelayDelete(self, '__faRejectEnter')
        event = 'clientCleanup'
        self.acceptOnce(event, self.__handleClientCleanup)
        base.cr.playGame.getPlace().setState('stopped')
        self.acceptOnce('doorRejectAck', self.__handleRejectAck)
        self.acceptOnce('stoppedAsleep', self.__handleFallAsleepDoor)

    def __handleClientCleanup(self):
        if hasattr(self, 'rejectDialog') and self.rejectDialog:
            self.rejectDialog.doneStatus = 'ok'
        self.__handleRejectAck()

    def __handleFallAsleepDoor(self):
        self.rejectDialog.doneStatus = 'ok'
        self.__handleRejectAck()

    def __handleRejectAck(self):
        self.ignore('doorRejectAck')
        self.ignore('stoppedAsleep')
        self.ignore('clientCleanup')
        doneStatus = self.rejectDialog.doneStatus
        if doneStatus != 'ok':
            self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
        self.__basicRejectEnter()
        self.rejectDialog.delayDelete.destroy()
        self.rejectDialog.cleanup()
        del self.rejectDialog

    def getDoorNodePath(self):
        if self.doorType == DoorTypes.INT_STANDARD:
            otherNP = render.find('**/door_origin')
        elif self.doorType == DoorTypes.EXT_STANDARD:
            if hasattr(self, 'tempDoorNodePath'):
                return self.tempDoorNodePath
            else:
                posHpr = self.cr.playGame.dnaStore.getDoorPosHprFromBlockNumber(self.block)
                otherNP = NodePath('doorOrigin')
                otherNP.setPos(posHpr.getPos())
                otherNP.setHpr(posHpr.getHpr())
                self.tempDoorNodePath = otherNP
        elif self.specialDoorTypes.has_key(self.doorType):
            building = self.getBuilding()
            otherNP = building.find('**/door_origin_' + str(self.doorIndex))
        elif self.doorType == DoorTypes.INT_HQ:
            otherNP = render.find('**/door_origin_' + str(self.doorIndex))
        else:
            self.notify.error('No such door type as ' + str(self.doorType))
        return otherNP

    def avatarExitTrack(self, avatar, duration):
        if hasattr(avatar, 'stopSmooth'):
            avatar.stopSmooth()
        otherNP = self.getDoorNodePath()
        trackName = 'avatarExitDoor-%d-%d' % (self.doId, avatar.doId)
        track = Sequence(name=trackName)
        track.append(self.getAnimStateInterval(avatar, 'walk'))
        track.append(PosHprInterval(avatar, Point3(-self.doorX, 0, ToontownGlobals.FloorOffset), VBase3(179, 0, 0), other=otherNP))
        track.append(Func(avatar.setParent, ToontownGlobals.SPRender))
        if avatar.doId == base.localAvatar.doId:
            track.append(PosHprInterval(camera, VBase3(-self.doorX, 5, avatar.getHeight()), VBase3(180, 0, 0), other=otherNP))
        if avatar.doId == base.localAvatar.doId:
            finalPos = render.getRelativePoint(otherNP, Point3(-self.doorX, -6, ToontownGlobals.FloorOffset))
        else:
            finalPos = render.getRelativePoint(otherNP, Point3(-self.doorX, -3, ToontownGlobals.FloorOffset))
        track.append(LerpPosInterval(nodePath=avatar, duration=duration, pos=finalPos, blendType='easeInOut'))
        if avatar.doId == base.localAvatar.doId:
            track.append(Func(self.exitCompleted))
            track.append(Func(base.transitions.irisIn))
        if hasattr(avatar, 'startSmooth'):
            track.append(Func(avatar.startSmooth))
        track.delayDelete = DelayDelete.DelayDelete(avatar, 'DistributedDoor.avatarExitTrack')
        return track

    def exitCompleted(self):
        base.localAvatar.setAnimState('neutral')
        place = self.cr.playGame.getPlace()
        if place:
            place.setState('walk')
        base.localAvatar.d_setParent(ToontownGlobals.SPRender)

    def avatarExit(self, avatarID):
        if avatarID in self.avatarIDList:
            self.avatarIDList.remove(avatarID)
            if avatarID == base.localAvatar.doId:
                self.exitCompleted()
        else:
            self.avatarExitIDList.append(avatarID)

    def finishDoorTrack(self):
        if self.doorTrack:
            self.doorTrack.finish()
        self.doorTrack = None
        return

    def finishDoorExitTrack(self):
        if self.doorExitTrack:
            self.doorExitTrack.finish()
        self.doorExitTrack = None
        return

    def finishAllTracks(self):
        self.finishDoorTrack()
        self.finishDoorExitTrack()
        for t in self.avatarTracks:
            t.finish()
            DelayDelete.cleanupDelayDeletes(t)

        self.avatarTracks = []
        for t in self.avatarExitTracks:
            t.finish()
            DelayDelete.cleanupDelayDeletes(t)

        self.avatarExitTracks = []

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def getRequestStatus(self):
        zoneId = self.otherZoneId
        request = {'loader': ZoneUtil.getBranchLoaderName(zoneId),
         'where': ZoneUtil.getToonWhereName(zoneId),
         'how': 'doorIn',
         'hoodId': ZoneUtil.getHoodId(zoneId),
         'zoneId': zoneId,
         'shardId': None,
         'avId': -1,
         'allowRedirect': 0,
         'doorDoId': self.otherDoId}
        return request

    def enterClosing(self, ts):
        doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight')
        if doorFrameHoleRight.isEmpty():
            self.notify.warning('enterClosing(): did not find doorFrameHoleRight')
            return
        rightDoor = self.findDoorNode('rightDoor')
        if rightDoor.isEmpty():
            self.notify.warning('enterClosing(): did not find rightDoor')
            return
        otherNP = self.getDoorNodePath()
        trackName = 'doorClose-%d' % self.doId
        if self.rightSwing:
            h = 100
        else:
            h = -100
        self.finishDoorTrack()
        self.doorTrack = Sequence(LerpHprInterval(nodePath=rightDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleRight.hide), Func(self.hideIfHasFlat, rightDoor), SoundInterval(self.closeSfx, node=rightDoor), name=trackName)
        self.doorTrack.start(ts)
        if hasattr(self, 'done'):
            request = self.getRequestStatus()
            messenger.send('doorDoneEvent', [request])

    def exitClosing(self):
        pass

    def enterClosed(self, ts):
        pass

    def exitClosed(self):
        pass

    def enterOpening(self, ts):
        doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight')
        if doorFrameHoleRight.isEmpty():
            self.notify.warning('enterOpening(): did not find doorFrameHoleRight')
            return
        rightDoor = self.findDoorNode('rightDoor')
        if rightDoor.isEmpty():
            self.notify.warning('enterOpening(): did not find rightDoor')
            return
        otherNP = self.getDoorNodePath()
        trackName = 'doorOpen-%d' % self.doId
        if self.rightSwing:
            h = 100
        else:
            h = -100
        self.finishDoorTrack()
        self.doorTrack = Parallel(SoundInterval(self.openSfx, node=rightDoor), Sequence(HprInterval(rightDoor, VBase3(0, 0, 0), other=otherNP), Wait(0.4), Func(rightDoor.show), Func(doorFrameHoleRight.show), LerpHprInterval(nodePath=rightDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), other=otherNP, blendType='easeInOut')), name=trackName)
        self.doorTrack.start(ts)

    def exitOpening(self):
        pass

    def enterOpen(self, ts):
        for avatarID in self.avatarIDList:
            avatar = self.cr.doId2do.get(avatarID)
            if avatar:
                track = self.avatarEnterDoorTrack(avatar, 1.0)
                track.start(ts)
                self.avatarTracks.append(track)
            if avatarID == base.localAvatar.doId:
                self.done = 1

        self.avatarIDList = []

    def exitOpen(self):
        for track in self.avatarTracks:
            track.finish()
            DelayDelete.cleanupDelayDeletes(track)

        self.avatarTracks = []

    def exitDoorEnterOff(self):
        pass

    def exitDoorExitOff(self):
        pass

    def exitDoorEnterClosing(self, ts):
        doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft')
        if doorFrameHoleLeft.isEmpty():
            self.notify.warning('enterOpening(): did not find flatDoors')
            return
        if self.leftSwing:
            h = -100
        else:
            h = 100
        leftDoor = self.findDoorNode('leftDoor')
        if not leftDoor.isEmpty():
            otherNP = self.getDoorNodePath()
            trackName = 'doorExitTrack-%d' % self.doId
            self.finishDoorExitTrack()
            self.doorExitTrack = Sequence(LerpHprInterval(nodePath=leftDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleLeft.hide), Func(self.hideIfHasFlat, leftDoor), SoundInterval(self.closeSfx, node=leftDoor), name=trackName)
            self.doorExitTrack.start(ts)

    def exitDoorExitClosing(self):
        pass

    def exitDoorEnterClosed(self, ts):
        pass

    def exitDoorExitClosed(self):
        pass

    def exitDoorEnterOpening(self, ts):
        doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft')
        if doorFrameHoleLeft.isEmpty():
            self.notify.warning('enterOpening(): did not find flatDoors')
            return
        leftDoor = self.findDoorNode('leftDoor')
        if self.leftSwing:
            h = -100
        else:
            h = 100
        if not leftDoor.isEmpty():
            otherNP = self.getDoorNodePath()
            trackName = 'doorDoorExitTrack-%d' % self.doId
            self.finishDoorExitTrack()
            self.doorExitTrack = Parallel(SoundInterval(self.openSfx, node=leftDoor), Sequence(Func(leftDoor.show), Func(doorFrameHoleLeft.show), LerpHprInterval(nodePath=leftDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), other=otherNP, blendType='easeInOut')), name=trackName)
            self.doorExitTrack.start(ts)
        else:
            self.notify.warning('exitDoorEnterOpening(): did not find leftDoor')

    def exitDoorExitOpening(self):
        pass

    def exitDoorEnterOpen(self, ts):
        for avatarID in self.avatarExitIDList:
            avatar = self.cr.doId2do.get(avatarID)
            if avatar:
                track = self.avatarExitTrack(avatar, 0.2)
                track.start()
                self.avatarExitTracks.append(track)

        self.avatarExitIDList = []

    def exitDoorExitOpen(self):
        for track in self.avatarExitTracks:
            track.finish()
            DelayDelete.cleanupDelayDeletes(track)

        self.avatarExitTracks = []

    def findDoorNode(self, string, allowEmpty = False):
        building = self.getBuilding()
        if not building:
            self.notify.warning('getBuilding() returned None, avoiding crash, remark 896029')
            foundNode = None
        else:
            foundNode = building.find('**/door_' + str(self.doorIndex) + '/**/' + string + '*;+s+i')
            if foundNode.isEmpty():
                foundNode = building.find('**/' + string + '*;+s+i')
        if allowEmpty:
            return foundNode
        return foundNode

    def hideIfHasFlat(self, node):
        if self.bHasFlat:
            node.hide()
