Example #1
0
 def generate(self):
     DistributedPartyTeamActivity.generate(self)
     self._hopOffFinishedSV = StateVar(True)
     self._rewardFinishedSV = StateVar(True)
     self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady,
                                             self._hopOffFinishedSV,
                                             self._rewardFinishedSV)
 def generate(self):
     DistributedPartyTeamActivity.generate(self)
     self._hopOffFinishedSV = StateVar(True)
     self._rewardFinishedSV = StateVar(True)
     self._isWalkStateReadyFC = FunctionCall(
         self._testWalkStateReady, self._hopOffFinishedSV, self._rewardFinishedSV
     )
 def finalInit(self):
     from direct.fsm.StatePush import StateVar
     self._profileTasks = StateVar(False)
     self.setProfileTasks(
         ConfigVariableBool('profile-task-spikes', 0).getValue())
     self._profileFrames = StateVar(False)
     self.setProfileFrames(
         ConfigVariableBool('profile-frames', 0).getValue())
Example #4
0
 def finalInit(self):
     # This function should be called once during startup, after
     # most things are imported.
     from direct.fsm.StatePush import StateVar
     self._profileTasks = StateVar(False)
     self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
     self._profileFrames = StateVar(False)
     self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
Example #5
0
 def finalInit(self):
     # This function should be called once during startup, after
     # most things are imported.
     from direct.fsm.StatePush import StateVar
     self._profileTasks = StateVar(False)
     self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
     self._profileFrames = StateVar(False)
     self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
 def generate(self):
     DistributedSimpleShip.generate(self)
     self._repairSpotWoodPile = None
     self._repairSpotWoodPiles = {}
     self._repairSpotHole = None
     self._repairSpotHoleFixed = None
     self._repairSpotHoles = {}
     self._repairSpotIvals = {}
     self._wheelInUse = StateVar(False)
Example #7
0
 def __init__(self, linkedEmote=None):
     SCElement.__init__(self)
     self.setLinkedEmote(linkedEmote)
     scGui = loader.loadModel(SCMenu.GuiModelName)
     self.emotionIcon = scGui.find('**/emotionIcon')
     self.setDisabled(False)
     self._SCTerminal__numCharges = -1
     self._handleWhisperModeSV = StateVar(False)
     self._handleWhisperModeFC = None
 def __init__(self):
     self._state = DestructiveScratchPad(health=StateVar(0),
                                         speed=StateVar(0),
                                         armor=StateVar(0),
                                         modelClass=StateVar(0),
                                         pvpTeam=StateVar(0),
                                         siegeTeam=StateVar(0),
                                         fullHealth=StateVar(False),
                                         willBeFullHealth=StateVar(False),
                                         validShipClass=StateVar(False),
                                         hasTeam=StateVar(False))
     self._statePushes = []
Example #9
0
    def __init__(self, linkedEmote=None):
        SCElement.__init__(self)
        self.setLinkedEmote(linkedEmote)

        scGui = loader.loadModel(SCMenu.GuiModelName)
        self.emotionIcon = scGui.find('**/emotionIcon')
        self.setDisabled(False)
        self.__numCharges = -1

        # should we listen for whisper mode changes?
        self._handleWhisperModeSV = StateVar(False)
        # can't set this up until we're ready to have the handler func called
        self._handleWhisperModeFC = None
 def __init__(self, cr):
     DistributedObject.__init__(self, cr)
     base.cogdoGame = self
     cr.cogdoGame = self
     self._waitingStartLabel = DirectLabel(
         text=TTL.MinigameWaitingForOtherToons,
         text_fg=VBase4(1, 1, 1, 1),
         relief=None,
         pos=(-0.6, 0, -0.75),
         scale=0.075,
     )
     self._waitingStartLabel.hide()
     self.loadFSM = ClassicFSM.ClassicFSM(
         "DistCogdoGame.loaded",
         [
             State.State("NotLoaded", self.enterNotLoaded, self.exitNotLoaded, ["Loaded"]),
             State.State("Loaded", self.enterLoaded, self.exitLoaded, ["NotLoaded"]),
         ],
         "NotLoaded",
         "NotLoaded",
     )
     self.loadFSM.enterInitialState()
     self.fsm = ClassicFSM.ClassicFSM(
         "DistCogdoGame",
         [
             State.State("Visible", self.enterVisible, self.exitVisible, ["Intro"]),
             State.State("Intro", self.enterIntro, self.exitIntro, ["WaitServerStart"]),
             State.State("WaitServerStart", self.enterWaitServerStart, self.exitWaitServerStart, ["Game"]),
             State.State("Game", self.enterGame, self.exitGame, ["Finish"]),
             State.State("Finish", self.enterFinish, self.exitFinish, ["Off"]),
             State.State("Off", self.enterOff, self.exitOff, ["Visible"]),
         ],
         "Off",
         "Off",
     )
     self.fsm.enterInitialState()
     self.difficultyOverride = None
     self.exteriorZoneOverride = None
     self._gotInterior = StateVar(False)
     self._toonsInEntranceElev = StateVar(False)
     self._wantStashElevator = StateVar(False)
     self._stashElevatorFC = FunctionCall(
         self._doStashElevator, self._toonsInEntranceElev, self._gotInterior, self._wantStashElevator
     )
     return
 def __init__(self, linkedEmote = None):
     SCElement.__init__(self)
     self.setLinkedEmote(linkedEmote)
     scGui = loader.loadModel(SCMenu.GuiModelName)
     self.emotionIcon = scGui.find('**/emotionIcon')
     self.setDisabled(False)
     self._SCTerminal__numCharges = -1
     self._handleWhisperModeSV = StateVar(False)
     self._handleWhisperModeFC = None
 def generate(self):
     DistributedSimpleShip.generate(self)
     self._repairSpotWoodPile = None
     self._repairSpotWoodPiles = { }
     self._repairSpotHole = None
     self._repairSpotHoleFixed = None
     self._repairSpotHoles = { }
     self._repairSpotIvals = { }
     self._wheelInUse = StateVar(False)
 def __init__(self, cr):
     DistributedObject.__init__(self, cr)
     base.cogdoGame = self
     cr.cogdoGame = self
     self._waitingStartLabel = DirectLabel(text=TTL.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075)
     self._waitingStartLabel.hide()
     self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGame.loaded', [State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded, ['Loaded']), State.State('Loaded', self.enterLoaded, self.exitLoaded, ['NotLoaded'])], 'NotLoaded', 'NotLoaded')
     self.loadFSM.enterInitialState()
     self.fsm = ClassicFSM.ClassicFSM('DistCogdoGame', [State.State('Visible', self.enterVisible, self.exitVisible, ['Intro']),
      State.State('Intro', self.enterIntro, self.exitIntro, ['WaitServerStart']),
      State.State('WaitServerStart', self.enterWaitServerStart, self.exitWaitServerStart, ['Game']),
      State.State('Game', self.enterGame, self.exitGame, ['Finish']),
      State.State('Finish', self.enterFinish, self.exitFinish, ['Off']),
      State.State('Off', self.enterOff, self.exitOff, ['Visible'])], 'Off', 'Off')
     self.fsm.enterInitialState()
     self.difficultyOverride = None
     self.exteriorZoneOverride = None
     self._gotInterior = StateVar(False)
     self._toonsInEntranceElev = StateVar(False)
     self._wantStashElevator = StateVar(False)
     self._stashElevatorFC = FunctionCall(self._doStashElevator, self._toonsInEntranceElev, self._gotInterior, self._wantStashElevator)
 def __init__(self, cr):
     DistributedObject.__init__(self, cr)
     base.cogdoGame = self
     self._waitingStartLabel = DirectLabel(text=TTL.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075)
     self._waitingStartLabel.hide()
     self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGame.loaded', [State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded, ['Loaded']), State.State('Loaded', self.enterLoaded, self.exitLoaded, ['NotLoaded'])], 'NotLoaded', 'NotLoaded')
     self.loadFSM.enterInitialState()
     self.fsm = ClassicFSM.ClassicFSM('DistCogdoGame', [State.State('Visible', self.enterVisible, self.exitVisible, ['Intro']),
      State.State('Intro', self.enterIntro, self.exitIntro, ['WaitServerStart']),
      State.State('WaitServerStart', self.enterWaitServerStart, self.exitWaitServerStart, ['Game']),
      State.State('Game', self.enterGame, self.exitGame, ['Finish']),
      State.State('Finish', self.enterFinish, self.exitFinish, ['Off']),
      State.State('Off', self.enterOff, self.exitOff, ['Visible'])], 'Off', 'Off')
     self.fsm.enterInitialState()
     self.difficultyOverride = None
     self.exteriorZoneOverride = None
     self._gotInterior = StateVar(False)
     self._toonsInEntranceElev = StateVar(False)
     self._wantStashElevator = StateVar(False)
     self._stashElevatorFC = FunctionCall(self._doStashElevator, self._toonsInEntranceElev, self._gotInterior, self._wantStashElevator)
     return
class DistCogdoGame(DistCogdoGameBase, DistributedObject):
    notify = directNotify.newCategory('DistCogdoGame')

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        base.cogdoGame = self
        cr.cogdoGame = self
        self._waitingStartLabel = DirectLabel(text=TTL.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075)
        self._waitingStartLabel.hide()
        self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGame.loaded', [State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded, ['Loaded']), State.State('Loaded', self.enterLoaded, self.exitLoaded, ['NotLoaded'])], 'NotLoaded', 'NotLoaded')
        self.loadFSM.enterInitialState()
        self.fsm = ClassicFSM.ClassicFSM('DistCogdoGame', [State.State('Visible', self.enterVisible, self.exitVisible, ['Intro']),
         State.State('Intro', self.enterIntro, self.exitIntro, ['WaitServerStart']),
         State.State('WaitServerStart', self.enterWaitServerStart, self.exitWaitServerStart, ['Game']),
         State.State('Game', self.enterGame, self.exitGame, ['Finish']),
         State.State('Finish', self.enterFinish, self.exitFinish, ['Off']),
         State.State('Off', self.enterOff, self.exitOff, ['Visible'])], 'Off', 'Off')
        self.fsm.enterInitialState()
        self.difficultyOverride = None
        self.exteriorZoneOverride = None
        self._gotInterior = StateVar(False)
        self._toonsInEntranceElev = StateVar(False)
        self._wantStashElevator = StateVar(False)
        self._stashElevatorFC = FunctionCall(self._doStashElevator, self._toonsInEntranceElev, self._gotInterior, self._wantStashElevator)

    def getTitle(self):
        pass

    def getInstructions(self):
        pass

    def setInteriorId(self, interiorId):
        self._interiorId = interiorId

    def setExteriorZone(self, exteriorZone):
        self.exteriorZone = exteriorZone

    def setDifficultyOverrides(self, difficultyOverride, exteriorZoneOverride):
        if difficultyOverride != CogdoGameConsts.NoDifficultyOverride:
            self.difficultyOverride = difficultyOverride / float(CogdoGameConsts.DifficultyOverrideMult)
        if exteriorZoneOverride != CogdoGameConsts.NoExteriorZoneOverride:
            self.exteriorZoneOverride = exteriorZoneOverride

    def getInterior(self):
        return self.cr.getDo(self._interiorId)

    def getEntranceElevator(self, callback):
        return self.getInterior().getEntranceElevator(callback)

    def getToonIds(self):
        interior = self.getInterior()
        if interior is not None:
            return interior.getToonIds()
        else:
            return []

    def getToon(self, toonId):
        if self.cr.doId2do.has_key(toonId):
            return self.cr.doId2do[toonId]
        else:
            return None

    def getNumPlayers(self):
        return len(self.getToonIds())

    def isSinglePlayer(self):
        if self.getNumPlayers() == 1:
            return 1
        else:
            return 0

    def announceGenerate(self):
        DistributedObject.announceGenerate(self)
        self.loadFSM.request('Loaded')
        self._requestInterior()
        self.notify.info('difficulty: %s, safezoneId: %s' % (self.getDifficulty(), self.getSafezoneId()))

    def _requestInterior(self):
        self.cr.relatedObjectMgr.requestObjects([self._interiorId], allCallback=self._handleGotInterior)

    def _handleGotInterior(self, objs):
        self._gotInterior.set(True)
        self.getEntranceElevator(self.placeEntranceElev)

    def stashEntranceElevator(self):
        self._wantStashElevator.set(True)

    def placeEntranceElev(self, elev):
        pass

    def _doStashElevator(self, toonsInEntranceElev, gotInterior, wantStashElevator):
        if gotInterior:
            interior = self.getInterior()
            if interior:
                if not toonsInEntranceElev and wantStashElevator:
                    interior.stashElevatorIn()
                else:
                    interior.stashElevatorIn(False)

    def disable(self):
        base.cogdoGame = None
        self.cr.cogdoGame = None
        self.fsm.requestFinalState()
        self.loadFSM.requestFinalState()
        self.fsm = None
        self.loadFSM = None
        DistributedObject.disable(self)

    def delete(self):
        self._stashElevatorFC.destroy()
        self._wantStashElevator.destroy()
        self._toonsInEntranceElev.destroy()
        self._gotInterior.destroy()
        self._waitingStartLabel.destroy()
        self._waitingStartLabel = None
        DistributedObject.delete(self)

    def getDifficulty(self):
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        if hasattr(base, 'cogdoGameDifficulty'):
            return float(base.cogdoGameDifficulty)
        return CogdoGameConsts.getDifficulty(self.getSafezoneId())

    def getSafezoneId(self):
        if self.exteriorZoneOverride is not None:
            return self.exteriorZoneOverride
        if hasattr(base, 'cogdoGameSafezoneId'):
            return CogdoGameConsts.getSafezoneId(base.cogdoGameSafezoneId)
        return CogdoGameConsts.getSafezoneId(self.exteriorZone)

    def enterNotLoaded(self):
        pass

    def exitNotLoaded(self):
        pass

    def enterLoaded(self):
        pass

    def exitLoaded(self):
        pass

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def setVisible(self):
        self.fsm.request('Visible')

    def setIntroStart(self):
        self.fsm.request('Intro')

    def enterVisible(self):
        self._toonsInEntranceElev.set(True)

    def exitVisible(self):
        pass

    def enterIntro(self, duration = MinigameGlobals.rulesDuration):
        base.cr.playGame.getPlace().fsm.request('Game')
        self._rulesDoneEvent = self.uniqueName('cogdoGameRulesDone')
        self.accept(self._rulesDoneEvent, self._handleRulesDone)
        self._rulesPanel = CogdoGameRulesPanel('CogdoGameRulesPanel', self.getTitle(), '', self._rulesDoneEvent, timeout=duration)
        self._rulesPanel.load()
        self._rulesPanel.enter()

    def exitIntro(self):
        self._toonsInEntranceElev.set(False)
        self.ignore(self._rulesDoneEvent)
        if self._rulesPanel:
            self._rulesPanel.exit()
            self._rulesPanel.unload()
            self._rulesPanel = None

    def _handleRulesDone(self):
        self.ignore(self._rulesDoneEvent)
        self._rulesPanel.exit()
        self._rulesPanel.unload()
        self._rulesPanel = None
        self.fsm.request('WaitServerStart')
        self.d_setAvatarReady()

    def d_setAvatarReady(self):
        self.sendUpdate('setAvatarReady', [])

    def enterWaitServerStart(self):
        numToons = 1
        interior = self.getInterior()
        if interior:
            numToons = len(interior.getToonIds())
        if numToons > 1:
            msg = TTL.MinigameWaitingForOtherPlayers
        else:
            msg = TTL.MinigamePleaseWait
        self._waitingStartLabel['text'] = msg
        self._waitingStartLabel.show()

    def exitWaitServerStart(self):
        self._waitingStartLabel.hide()

    def setGameStart(self, timestamp):
        self._startTime = globalClockDelta.networkToLocalTime(timestamp)
        self.fsm.request('Game')

    def getStartTime(self):
        return self._startTime

    def enterGame(self):
        if SCHELLGAMES_DEV:
            self.acceptOnce('escape', messenger.send, ['magicWord', ['~endMaze']])

    def exitGame(self):
        if SCHELLGAMES_DEV:
            self.ignore('escape')

    def setGameFinish(self, timestamp):
        self._finishTime = globalClockDelta.networkToLocalTime(timestamp)
        self.fsm.request('Finish')

    def getFinishTime(self):
        return self._finishTime

    def enterFinish(self):
        pass

    def exitFinish(self):
        pass

    def setToonSad(self, toonId):
        pass

    def setToonDisconnect(self, toonId):
        pass
 def _addAttrib(self, name, defaultVal, type):
     setattr(self, name, StateVar(defaultVal))
     setattr(self, getSetterName(name), Functor(self._setter, name))
     self._attribNames.append(name)
class DistCogdoGame(DistCogdoGameBase, DistributedObject):
    notify = directNotify.newCategory('DistCogdoGame')

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        base.cogdoGame = self
        cr.cogdoGame = self
        self._waitingStartLabel = DirectLabel(
            text=TTL.MinigameWaitingForOtherPlayers,
            text_fg=VBase4(1, 1, 1, 1),
            relief=None,
            pos=(-0.6, 0, -0.75),
            scale=0.075)
        self._waitingStartLabel.hide()
        self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGame.loaded', [
            State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded,
                        ['Loaded']),
            State.State('Loaded', self.enterLoaded, self.exitLoaded,
                        ['NotLoaded'])
        ], 'NotLoaded', 'NotLoaded')
        self.loadFSM.enterInitialState()
        self.fsm = ClassicFSM.ClassicFSM('DistCogdoGame', [
            State.State('Visible', self.enterVisible, self.exitVisible,
                        ['Intro']),
            State.State('Intro', self.enterIntro, self.exitIntro,
                        ['WaitServerStart']),
            State.State('WaitServerStart', self.enterWaitServerStart,
                        self.exitWaitServerStart, ['Game']),
            State.State('Game', self.enterGame, self.exitGame, ['Finish']),
            State.State('Finish', self.enterFinish, self.exitFinish, ['Off']),
            State.State('Off', self.enterOff, self.exitOff, ['Visible'])
        ], 'Off', 'Off')
        self.fsm.enterInitialState()
        self.difficultyOverride = None
        self.exteriorZoneOverride = None
        self._gotInterior = StateVar(False)
        self._toonsInEntranceElev = StateVar(False)
        self._wantStashElevator = StateVar(False)
        self._stashElevatorFC = FunctionCall(self._doStashElevator,
                                             self._toonsInEntranceElev,
                                             self._gotInterior,
                                             self._wantStashElevator)
        return

    def getTitle(self):
        pass

    def getInstructions(self):
        pass

    def setInteriorId(self, interiorId):
        self._interiorId = interiorId

    def setExteriorZone(self, exteriorZone):
        self.exteriorZone = exteriorZone

    def setDifficultyOverrides(self, difficultyOverride, exteriorZoneOverride):
        if difficultyOverride != CogdoGameConsts.NoDifficultyOverride:
            self.difficultyOverride = difficultyOverride / float(
                CogdoGameConsts.DifficultyOverrideMult)
        if exteriorZoneOverride != CogdoGameConsts.NoExteriorZoneOverride:
            self.exteriorZoneOverride = exteriorZoneOverride

    def getInterior(self):
        return self.cr.getDo(self._interiorId)

    def getEntranceElevator(self, callback):
        return self.getInterior().getEntranceElevator(callback)

    def getToonIds(self):
        interior = self.getInterior()
        if interior is not None:
            return interior.getToonIds()
        else:
            return []
        return

    def getToon(self, toonId):
        if self.cr.doId2do.has_key(toonId):
            return self.cr.doId2do[toonId]
        else:
            return None
        return None

    def getNumPlayers(self):
        return len(self.getToonIds())

    def isSinglePlayer(self):
        if self.getNumPlayers() == 1:
            return 1
        else:
            return 0

    def announceGenerate(self):
        DistributedObject.announceGenerate(self)
        self.loadFSM.request('Loaded')
        self._requestInterior()
        self.notify.info('difficulty: %s, safezoneId: %s' %
                         (self.getDifficulty(), self.getSafezoneId()))

    def _requestInterior(self):
        self.cr.relatedObjectMgr.requestObjects(
            [self._interiorId], allCallback=self._handleGotInterior)

    def _handleGotInterior(self, objs):
        self._gotInterior.set(True)
        self.getEntranceElevator(self.placeEntranceElev)

    def stashEntranceElevator(self):
        self._wantStashElevator.set(True)

    def placeEntranceElev(self, elev):
        pass

    def _doStashElevator(self, toonsInEntranceElev, gotInterior,
                         wantStashElevator):
        if gotInterior:
            interior = self.getInterior()
            if interior:
                if not toonsInEntranceElev and wantStashElevator:
                    interior.stashElevatorIn()
                else:
                    interior.stashElevatorIn(False)

    def disable(self):
        base.cogdoGame = None
        self.cr.cogdoGame = None
        self.fsm.requestFinalState()
        self.loadFSM.requestFinalState()
        self.fsm = None
        self.loadFSM = None
        DistributedObject.disable(self)
        return

    def delete(self):
        self._stashElevatorFC.destroy()
        self._wantStashElevator.destroy()
        self._toonsInEntranceElev.destroy()
        self._gotInterior.destroy()
        self._waitingStartLabel.destroy()
        self._waitingStartLabel = None
        DistributedObject.delete(self)
        return

    def getDifficulty(self):
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        if hasattr(base, 'cogdoGameDifficulty'):
            return float(base.cogdoGameDifficulty)
        return CogdoGameConsts.getDifficulty(self.getSafezoneId())

    def getSafezoneId(self):
        if self.exteriorZoneOverride is not None:
            return self.exteriorZoneOverride
        if hasattr(base, 'cogdoGameSafezoneId'):
            return CogdoGameConsts.getSafezoneId(base.cogdoGameSafezoneId)
        return CogdoGameConsts.getSafezoneId(self.exteriorZone)

    def enterNotLoaded(self):
        pass

    def exitNotLoaded(self):
        pass

    def enterLoaded(self):
        pass

    def exitLoaded(self):
        pass

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def setVisible(self):
        self.fsm.request('Visible')

    def setIntroStart(self):
        self.fsm.request('Intro')

    def enterVisible(self):
        self._toonsInEntranceElev.set(True)

    def exitVisible(self):
        pass

    def enterIntro(self, duration=MinigameGlobals.rulesDuration):
        base.cr.playGame.getPlace().fsm.request('Game')
        self._rulesDoneEvent = self.uniqueName('cogdoGameRulesDone')
        self.accept(self._rulesDoneEvent, self._handleRulesDone)
        self._rulesPanel = CogdoGameRulesPanel('CogdoGameRulesPanel',
                                               self.getTitle(),
                                               '',
                                               self._rulesDoneEvent,
                                               timeout=duration)
        self._rulesPanel.load()
        self._rulesPanel.enter()

    def exitIntro(self):
        self._toonsInEntranceElev.set(False)
        self.ignore(self._rulesDoneEvent)
        if self._rulesPanel:
            self._rulesPanel.exit()
            self._rulesPanel.unload()
            self._rulesPanel = None
        return

    def _handleRulesDone(self):
        self.ignore(self._rulesDoneEvent)
        self._rulesPanel.exit()
        self._rulesPanel.unload()
        self._rulesPanel = None
        self.fsm.request('WaitServerStart')
        self.d_setAvatarReady()
        return

    def d_setAvatarReady(self):
        self.sendUpdate('setAvatarReady', [])

    def enterWaitServerStart(self):
        numToons = 1
        interior = self.getInterior()
        if interior:
            numToons = len(interior.getToonIds())
        if numToons > 1:
            msg = TTL.MinigameWaitingForOtherPlayers
        else:
            msg = TTL.MinigamePleaseWait
        self._waitingStartLabel['text'] = msg
        self._waitingStartLabel.show()

    def exitWaitServerStart(self):
        self._waitingStartLabel.hide()

    def setGameStart(self, timestamp):
        self._startTime = globalClockDelta.networkToLocalTime(timestamp)
        self.fsm.request('Game')

    def getStartTime(self):
        return self._startTime

    #def enterGame(self):
    #if SCHELLGAMES_DEV:
    #self.acceptOnce('escape', messenger.send, ['magicWord', ['~endMaze']])

    def exitGame(self):
        if SCHELLGAMES_DEV:
            self.ignore('escape')

    def setGameFinish(self, timestamp):
        self._finishTime = globalClockDelta.networkToLocalTime(timestamp)
        self.fsm.request('Finish')

    def getFinishTime(self):
        return self._finishTime

    def enterFinish(self):
        pass

    def exitFinish(self):
        pass

    def setToonSad(self, toonId):
        pass

    def setToonDisconnect(self, toonId):
        pass
Example #18
0
class DistributedCogdoInterior(DistributedObject.DistributedObject):
    id = 0
    cageHeights = [11.36, 0.01]

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.toons = []
        self.activeIntervals = {}
        self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg')
        self.closeSfx = base.loadSfx(
            'phase_5/audio/sfx/elevator_door_close.ogg')
        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []
        self.distBldgDoId = None
        self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0)
        self.currentFloor = -1
        self.elevatorName = self.__uniqueName('elevator')
        self.floorModel = None
        self.elevatorOutOpen = 0
        self.BottomFloor_SuitPositions = [
            Point3(0, 15, 0),
            Point3(10, 20, 0),
            Point3(-7, 24, 0),
            Point3(-10, 0, 0)
        ]

        self.BottomFloor_SuitHs = [75, 170, -91, -44]

        self.Cubicle_SuitPositions = [
            Point3(0, 18, 0),
            Point3(10, 12, 0),
            Point3(-9, 11, 0),
            Point3(-3, 13, 0)
        ]

        self.Cubicle_SuitHs = [170, 56, -52, 10]

        self.BossOffice_SuitPositions = [
            Point3(0, 15, 0),
            Point3(10, 20, 0),
            Point3(-10, 6, 0),
            Point3(-17, 30, 0)
        ]

        self.BossOffice_SuitHs = [170, 120, 12, 38]

        self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 1)
        self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom()
        self.brResults = [[], []]
        self.barrelRoomIntroTrack = None
        self.penthouseOutroTrack = None
        self.penthouseOutroChatDoneTrack = None
        self.penthouseIntroTrack = None
        self.waitMusic = base.loadMusic(
            'phase_7/audio/bgm/encntr_toon_winning_indoor.ogg')
        self.elevatorMusic = base.loadMusic(
            'phase_7/audio/bgm/tt_elevator.ogg')
        self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInterior', [
            State.State('WaitForAllToonsInside',
                        self.enterWaitForAllToonsInside,
                        self.exitWaitForAllToonsInside, ['Elevator']),
            State.State('Elevator', self.enterElevator, self.exitElevator,
                        ['Game', 'BattleIntro', 'BarrelRoomIntro']),
            State.State('Game', self.enterGame, self.exitGame, [
                'Resting', 'Failed', 'BattleIntro', 'BarrelRoomIntro',
                'Elevator'
            ]),
            State.State('BarrelRoomIntro', self.enterBarrelRoomIntro,
                        self.exitBarrelRoomIntro, ['CollectBarrels', 'Off']),
            State.State('CollectBarrels', self.enterCollectBarrels,
                        self.exitCollectBarrels, ['BarrelRoomReward', 'Off']),
            State.State('BarrelRoomReward', self.enterBarrelRoomReward,
                        self.exitBarrelRoomReward,
                        ['Battle', 'ReservesJoining', 'BattleIntro', 'Off']),
            State.State('BattleIntro', self.enterBattleIntro,
                        self.exitBattleIntro,
                        ['Battle', 'ReservesJoining', 'Off']),
            State.State('Battle', self.enterBattle, self.exitBattle,
                        ['Resting', 'Reward', 'ReservesJoining']),
            State.State('ReservesJoining', self.enterReservesJoining,
                        self.exitReservesJoining, ['Battle']),
            State.State('Resting', self.enterResting, self.exitResting,
                        ['Elevator']),
            State.State('Reward', self.enterReward, self.exitReward, ['Off']),
            State.State('Failed', self.enterFailed, self.exitFailed, ['Off']),
            State.State('Off', self.enterOff, self.exitOff,
                        ['Elevator', 'WaitForAllToonsInside', 'Battle'])
        ], 'Off', 'Off')

        self.fsm.enterInitialState()
        self._haveEntranceElevator = StateVar(False)
        self._stashEntranceElevator = StateVar(False)
        self._stashEntranceElevatorFC = FunctionCall(
            self._doStashEntranceElevator, self._haveEntranceElevator,
            self._stashEntranceElevator)
        self._entranceElevCallbacks = []
        self._doEntranceElevCallbacksFC = FunctionCall(
            self._doEntranceElevCallbacks, self._haveEntranceElevator)
        self.cage = None
        self.shopOwnerNpcId = None
        self.shopOwnerNpc = None
        self._movie = None
        self.SOSToonName = None
        self.FOType = None

    def setShopOwnerNpcId(self, npcId):
        self.shopOwnerNpcId = npcId

    def setSOSNpcId(self, npcId):
        self.SOSToonName = NPCToons.getNPCName(npcId)

    def setFOType(self, typeId):
        self.FOType = chr(typeId)

    def getFOType(self):
        return self.FOType

    def __uniqueName(self, name):
        DistributedCogdoInterior.id += 1
        return name + '%d' % DistributedCogdoInterior.id

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        self.announceGenerateName = self.uniqueName('generate')
        self.accept(self.announceGenerateName, self.handleAnnounceGenerate)
        self.elevatorModelIn = loader.loadModel(
            'phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
        self.leftDoorIn = self.elevatorModelIn.find('**/left_door')
        self.rightDoorIn = self.elevatorModelIn.find('**/right_door')
        self.elevatorModelOut = loader.loadModel(
            'phase_5/models/cogdominium/tt_m_ara_csa_elevator')
        self.leftDoorOut = self.elevatorModelOut.find('**/left_door')
        self.rightDoorOut = self.elevatorModelOut.find('**/right_door')

    def __makeShopOwnerNpc(self):
        if self.shopOwnerNpc:
            return

        self.shopOwnerNpc = NPCToons.createLocalNPC(self.shopOwnerNpcId)
        if not self.shopOwnerNpc:
            self.notify.warning(
                'No shopkeeper in this cogdominium, using FunnyFarm Sellbot FO NPCToons'
            )
            random.seed(self.doId)
            shopkeeper = random.randint(7001, 7009)
            self.shopOwnerNpc = NPCToons.createLocalNPC(shopkeeper)

        self.shopOwnerNpc.addActive()
        self.shopOwnerNpc.reparentTo(self.cage)
        self.shopOwnerNpc.setPosHpr(0, -2, 0, 180, 0, 0)
        self.shopOwnerNpc.loop('neutral')

    def setElevatorLights(self, elevatorModel):
        npc = elevatorModel.findAllMatches('**/floor_light_?;+s')
        for i in xrange(npc.getNumPaths()):
            np = npc.getPath(i)
            np.setDepthOffset(120)
            floor = int(np.getName()[-1:]) - 1
            if floor == self.currentFloor:
                np.setColor(LIGHT_ON_COLOR)
            elif floor < self.layout.getNumGameFloors() + (
                    1 if self.FOType != "s" else 0):
                if self.isBossFloor(self.currentFloor):
                    np.setColor(LIGHT_ON_COLOR)
                else:
                    np.setColor(LIGHT_OFF_COLOR)
            else:
                np.hide()

    def startAlertElevatorLightIval(self, elevatorModel):
        light = elevatorModel.find('**/floor_light_%s' %
                                   (self.currentFloor + 1))
        track = Sequence(Func(light.setColor, Vec4(1.0, 0.6, 0.6, 1.0)),
                         Wait(0.9), Func(light.setColor, LIGHT_ON_COLOR),
                         Wait(0.9))
        self.activeIntervals['alertElevatorLight'] = track
        track.loop()

    def stopAlertElevatorLightIval(self, elevatorModel):
        self.__finishInterval('alertElevatorLight')
        self.setElevatorLights(elevatorModel)

    def handleAnnounceGenerate(self, obj):
        self.ignore(self.announceGenerateName)
        self.cageDoorSfx = loader.loadSfx(
            'phase_5/audio/sfx/CHQ_SOS_cage_door.ogg')
        self.cageLowerSfx = loader.loadSfx(
            'phase_5/audio/sfx/CHQ_SOS_cage_lower.ogg')
        self.sendUpdate('setAvatarJoined', [])

    def disable(self):
        self.fsm.requestFinalState()
        self.__cleanupIntervals()
        self.ignoreAll()
        self.__cleanup()
        self.__cleanupShopOwnerNpc()
        self.__cleanupPenthouseIntro()
        DistributedObject.DistributedObject.disable(self)

    def __cleanupShopOwnerNpc(self):
        if self.shopOwnerNpc:
            self.shopOwnerNpc.removeActive()
            self.shopOwnerNpc.delete()
            self.shopOwnerNpc = None

    def __cleanupPenthouseIntro(self):
        if hasattr(self, '_movie') and self._movie:
            self._movie.unload()
            self._movie = None

    def delete(self):
        self._stashEntranceElevatorFC.destroy()
        self._doEntranceElevCallbacksFC.destroy()
        self._haveEntranceElevator.destroy()
        self._stashEntranceElevator.destroy()
        self._entranceElevCallbacks = None
        del self.waitMusic
        del self.elevatorMusic
        del self.openSfx
        del self.closeSfx
        del self.fsm
        base.localAvatar.inventory.setBattleCreditMultiplier(1)
        DistributedObject.DistributedObject.delete(self)

    def isBossFloor(self, floorNum):
        if not self.layout.hasBossBattle():
            return False

        return (self.layout.getBossBattleFloor() + 0) == floorNum

    def __cleanup(self):
        self.toons = []
        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []
        if self.elevatorModelIn != None:
            self.elevatorModelIn.removeNode()

        if self.elevatorModelOut != None:
            self.elevatorModelOut.removeNode()

        if self.floorModel != None:
            self.floorModel.removeNode()

        if self.cage != None:
            self.cage = None

        if self.barrelRoom != None:
            self.barrelRoom.destroy()
            self.barrelRoom = None

        self.leftDoorIn = None
        self.rightDoorIn = None
        self.leftDoorOut = None
        self.rightDoorOut = None

    def __addToon(self, toon):
        self.accept(toon.uniqueName('disable'),
                    self.__handleUnexpectedExit,
                    extraArgs=[toon])

    def __handleUnexpectedExit(self, toon):
        self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId)
        self.__removeToon(toon, unexpected=1)

    def __removeToon(self, toon, unexpected=0):
        if self.toons.count(toon) == 1:
            self.toons.remove(toon)
        self.ignore(toon.uniqueName('disable'))

    def __finishInterval(self, name):
        if name in self.activeIntervals:
            interval = self.activeIntervals[name]
            if interval.isPlaying():
                interval.finish()

    def __cleanupIntervals(self):
        for interval in self.activeIntervals.values():
            interval.finish()

        self.activeIntervals = {}

    def __closeInElevator(self):
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)

    def getZoneId(self):
        return self.zoneId

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def getExtZoneId(self):
        return self.extZoneId

    def setExtZoneId(self, extZoneId):
        self.extZoneId = extZoneId

    def getDistBldgDoId(self):
        return self.distBldgDoId

    def setDistBldgDoId(self, distBldgDoId):
        self.distBldgDoId = distBldgDoId

    def setNumFloors(self, numFloors):
        self.layout = CogdoLayout(numFloors)

    def getToonIds(self):
        toonIds = []
        for toon in self.toons:
            toonIds.append(toon.doId)

        return toonIds

    def setToons(self, toonIds, hack):
        self.toonIds = toonIds
        oldtoons = self.toons
        self.toons = []
        for toonId in toonIds:
            if toonId != 0:
                if toonId in self.cr.doId2do:
                    toon = self.cr.doId2do[toonId]
                    toon.stopSmooth()
                    self.toons.append(toon)
                    if not oldtoons.count(toon):
                        self.__addToon(toon)
                else:
                    self.notify.warning('setToons() - no toon: %d' % toonId)

        for toon in oldtoons:
            if not self.toons.count(toon):
                self.__removeToon(toon)

    def setSuits(self, suitIds, reserveIds, values):
        oldsuits = self.suits
        self.suits = []
        self.joiningReserves = []
        for suitId in suitIds:
            if suitId in self.cr.doId2do:
                suit = self.cr.doId2do[suitId]
                self.suits.append(suit)
                suit.fsm.request('Battle')
                suit.buildingSuit = 1
                suit.reparentTo(render)
                if not oldsuits.count(suit):
                    self.joiningReserves.append(suit)

                if 'Elevator' in repr(self.fsm):
                    pos, h = BattleBase.BattleBase.suitPoints[
                        len(suitIds) - 1][suitIds.index(suitId)]
                    suit.setPos(pos)
                    suit.setH(h)
            else:
                self.notify.warning('setSuits() - no suit: %d' % suitId)

        self.reserveSuits = []
        for index in xrange(len(reserveIds)):
            suitId = reserveIds[index]
            if suitId in self.cr.doId2do:
                suit = self.cr.doId2do[suitId]
                self.reserveSuits.append((suit, values[index]))
            else:
                self.notify.warning('setSuits() - no suit: %d' % suitId)

        if len(self.joiningReserves) > 0:
            self.fsm.request('ReservesJoining')

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

    def stashElevatorIn(self, stash=True):
        self._stashEntranceElevator.set(stash)

    def getEntranceElevator(self, callback):
        if self._haveEntranceElevator.get():
            callback(self.elevIn)
        else:
            self._entranceElevCallbacks.append(callback)

    def _doEntranceElevCallbacks(self, haveElev):
        if haveElev:
            while len(self._entranceElevCallbacks):
                cbs = self._entranceElevCallbacks[:]
                self._entranceElevCallbacks = []
                for callback in cbs:
                    callback(self.elevIn)

    def _doStashEntranceElevator(self, haveElev, doStash):
        if haveElev:
            if doStash:
                self.elevIn.stash()
            else:
                self.elevIn.unstash()

    def d_elevatorDone(self):
        self.sendUpdate('elevatorDone', [])

    def d_reserveJoinDone(self):
        self.sendUpdate('reserveJoinDone', [])

    def enterOff(self, ts=0):
        messenger.send('sellbotFieldOfficeChanged', [False])

    def exitOff(self):
        pass

    def enterWaitForAllToonsInside(self, ts=0):
        base.transitions.fadeOut(0)

    def exitWaitForAllToonsInside(self):
        pass

    def enterGame(self, ts=0):
        base.cr.forbidCheesyEffects(1)

    def exitGame(self):
        base.cr.forbidCheesyEffects(0)

    def __playElevator(self, ts, name, callback):
        SuitHs = []
        SuitPositions = []
        if self.floorModel:
            self.floorModel.removeNode()
            self.floorModel = None

        if self.cage:
            self.cage = None

        if self.currentFloor == 0:
            SuitHs = self.BottomFloor_SuitHs
            SuitPositions = self.BottomFloor_SuitPositions

        if self.isBossFloor(self.currentFloor):
            self.notify.info('__playElevator: currentFloor %s is boss' %
                             self.currentFloor)
            self.barrelRoom.unload()
            if self.FOType:
                penthouseName = SUITE_DICT.get(self.FOType)
                for i in range(4):
                    self.floorModel = loader.loadModel(
                        'phase_5/models/cogdominium/%s' % penthouseName)
            self.cage = self.floorModel.find('**/cage')
            pos = self.cage.getPos()
            self.cagePos = []
            for height in self.cageHeights:
                self.cagePos.append(Point3(pos[0], pos[1], height))
            self.cageDoor = self.floorModel.find('**/cage_door')
            self.cageDoor.wrtReparentTo(self.cage)
            if self.FOType:
                paintingModelName = PAINTING_DICT.get(self.FOType)
                for i in xrange(4):
                    paintingModel = loader.loadModel(
                        'phase_5/models/cogdominium/%s' % paintingModelName)
                    loc = self.floorModel.find('**/loc_painting%d' % (i + 1))
                    paintingModel.reparentTo(loc)
            if not self.floorModel.find('**/trophyCase').isEmpty():
                for i in range(4):
                    goldEmblem = loader.loadModel(
                        'phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy.bam'
                    )
                    loc = self.floorModel.find('**/gold_0%d' % (i + 1))
                    goldEmblem.reparentTo(loc)
                for i in range(20):
                    silverEmblem = loader.loadModel(
                        'phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam'
                    )
                    loc = self.floorModel.find('**/silver_0%d' % (i + 1))
                    silverEmblem.reparentTo(loc)

            SuitHs = self.BossOffice_SuitHs
            SuitPositions = self.BossOffice_SuitPositions
            self.__makeShopOwnerNpc()
        else:
            if self._wantBarrelRoom:
                self.barrelRoom.load()
                self.barrelRoom.hide()
            SuitHs = self.Cubicle_SuitHs
            SuitPositions = self.Cubicle_SuitPositions

        if self.floorModel:
            self.floorModel.reparentTo(render)
            if self.isBossFloor(self.currentFloor):
                self.notify.info('Load boss_suit_office')
                elevIn = self.floorModel.find(
                    CogdoGameConsts.PenthouseElevatorInPath).copyTo(render)
                elevOut = self.floorModel.find(
                    CogdoGameConsts.PenthouseElevatorOutPath)
                frame = self.elevatorModelOut.find('**/frame')
                if not frame.isEmpty():
                    frame.hide()
                frame = self.elevatorModelIn.find('**/frame')
                if not frame.isEmpty():
                    frame.hide()
                self.elevatorModelOut.reparentTo(elevOut)
                self.elevatorModelOut.setY(0)
            else:
                elevIn = self.floorModel.find('**/elevator-in')
                elevOut = self.floorModel.find('**/elevator-out')
        elif self._wantBarrelRoom and self.barrelRoom.isLoaded(
        ) and self.currentFloor == 2 and self.FOType == 'l':
            elevIn = self.barrelRoom.model.find(
                CogdoBarrelRoomConsts.BarrelRoomElevatorInPath)
            elevOut = self.barrelRoom.model.find(
                CogdoBarrelRoomConsts.BarrelRoomElevatorOutPath)
            y = elevOut.getY(render)
            elevOut = elevOut.copyTo(render)
            elevOut.setY(render, y - 0.75)
        else:
            floorModel = loader.loadModel(
                'phase_7/models/modules/boss_suit_office')
            elevIn = floorModel.find('**/elevator-in').copyTo(render)
            elevOut = floorModel.find('**/elevator-out').copyTo(render)
            floorModel.removeNode()

        self.elevIn = elevIn
        self.elevOut = elevOut
        self._haveEntranceElevator.set(True)
        for index in range(len(self.suits)):
            if not self.suits[index].isEmpty():
                self.suits[index].setPos(SuitPositions[index])
                if len(self.suits) > 2:
                    self.suits[index].setH(SuitHs[index])
                else:
                    self.suits[index].setH(170)
                self.suits[index].loop('neutral')

        for toon in self.toons:
            toon.reparentTo(self.elevatorModelIn)
            index = self.toonIds.index(toon.doId)
            toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1],
                        ElevatorPoints[index][2])
            toon.setHpr(180, 0, 0)
            toon.loop('neutral')

        self.elevatorModelIn.reparentTo(elevIn)
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)
        camera.reparentTo(self.elevatorModelIn)
        camera.setH(180)
        camera.setP(0)
        camera.setPos(0, 14, 4)
        base.playMusic(self.elevatorMusic, looping=1, volume=0.8)
        track = Sequence(
            Func(base.transitions.noTransitions),
            ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL),
            ElevatorUtils.getOpenInterval(self,
                                          self.leftDoorIn,
                                          self.rightDoorIn,
                                          self.openSfx,
                                          None,
                                          type=ELEVATOR_NORMAL),
            Func(camera.wrtReparentTo, render))
        for toon in self.toons:
            track.append(Func(toon.wrtReparentTo, render))

        track.append(Func(callback))
        track.start(ts)
        self.activeIntervals[name] = track

    def enterElevator(self, ts=0):
        if not self._CogdoGameRepeat:
            self.currentFloor += 1

        self.cr.playGame.getPlace().currentFloor = self.currentFloor
        self.setElevatorLights(self.elevatorModelIn)
        self.setElevatorLights(self.elevatorModelOut)
        if not self.isBossFloor(self.currentFloor):
            self.elevatorModelOut.detachNode()
            messenger.send('sellbotFieldOfficeChanged', [True])
        else:
            if self.FOType == 's':
                self._movie = CogdoElevatorMovie()
                self._movie.load()
                self._movie.play()

        self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone)
        mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
        base.localAvatar.inventory.setBattleCreditMultiplier(mult)

    def __handleElevatorDone(self):
        self.d_elevatorDone()

    def exitElevator(self):
        self.elevatorMusic.stop()
        if self._movie:
            self._movie.end()
            self.__cleanupPenthouseIntro()

        self.__finishInterval(self.elevatorName)

    def __setupBarrelRoom(self):
        self.currentFloor += 1
        base.transitions.irisOut(0.0)
        self.elevatorModelOut.setY(-12)
        self.elevatorModelIn.reparentTo(
            self.barrelRoom.model.find(
                CogdoBarrelRoomConsts.BarrelRoomElevatorInPath))
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)
        self._showExitElevator()
        self.barrelRoom.show()
        self.barrelRoom.placeToonsAtEntrance(self.toons)
        self.setElevatorLights(self.elevatorModelOut)

    def barrelRoomIntroDone(self):
        self.sendUpdate('toonBarrelRoomIntroDone', [])

    def enterBarrelRoomIntro(self, ts=0):
        if not self.isBossFloor(self.currentFloor):
            if self._wantBarrelRoom:
                self.__setupBarrelRoom()
                self.barrelRoomIntroTrack, trackName = self.barrelRoom.getIntroInterval(
                )
                self.barrelRoomIntroDoneEvent = trackName
                self.accept(self.barrelRoomIntroDoneEvent,
                            self.barrelRoomIntroDone)
                self.activeIntervals[trackName] = self.barrelRoomIntroTrack
                self.barrelRoomIntroTrack.start(ts)
                self._movie = CogdoBarrelRoomIntro()
                self._movie.load()
                self._movie.play()
            else:
                self._showExitElevator()

    def exitBarrelRoomIntro(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            self.ignore(self.barrelRoomIntroDoneEvent)
            if self.barrelRoomIntroTrack:
                self.barrelRoomIntroTrack.finish()
                DelayDelete.cleanupDelayDeletes(self.barrelRoomIntroTrack)
                self.barrelRoomIntroTrack = None

    def __handleLocalToonLeftBarrelRoom(self):
        self.notify.info('Local toon teleported out of barrel room.')
        self.sendUpdate('toonLeftBarrelRoom', [])
        self.barrelRoom.deactivate()

    def enterCollectBarrels(self, ts=0):
        if not self.isBossFloor(self.currentFloor):
            if self._wantBarrelRoom:
                self.acceptOnce('localToonLeft',
                                self.__handleLocalToonLeftBarrelRoom)
                self.barrelRoom.activate()
                base.playMusic(self.waitMusic, looping=1, volume=0.7)
                base.localAvatar.questMap.stop()

    def exitCollectBarrels(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            self.ignore('localToonLeft')
            self.barrelRoom.deactivate()
            self.waitMusic.stop()

    def __brRewardDone(self, task=None):
        self.notify.info('Toon finished watching the barrel room reward.')
        self.sendUpdate('toonBarrelRoomRewardDone', [])
        self.fsm.request('Battle')

    def enterBarrelRoomReward(self, ts=0):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            base.cr.playGame.getPlace().fsm.request('stopped')
            self.startAlertElevatorLightIval(self.elevatorModelOut)
            track, trackName = self.barrelRoom.showRewardUi(
                callback=self.__brRewardDone)
            self.activeIntervals[trackName] = track
            track.start()
            self.barrelRoom.placeToonsNearBattle(self.toons)

    def exitBarrelRoomReward(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            base.cr.playGame.getPlace().fsm.request('walk')
            self.stopAlertElevatorLightIval(self.elevatorModelOut)
            self.barrelRoom.hideRewardUi()

    def enterBattleIntro(self, ts=0):
        self._movie = CogdoExecutiveSuiteIntro(self.shopOwnerNpc)
        self._movie.load()
        self._movie.play()

    def exitBattleIntro(self):
        self._movie.end()
        self.__cleanupPenthouseIntro()

    def __playCloseElevatorOut(self, name, delay=0):
        track = Sequence(
            Wait(delay + SUIT_LEAVE_ELEVATOR_TIME),
            Parallel(
                SoundInterval(self.closeSfx),
                LerpPosInterval(
                    self.leftDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL),
                    startPos=Point3(0, 0, 0),
                    blendType='easeOut'),
                LerpPosInterval(
                    self.rightDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL),
                    startPos=Point3(0, 0, 0),
                    blendType='easeOut')))
        track.start()
        self.activeIntervals[name] = track

    def enterBattle(self, ts=0):
        if self._wantBarrelRoom and self.elevatorOutOpen == 1:
            self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'),
                                        delay=2)
            camera.setPos(0, -15, 6)
            camera.headsUp(self.elevatorModelOut)

    def _showExitElevator(self):
        self.elevatorModelOut.reparentTo(self.elevOut)
        self.leftDoorOut.setPos(3.5, 0, 0)
        self.rightDoorOut.setPos(-3.5, 0, 0)
        if not self._wantBarrelRoom and self.elevatorOutOpen == 1:
            self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'))
            camera.setPos(0, -15, 6)
            camera.headsUp(self.elevatorModelOut)

    def exitBattle(self):
        if self.elevatorOutOpen == 1:
            self.__finishInterval(self.uniqueName('close-out-elevator'))
            self.elevatorOutOpen = 0

    def __playReservesJoining(self, ts, name, callback):
        index = 0
        for suit in self.joiningReserves:
            suit.reparentTo(render)
            suit.setPos(
                self.elevatorModelOut,
                Point3(ElevatorPoints[index][0], ElevatorPoints[index][1],
                       ElevatorPoints[index][2]))
            index += 1
            suit.setH(180)
            suit.loop('neutral')
        if len(self.suits) == len(self.joiningReserves):
            camSequence = Sequence(Func(camera.wrtReparentTo, localAvatar),
                                   Func(camera.setPos, Point3(0, 5, 5)),
                                   Func(camera.headsUp, self.elevatorModelOut))
        else:
            camSequence = Sequence(
                Func(camera.wrtReparentTo, self.elevatorModelOut),
                Func(camera.setPos, Point3(0, -8, 2)),
                Func(camera.setHpr, Vec3(0, 10, 0)))

        track = Sequence(
            camSequence,
            Parallel(
                SoundInterval(self.openSfx),
                LerpPosInterval(
                    self.leftDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    Point3(0, 0, 0),
                    startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL),
                    blendType='easeOut'),
                LerpPosInterval(
                    self.rightDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    Point3(0, 0, 0),
                    startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL),
                    blendType='easeOut')), Wait(SUIT_HOLD_ELEVATOR_TIME),
            Func(camera.wrtReparentTo, render), Func(callback))
        track.start(ts)
        self.activeIntervals[name] = track

    def enterReservesJoining(self, ts=0):
        self.__playReservesJoining(ts, self.uniqueName('reserves-joining'),
                                   self.__handleReserveJoinDone)
        return None

    def __handleReserveJoinDone(self):
        self.joiningReserves = []
        self.elevatorOutOpen = 1
        self.d_reserveJoinDone()

    def exitReservesJoining(self):
        self.__finishInterval(self.uniqueName('reserves-joining'))
        return None

    def enterResting(self, ts=0):
        self._showExitElevator()
        self._setAvPosFDC = FrameDelayedCall('setAvPos', self._setAvPosToExit)
        if self._wantBarrelRoom:
            self.barrelRoom.showBattleAreaLight(True)

        base.playMusic(self.waitMusic, looping=1, volume=0.7)
        self.__closeInElevator()
        self._haveEntranceElevator.set(False)
        self._stashEntranceElevator.set(False)

    def _setAvPosToExit(self):
        base.localAvatar.setPos(self.elevOut, 0, -22, 0)
        base.localAvatar.setHpr(self.elevOut, 0, 0, 0)
        base.cr.playGame.getPlace().fsm.request('walk')

    def exitResting(self):
        self._setAvPosFDC.destroy()
        self.waitMusic.stop()

    def enterReward(self, ts=0):
        if self.isBossFloor(self.currentFloor):
            self.penthouseOutroTrack = self.__outroPenthouse()
            self.penthouseOutroTrack.start(ts)
        else:
            self.exitCogdoBuilding()

    def exitReward(self):
        self.notify.debug('exitReward')
        if self.penthouseOutroTrack:
            self.penthouseOutroTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.penthouseOutroTrack)
            self.penthouseOutroTrack = None
            if not self.penthouseOutroChatDoneTrack:
                self.notify.debug(
                    'exitReward: instanting outroPenthouseChatDone track')
                self.__outroPenthouseChatDone()
            self.penthouseOutroChatDoneTrack.finish()
            self.penthouseOutroChatDoneTrack = None

    def enterFailed(self, ts=0):
        self.exitCogdoBuilding()

    def exitFailed(self):
        self.notify.debug('exitFailed()')
        self.exitCogdoBuilding()

    def exitCogdoBuilding(self):
        if base.localAvatar.hp < 0:
            return

        base.localAvatar.b_setParent(ToontownGlobals.SPHidden)
        request = {
            'loader': ZoneUtil.getBranchLoaderName(self.extZoneId),
            'where': ZoneUtil.getToonWhereName(self.extZoneId),
            'how': 'elevatorIn',
            'hoodId': ZoneUtil.getHoodId(self.extZoneId),
            'zoneId': self.extZoneId,
            'shardId': None,
            'avId': -1,
            'bldgDoId': self.distBldgDoId
        }

        messenger.send('DSIDoneEvent', [request])

    def displayBadges(self):
        numFloors = self.layout.getNumGameFloors()
        if numFloors > 5 or numFloors < 3:
            pass
        else:
            self.notify.warning('Invalid floor number for display badges.')

        for player in xrange(len(self.toons)):
            goldBadge = loader.loadModel(
                'phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy')
            goldBadge.setScale(1.2)
            goldNode = render.find('**/gold_0' + str(player + 1))
            goldBadge.reparentTo(goldNode)

            for floor in xrange(numFloors):
                silverBadge = loader.loadModel(
                    'phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam')
                silverBadge.setScale(1.2)
                silverNode = render.find('**/silver_0' + str(floor * 4 +
                                                             (player + 1)))
                silverBadge.reparentTo(silverNode)

    def __outroPenthouse(self):
        avatar = base.localAvatar
        trackName = '__outroPenthouse-%d' % avatar.doId
        track = Parallel(name=trackName)
        base.cr.playGame.getPlace().fsm.request('stopped')
        if self.FOType == "l":
            speech = TTLocalizer.CogdoLawbotExecutiveSuiteToonThankYou
        else:
            speech = TTLocalizer.CogdoExecutiveSuiteToonThankYou % self.SOSToonName

        track.append(
            Sequence(
                Func(camera.wrtReparentTo, localAvatar),
                Func(camera.setPos, 0, -9, 9),
                Func(camera.lookAt, Point3(5, 15, 0)),
                Parallel(
                    self.cage.posInterval(0.75,
                                          self.cagePos[1],
                                          blendType='easeOut'),
                    SoundInterval(self.cageLowerSfx, duration=0.5)),
                Parallel(
                    self.cageDoor.hprInterval(0.5,
                                              VBase3(0, 90, 0),
                                              blendType='easeOut'),
                    Sequence(SoundInterval(self.cageDoorSfx), duration=0)),
                Wait(0.25), Func(self.shopOwnerNpc.wrtReparentTo, render),
                Func(self.shopOwnerNpc.setScale, 1),
                Func(self.shopOwnerNpc.loop, 'walk'),
                Func(self.shopOwnerNpc.headsUp, Point3(0, 10, 0)),
                ParallelEndTogether(
                    self.shopOwnerNpc.posInterval(1.5, Point3(0, 10, 0)),
                    self.shopOwnerNpc.hprInterval(0.5,
                                                  VBase3(180, 0, 0),
                                                  blendType='easeInOut')),
                Func(self.shopOwnerNpc.setChatAbsolute,
                     TTLocalizer.CagedToonYippee, CFSpeech),
                ActorInterval(self.shopOwnerNpc, 'jump'),
                Func(self.shopOwnerNpc.loop, 'neutral'),
                Func(self.shopOwnerNpc.headsUp, localAvatar),
                Func(self.shopOwnerNpc.setLocalPageChat, speech, 0),
                Func(camera.lookAt, self.shopOwnerNpc, Point3(0, 0, 2))))
        self.activeIntervals[trackName] = track
        self.accept('doneChatPage', self.__outroPenthouseChatDone)
        return track

    def __outroPenthouseChatDone(self, elapsed=None):
        self.shopOwnerNpc.setChatAbsolute(
            TTLocalizer.CogdoExecutiveSuiteToonBye, CFSpeech)
        self.ignore('doneChatPage')
        track = Parallel(
            Sequence(ActorInterval(self.shopOwnerNpc, 'wave'),
                     Func(self.shopOwnerNpc.loop, 'neutral')),
            Sequence(Wait(2.0), Func(self.exitCogdoBuilding),
                     Func(base.camLens.setFov, settings['fieldofview'])))
        track.start()
        self.penthouseOutroChatDoneTrack = track
Example #19
0
class SCTerminal(SCElement):

    def __init__(self, linkedEmote = None):
        SCElement.__init__(self)
        self.setLinkedEmote(linkedEmote)
        scGui = loader.loadModel(SCMenu.GuiModelName)
        self.emotionIcon = scGui.find('**/emotionIcon')
        self.setDisabled(False)
        self.__numCharges = -1
        self._handleWhisperModeSV = StateVar(False)
        self._handleWhisperModeFC = None
        return

    def destroy(self):
        self._handleWhisperModeSV.set(False)
        if self._handleWhisperModeFC:
            self._handleWhisperModeFC.destroy()
        self._handleWhisperModeSV.destroy()
        SCElement.destroy(self)

    def privSetSettingsRef(self, settingsRef):
        SCElement.privSetSettingsRef(self, settingsRef)
        if self._handleWhisperModeFC is None:
            self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged, self._handleWhisperModeSV)
            self._handleWhisperModeFC.pushCurrentState()
        self._handleWhisperModeSV.set(self.settingsRef is not None and not self.isWhisperable())
        return

    def _handleWhisperModeSVChanged(self, handleWhisperMode):
        if handleWhisperMode:
            self._wmcListener = DirectObject()
            self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange)
        elif hasattr(self, '_wmcListener'):
            self._wmcListener.ignoreAll()
            del self._wmcListener
            self.invalidate()

    def _handleWhisperModeChange(self, whisperMode):
        self.invalidate()

    def handleSelect(self):
        messenger.send(self.getEventName(SCTerminalSelectedEvent))
        if self.hasLinkedEmote() and self.linkedEmoteEnabled():
            messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote])

    def isWhisperable(self):
        return True

    def getLinkedEmote(self):
        return self.linkedEmote

    def setLinkedEmote(self, linkedEmote):
        self.linkedEmote = linkedEmote
        self.invalidate()

    def hasLinkedEmote(self):
        return self.linkedEmote is not None

    def linkedEmoteEnabled(self):
        if Emote.globalEmote:
            return Emote.globalEmote.isEnabled(self.linkedEmote)

    def getCharges(self):
        return self.__numCharges

    def setCharges(self, nCharges):
        self.__numCharges = nCharges
        if nCharges is 0:
            self.setDisabled(True)

    def isDisabled(self):
        return self.__disabled or self.isWhispering() and not self.isWhisperable()

    def setDisabled(self, bDisabled):
        self.__disabled = bDisabled

    def onMouseClick(self, event):
        if not self.isDisabled():
            SCElement.onMouseClick(self, event)
            self.handleSelect()

    def getMinDimensions(self):
        width, height = SCElement.getMinDimensions(self)
        if self.hasLinkedEmote():
            width += 1.3
        return (width, height)

    def finalize(self, dbArgs = {}):
        if not self.isDirty():
            return
        args = {}
        if self.hasLinkedEmote():
            self.lastEmoteIconColor = self.getEmoteIconColor()
            self.emotionIcon.setColorScale(*self.lastEmoteIconColor)
            args.update({'image': self.emotionIcon,
             'image_pos': (self.width - 0.6, 0, -self.height * 0.5)})
        if self.isDisabled():
            args.update({'rolloverColor': (0, 0, 0, 0),
             'pressedColor': (0, 0, 0, 0),
             'rolloverSound': None,
             'clickSound': None,
             'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)})
        args.update(dbArgs)
        SCElement.finalize(self, dbArgs=args)
        return

    def getEmoteIconColor(self):
        if self.linkedEmoteEnabled() and not self.isWhispering():
            r, g, b = self.getColorScheme().getEmoteIconColor()
        else:
            r, g, b = self.getColorScheme().getEmoteIconDisabledColor()
        return (r,
         g,
         b,
         1)

    def updateEmoteIcon(self):
        if hasattr(self, 'button'):
            self.lastEmoteIconColor = self.getEmoteIconColor()
            for i in xrange(self.button['numStates']):
                self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor)

        else:
            self.invalidate()

    def enterVisible(self):
        SCElement.enterVisible(self)
        if hasattr(self, 'lastEmoteIconColor'):
            if self.getEmoteIconColor() != self.lastEmoteIconColor:
                self.invalidate()

        def handleWhisperModeChange(whisperMode, self = self):
            if self.hasLinkedEmote():
                if self.isVisible() and not self.isWhispering():
                    self.updateEmoteIcon()

        self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange)

        def handleEmoteEnableStateChange(self = self):
            if self.hasLinkedEmote():
                if self.isVisible() and not self.isWhispering():
                    self.updateEmoteIcon()

        if self.hasLinkedEmote():
            if Emote.globalEmote:
                self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange)

    def exitVisible(self):
        SCElement.exitVisible(self)
        self.ignore(self.getEventName(SCWhisperModeChangeEvent))
        if Emote.globalEmote:
            self.ignore(Emote.globalEmote.EmoteEnableStateChanged)

    def getDisplayText(self):
        if self.getCharges() is not -1:
            return self.text + ' (%s)' % self.getCharges()
        else:
            return self.text
Example #20
0
class DistributedPartyTugOfWarActivity(DistributedPartyTeamActivity):

    notify = directNotify.newCategory("DistributedPartyTugOfWarActivity")

    def __init__(self, cr):
        """
        cr: instance of ClientRepository
        """
        DistributedPartyTeamActivity.__init__(
            self,
            cr,
            PartyGlobals.ActivityIds.PartyTugOfWar,
            startDelay=PartyGlobals.TugOfWarStartDelay)
        assert (self.notify.debug("__init__"))

        # these are the indices of the active buttons
        self.buttons = [0, 1]

        # these variables are used for calculation how fast the player is pressing the keys
        self.arrowKeys = None
        self.keyTTL = []
        self.idealRate = 0.0
        self.keyRate = 0
        self.allOutMode = False
        self.rateMatchAward = 0.0  # bonus for consistently hitting the ideal rate

        self.toonIdsToStartPositions = {}  # initial positions of toons
        self.toonIdsToIsPullingFlags = {}  # whether or not a toon is pulling
        self.toonIdsToRightHands = {}  # used for setting up ropes
        self.fallenToons = []  # toons in the water
        self.fallenPositions = []
        self.unusedFallenPositionsIndices = [0, 1, 2, 3]
        self.toonIdsToAnimIntervals = {}
        self.tugRopes = []

    def generate(self):
        DistributedPartyTeamActivity.generate(self)
        assert (self.notify.debug("generate"))

        self._hopOffFinishedSV = StateVar(True)
        self._rewardFinishedSV = StateVar(True)
        self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady,
                                                self._hopOffFinishedSV,
                                                self._rewardFinishedSV)

    def delete(self):
        self._isWalkStateReadyFC.destroy()
        self._hopOffFinishedSV.destroy()
        self._rewardFinishedSV.destroy()

        DistributedPartyTeamActivity.delete(self)

    def handleToonJoined(self, toonId):
        DistributedPartyTeamActivity.handleToonJoined(self, toonId)

        self.toonIdsToAnimIntervals[toonId] = None

        if toonId == base.localAvatar.doId:
            base.cr.playGame.getPlace().fsm.request("activity")

            # set camera to a 3rd person view of play area
            camera.wrtReparentTo(self.root)
            self.cameraMoveIval = LerpPosHprInterval(
                camera,
                1.5,
                PartyGlobals.TugOfWarCameraPos,
                PartyGlobals.TugOfWarCameraInitialHpr,
                other=self.root,
            )
            self.cameraMoveIval.start()

            self.localToonPosIndex = self.getIndex(base.localAvatar.doId,
                                                   self.localToonTeam)
            self.notify.debug("posIndex: %d" % self.localToonPosIndex)

            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][
                self.localToonPosIndex]
            # prevent toons from clipping through the dock by warping them to dock height
            if toon.getZ(self.root) < PartyGlobals.TugOfWarToonPositionZ:
                toon.setZ(self.root, PartyGlobals.TugOfWarToonPositionZ)
            targetH = fitDestAngle2Src(
                toon.getH(self.root),
                PartyGlobals.TugOfWarHeadings[self.localToonTeam])
            travelVector = targetPos - toon.getPos(self.root)
            duration = travelVector.length() / 5.0
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            self.toonIdsToAnimIntervals[toonId] = Sequence(
                Func(toon.startPosHprBroadcast, 0.1),
                Func(toon.b_setAnimState, "run"),
                LerpPosHprInterval(toon,
                                   duration,
                                   targetPos,
                                   VBase3(targetH, 0.0, 0.0),
                                   other=self.root),
                Func(toon.stopPosHprBroadcast),
                Func(toon.b_setAnimState, "neutral"),
            )
            self.toonIdsToAnimIntervals[toonId].start()

    def handleToonExited(self, toonId):
        DistributedPartyTeamActivity.handleToonExited(self, toonId)

        # clean up local toon stuff
        if toonId == base.localAvatar.doId:
            self.cameraMoveIval.pause()

            # make toon jump off the dock if needed
            if toonId not in self.fallenToons:
                # finish any existing interval for that toon
                if toonId in self.toonIdsToAnimIntervals and \
                   self.toonIdsToAnimIntervals[toonId] is not None:
                    self.toonIdsToAnimIntervals[toonId].finish()
                toon = self.getAvatar(toonId)
                # clamp targetHeading to minimize spin
                targetH = fitDestAngle2Src(toon.getH(self.root), 180.0)
                targetPos = self.hopOffPositions[self.getTeam(toonId)][
                    self.getIndex(toonId, self.getTeam(toonId))]
                hopOffAnim = Sequence(
                    Func(toon.startPosHprBroadcast, 0.1),
                    toon.hprInterval(0.2,
                                     VBase3(targetH, 0.0, 0.0),
                                     other=self.root),
                    Func(toon.b_setAnimState, "jump", 1.0),
                    Wait(0.4),
                    PartyUtils.arcPosInterval(0.75, toon, targetPos, 5.0,
                                              self.root),
                    Func(toon.stopPosHprBroadcast),
                    # make sure toon ends up on the ground on remote clients
                    Func(toon.sendCurrentPosition),
                    Func(self.hopOffFinished, toonId),
                )
                self.toonIdsToAnimIntervals[toonId] = hopOffAnim
                self._hopOffFinishedSV.set(False)
                self.toonIdsToAnimIntervals[toonId].start()
            # local toons not on the dock are put back into the walk state
            else:
                self._hopOffFinishedSV.set(True)
                del self.toonIdsToAnimIntervals[toonId]

    def handleRewardDone(self):
        # don't call down, it puts the toon in a bad state because it interferes with
        # the 'hopOffAnim'
        self._rewardFinishedSV.set(True)

    def _testWalkStateReady(self, hoppedOff, rewardFinished):
        assert (self.notify.debug("_testWalkStateReady %d %d" %
                                  (hoppedOff, rewardFinished)))

        if hoppedOff and rewardFinished:
            DistributedPartyTeamActivity.handleRewardDone(self)

    def hopOffFinished(self, toonId):
        assert (self.notify.debug("hopOffFinished( toonId=%d )" % toonId))

        if hasattr(self,"toonIdsToAnimIntervals") and \
           toonId in self.toonIdsToAnimIntervals:
            del self.toonIdsToAnimIntervals[toonId]  # clean up anim dictionary

        if toonId == base.localAvatar.doId:
            if hasattr(self._hopOffFinishedSV, '_value'):
                self._hopOffFinishedSV.set(True)

    def handleToonShifted(self, toonId):
        assert (self.notify.debug("handleToonShifted( toonId=%d )" % toonId))

        if toonId == base.localAvatar.doId:
            # update local toon's position on the dock if they got shifted
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId,
                                                   self.localToonTeam)
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][
                self.localToonPosIndex]
            self.toonIdsToAnimIntervals[toonId] = Sequence(
                Wait(0.6),  # give leaving toon time to jump off dock
                Func(toon.startPosHprBroadcast, 0.1),
                Func(toon.b_setAnimState, "run"),
                toon.posInterval(0.5, targetPos, other=self.root),
                Func(toon.stopPosHprBroadcast),
                Func(toon.b_setAnimState, "neutral"),
            )
            self.toonIdsToAnimIntervals[toonId].start()

    def handleToonDisabled(self, toonId):
        """
        A toon dropped unexpectedly from the game. Handle it!
        """
        assert (self.notify.debug("handleToonDisabled( toonId:%d )" % toonId))

        if self.toonIdsToAnimIntervals.has_key(toonId):
            if self.toonIdsToAnimIntervals[toonId]:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            else:
                self.notify.debug("self.toonIdsToAnimIntervals[%d] is none" %
                                  toonId)

    def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds):
        """Overrides DistributedPartyActivity's setToonsPlaying"""
        DistributedPartyTeamActivity.setToonsPlaying(self, leftTeamToonIds,
                                                     rightTeamToonIds)

        # update table of right hands
        self.toonIdsToRightHands.clear()
        for toonId in self.getToonIdsAsList():
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToRightHands[toonId] = toon.getRightHands()[0]

    def load(self):
        """
        Load the necessary assets
        """
        DistributedPartyTeamActivity.load(self)

        assert (self.notify.debug("load"))

        self.loadModels()
        self.loadGuiElements()
        self.loadSounds()
        self.loadIntervals()
        self.arrowKeys = ArrowKeys()

    def loadModels(self):
        # load the tug of war play area
        self.playArea = loader.loadModel(
            "phase_13/models/parties/partyTugOfWar")
        # reparent to the party ground root
        self.playArea.reparentTo(self.root)

        # place the activity sign
        self.sign.reparentTo(self.playArea.find("**/TugOfWar_sign_locator"))

        # define initial positions, with index 0 being closest to the other team
        self.dockPositions = [
            [],  # left team positions
            [],  # right team positions
        ]
        for i in range(4):
            self.dockPositions[0].append(
                Point3(
                    -PartyGlobals.TugOfWarInitialToonPositionsXOffset -
                    PartyGlobals.TugOfWarToonPositionXSeparation * i,
                    0.0,
                    PartyGlobals.TugOfWarToonPositionZ,
                ))
        for i in range(4):
            self.dockPositions[1].append(
                Point3(
                    PartyGlobals.TugOfWarInitialToonPositionsXOffset +
                    PartyGlobals.TugOfWarToonPositionXSeparation * i,
                    0.0,
                    PartyGlobals.TugOfWarToonPositionZ,
                ))
        self.hopOffPositions = [
            [],  # left team positions
            [],  # right team positions
        ]
        for i in range(1, 5):
            self.hopOffPositions[
                PartyGlobals.TeamActivityTeams.LeftTeam].append(
                    self.playArea.find("**/leftTeamHopOff%d_locator" %
                                       i).getPos())
            self.hopOffPositions[
                PartyGlobals.TeamActivityTeams.RightTeam].append(
                    self.playArea.find("**/rightTeamHopOff%d_locator" %
                                       i).getPos())

        # load positions for when toons fall into the water
        for i in range(1, 5):
            pos = self.playArea.find("**/fallenToon%d_locator" % i).getPos()
            self.fallenPositions.append(pos)

        # load collision that allows toons to play the game
        # create one for each dock that lets toons join a particular team
        self.joinCollision = []
        self.joinCollisionNodePaths = []
        for i in range(len(PartyGlobals.TeamActivityTeams)):
            collShape = CollisionTube(
                PartyGlobals.TugOfWarJoinCollisionEndPoints[0],
                PartyGlobals.TugOfWarJoinCollisionEndPoints[1],
                PartyGlobals.TugOfWarJoinCollisionRadius)
            collShape.setTangible(True)

            self.joinCollision.append(
                CollisionNode("TugOfWarJoinCollision%d" % i))
            self.joinCollision[i].addSolid(collShape)
            tubeNp = self.playArea.attachNewNode(self.joinCollision[i])
            tubeNp.node().setCollideMask(ToontownGlobals.WallBitmask)
            self.joinCollisionNodePaths.append(tubeNp)
            self.joinCollisionNodePaths[i].setPos(
                PartyGlobals.TugOfWarJoinCollisionPositions[i])
        self.__enableCollisions()

        # Get the rope texture by extracting it from its model.
        ropeModel = loader.loadModel(
            "phase_4/models/minigames/tug_of_war_rope")
        self.ropeTexture = ropeModel.findTexture("*")
        ropeModel.removeNode()

        # create as many ropes as we will ever need
        for i in range(PartyGlobals.TugOfWarMaximumPlayersPerTeam * 2 - 1):
            rope = Rope(self.uniqueName("TugRope%d" % i))
            if rope.showRope:
                rope.ropeNode.setRenderMode(RopeNode.RMBillboard)
                rope.ropeNode.setThickness(0.2)
                rope.setTexture(self.ropeTexture)
                rope.ropeNode.setUvMode(RopeNode.UVDistance)
                rope.ropeNode.setUvDirection(1)
                rope.setTransparency(1)
                rope.setColor(0.89, 0.89, 0.6, 1.0)
                rope.reparentTo(self.root)
                rope.stash()
            self.tugRopes.append(rope)

        # Splash object for when toon hits the water
        self.splash = Splash.Splash(self.root)
        self.splash.setScale(2.0, 4.0, 1.0)
        pos = self.fallenPositions[0]
        self.splash.setPos(pos[0], pos[1], PartyGlobals.TugOfWarSplashZOffset)
        self.splash.hide()

    def loadGuiElements(self):
        # load gui power meter
        self.powerMeter = MinigamePowerMeter(
            PartyGlobals.TugOfWarPowerMeterSize)
        self.powerMeter.reparentTo(aspect2d)
        self.powerMeter.setPos(0.0, 0.0, 0.6)
        self.powerMeter.hide()

        # Load the arrows for button indicator
        self.arrows = [None] * 2
        for x in range(len(self.arrows)):
            self.arrows[x] = loader.loadModel('phase_3/models/props/arrow')
            self.arrows[x].reparentTo(self.powerMeter)
            self.arrows[x].setScale(.2 - .4 * x, .2, .2)
            self.arrows[x].setPos(.12 - .24 * x, 0, -.26)

    def loadSounds(self):
        self.splashSound = base.loadSfx(
            "phase_4/audio/sfx/MG_cannon_splash.mp3")
        self.whistleSound = base.loadSfx(
            "phase_4/audio/sfx/AA_sound_whistle.mp3")

    def loadIntervals(self):
        # create an interval that updates the ideal key press rate for each stage
        self.updateIdealRateInterval = Sequence()
        # other code handles setting the initial ideal rate, so only add the
        # wait for the first state
        self.updateIdealRateInterval.append(
            Wait(PartyGlobals.TugOfWarTargetRateList[0][0]), )
        # for each stage after the first
        for i in range(1, len(PartyGlobals.TugOfWarTargetRateList)):
            duration = PartyGlobals.TugOfWarTargetRateList[i][0]
            idealRate = PartyGlobals.TugOfWarTargetRateList[i][1]
            # set ideal speed
            self.updateIdealRateInterval.append(
                Func(self.setIdealRate, idealRate))
            # add delay for stage's length or set last stage flag
            if i == (len(PartyGlobals.TugOfWarTargetRateList) - 1):
                self.updateIdealRateInterval.append(
                    Func(setattr, self, "allOutMode", True))
            else:
                self.updateIdealRateInterval.append(Wait(duration), )
        # create an interval that updates the local player's key press rate
        self.updateKeyPressRateInterval = Sequence(
            Wait(PartyGlobals.TugOfWarKeyPressUpdateRate),
            Func(self.updateKeyPressRate),
        )
        # create an interval that updates the local player's force and tells the
        # server
        self.reportToServerInterval = Sequence(
            Wait(PartyGlobals.TugOfWarKeyPressReportRate),
            Func(self.reportToServer),
        )

        self.setupInterval = Parallel()
        # run this even if the local toon is not playing
        self.globalSetupInterval = Sequence(
            Wait(PartyGlobals.TugOfWarReadyDuration +
                 PartyGlobals.TugOfWarGoDuration),
            Func(self.tightenRopes),
        )
        # only run this when a local toon is playing
        self.localSetupInterval = Sequence(
            Func(self.setStatus, TTLocalizer.PartyTugOfWarReady),
            Func(self.showStatus),
            Wait(PartyGlobals.TugOfWarReadyDuration),
            Func(base.playSfx, self.whistleSound),
            Func(self.setStatus, TTLocalizer.PartyTugOfWarGo),
            Wait(PartyGlobals.TugOfWarGoDuration),
            Func(self.enableKeys),
            Func(self.hideStatus),
            Func(self.updateIdealRateInterval.start),
            Func(self.updateKeyPressRateInterval.loop),
            Func(self.reportToServerInterval.loop),
        )

        # interval for playing the splash sound and showing the splash visual effect
        self.splashInterval = Sequence(
            Func(base.playSfx, self.splashSound),
            Func(self.splash.play),
        )

    def unload(self):
        DistributedPartyTeamActivity.unload(self)

        self.arrowKeys.destroy()
        self.unloadIntervals()
        self.unloadModels()
        self.unloadGuiElements()
        self.unloadSounds()

        # delete variables
        if hasattr(self, "toonIds"):
            del self.toonIds
        del self.buttons
        del self.arrowKeys
        del self.keyTTL
        del self.idealRate
        del self.keyRate
        del self.allOutMode
        del self.rateMatchAward

        del self.toonIdsToStartPositions
        del self.toonIdsToIsPullingFlags
        del self.toonIdsToRightHands
        del self.fallenToons
        del self.fallenPositions
        del self.unusedFallenPositionsIndices
        self.toonIdsToAnimIntervals.clear()
        del self.toonIdsToAnimIntervals

    def unloadModels(self):
        self.playArea.removeNode()
        del self.playArea

        del self.dockPositions
        del self.hopOffPositions

        self.__disableCollisions()
        while len(self.joinCollision) > 0:
            collNode = self.joinCollision.pop()
            del collNode
        while len(self.joinCollisionNodePaths) > 0:
            collNodePath = self.joinCollisionNodePaths.pop()
            collNodePath.removeNode()
            del collNodePath

        while len(self.tugRopes) > 0:
            rope = self.tugRopes.pop()
            if rope is not None:
                rope.removeNode()
            del rope
        del self.tugRopes

        self.splash.destroy()
        del self.splash

    def unloadGuiElements(self):
        for arrow in self.arrows:
            if arrow is not None:
                arrow.removeNode()
                del arrow
        del self.arrows

        if self.powerMeter is not None:
            self.powerMeter.cleanup()
            del self.powerMeter

    def unloadSounds(self):
        del self.splashSound
        del self.whistleSound

    def unloadIntervals(self):
        self.updateIdealRateInterval.pause()
        del self.updateIdealRateInterval

        self.updateKeyPressRateInterval.pause()
        del self.updateKeyPressRateInterval

        self.reportToServerInterval.pause()
        del self.reportToServerInterval

        self.setupInterval.pause()
        del self.setupInterval

        self.globalSetupInterval.pause()
        del self.globalSetupInterval

        self.localSetupInterval.pause()
        del self.localSetupInterval

        self.splashInterval.pause()
        del self.splashInterval

    def __enableCollisions(self):
        assert (self.notify.debug("__enableCollisions"))

        for i in range(len(PartyGlobals.TeamActivityTeams)):
            self.accept(
                "enterTugOfWarJoinCollision%d" % i,
                getattr(
                    self,
                    "_join%s" % PartyGlobals.TeamActivityTeams.getString(i)))

    def __disableCollisions(self):
        assert (self.notify.debug("__disableCollisions"))

        for i in range(len(PartyGlobals.TeamActivityTeams)):
            self.ignore("enterTugOfWarJoinCollision%d" % i)

    # FSM transition methods
    def startWaitForEnough(self):
        DistributedPartyTeamActivity.startWaitForEnough(self)

        self.__enableCollisions()

    def finishWaitForEnough(self):
        DistributedPartyTeamActivity.finishWaitForEnough(self)

        self.__disableCollisions()

    def startWaitToStart(self, waitStartTimestamp):
        DistributedPartyTeamActivity.startWaitToStart(self, waitStartTimestamp)

        self.__enableCollisions()

    def finishWaitToStart(self):
        DistributedPartyTeamActivity.finishWaitToStart(self)

        self.__disableCollisions()

    def startRules(self):
        DistributedPartyTeamActivity.startRules(self)

        self.setUpRopes()
        # display rules to the local toon if we have one
        if self.isLocalToonPlaying:
            self.showControls()

    def finishRules(self):
        DistributedPartyTeamActivity.finishRules(self)

        # check for a non-standard transition and do additional cleanup as needed
        if self.activityFSM.getCurrentOrNextState() == "WaitForEnough":
            self.hideRopes()
            self.hideControls()

    def finishWaitForServer(self):
        DistributedPartyTeamActivity.finishWaitForServer(self)

        # check for a non-standard transition and do additional cleanup as needed
        if self.activityFSM.getCurrentOrNextState() == "WaitForEnough":
            self.hideRopes()
            self.hideControls()

    def startActive(self):
        DistributedPartyTeamActivity.startActive(self)

        # reset active variables
        self.toonIdsToStartPositions.clear()
        self.toonIdsToIsPullingFlags.clear()

        for toonId in self.getToonIdsAsList():
            self.toonIdsToIsPullingFlags[toonId] = False
            toon = self.getAvatar(toonId)

            if toon:
                self.toonIdsToStartPositions[toonId] = toon.getPos(self.root)
            else:
                # what the heck do we do at this point? lets try 0,0,0
                self.notify.warning(
                    "couldn't find toon %d assigning 0,0,0 to startPos" %
                    toonId)
                self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0)

        self.unusedFallenPositionsIndices = [0, 1, 2, 3]
        self.setupInterval = Parallel(self.globalSetupInterval)

        if self.isLocalToonPlaying:
            self.keyTTL = []
            self.idealForce = 0.0
            self.keyRate = 0
            self.rateMatchAward = 0.0
            self.allOutMode = False
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
            self.setupInterval.append(self.localSetupInterval)

        self.setupInterval.start()

    def finishActive(self):
        DistributedPartyTeamActivity.finishActive(self)

        self.hideControls()
        self.disableKeys()
        self.setupInterval.pause()
        self.reportToServerInterval.pause()
        self.updateKeyPressRateInterval.pause()
        self.updateIdealRateInterval.pause()
        self.hideRopes()

    def startConclusion(self, losingTeam):
        DistributedPartyTeamActivity.startConclusion(self, losingTeam)

        if self.isLocalToonPlaying:
            self._rewardFinishedSV.set(False)

            if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
                self.setStatus(TTLocalizer.PartyTeamActivityGameTie)
            else:
                self.setStatus(TTLocalizer.PartyTugOfWarGameEnd)

            self.showStatus()

        if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
            # tie
            for toonId in self.getToonIdsAsList():
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop("neutral")
        else:
            # winning and losing team
            for toonId in self.toonIds[losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop("neutral")
            for toonId in self.toonIds[1 - losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop("victory")

        for ival in self.toonIdsToAnimIntervals.values():
            if ival is not None:
                ival.finish()

    def finishConclusion(self):
        DistributedPartyTeamActivity.finishConclusion(self)

        self.fallenToons = []

    def getTitle(self):
        return TTLocalizer.PartyTugOfWarTitle

    def getInstructions(self):
        return TTLocalizer.TugOfWarInstructions

    def showControls(self):
        # show the power meter and arrows so player can see them while they
        # read the rules
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarDisabledArrowColor)
        # set meter to first stage values
        self.powerMeter.setTarget(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setPower(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))
        self.powerMeter.clearTooSlowTooFast()
        self.powerMeter.show()

    def hideControls(self):
        self.powerMeter.hide()

    def setUpRopes(self):
        self.notify.debug("setUpRopes")
        ropeIndex = 0
        # setup rope linking the left team to the right team
        leftToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]:
            leftToonId = self.toonIds[
                PartyGlobals.TeamActivityTeams.LeftTeam][0]
        rightToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]:
            rightToonId = self.toonIds[
                PartyGlobals.TeamActivityTeams.RightTeam][0]
        if leftToonId in self.toonIdsToRightHands and \
           rightToonId in self.toonIdsToRightHands:
            self.tugRopes[ropeIndex].setup(
                3,
                (
                    (self.toonIdsToRightHands[self.toonIds[
                        PartyGlobals.TeamActivityTeams.LeftTeam][0]],
                     (0, 0, 0)),
                    (self.root, (0.0, 0.0, 2.5)),
                    (self.toonIdsToRightHands[self.toonIds[
                        PartyGlobals.TeamActivityTeams.RightTeam][0]],
                     (0, 0, 0)),
                ),
                [0, 0, 0, 1, 1, 1],
            )
            self.tugRopes[ropeIndex].unstash()
            ropeIndex += 1

        # setup ropes linking toons on the left team
        if len(self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]) > 1:
            for i in range(
                    len(self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam])
                    - 1, 0, -1):
                self.notify.debug(
                    "Connecting rope between toon %d and toon %d of left team."
                    % (i, i - 1))
                self.tugRopes[ropeIndex].setup(
                    3,
                    (
                        (self.toonIdsToRightHands[self.toonIds[
                            PartyGlobals.TeamActivityTeams.LeftTeam][i]],
                         (0, 0, 0)),
                        (self.toonIdsToRightHands[self.toonIds[
                            PartyGlobals.TeamActivityTeams.LeftTeam][i]],
                         (0, 0, 0)),
                        (self.toonIdsToRightHands[self.toonIds[
                            PartyGlobals.TeamActivityTeams.LeftTeam][i - 1]],
                         (0, 0, 0)),
                    ),
                    [0, 0, 0, 1, 1, 1],
                )
                self.tugRopes[ropeIndex].unstash()
                ropeIndex += 1

        # setup ropes linking toons on the right team
        if len(self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]) > 1:
            for i in range(
                    len(self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam])
                    - 1):
                self.notify.debug(
                    "Connecting rope between toon %d and toon %d of left team."
                    % (i, i + 1))
                self.tugRopes[ropeIndex].setup(
                    3,
                    (
                        (self.toonIdsToRightHands[self.toonIds[
                            PartyGlobals.TeamActivityTeams.RightTeam][i]],
                         (0, 0, 0)),
                        (self.toonIdsToRightHands[self.toonIds[
                            PartyGlobals.TeamActivityTeams.RightTeam][i]],
                         (0, 0, 0)),
                        (self.toonIdsToRightHands[self.toonIds[
                            PartyGlobals.TeamActivityTeams.RightTeam][i + 1]],
                         (0, 0, 0)),
                    ),
                    [0, 0, 0, 1, 1, 1],
                )
                self.tugRopes[ropeIndex].unstash()
                ropeIndex += 1

    def tightenRopes(self):
        """
        The pulling part has started. Make the rope between the teams taut.
        """
        self.notify.debug("tightenRopes")
        self.tugRopes[0].setup(
            3,
            (
                (self.toonIdsToRightHands[self.toonIds[
                    PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)),
                (self.toonIdsToRightHands[self.toonIds[
                    PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)),
                (self.toonIdsToRightHands[self.toonIds[
                    PartyGlobals.TeamActivityTeams.RightTeam][0]], (0, 0, 0)),
            ),
            [0, 0, 0, 1, 1, 1],
        )

    def hideRopes(self):
        self.notify.debug("hideRopes")
        for rope in self.tugRopes:
            rope.stash()

    def handleGameTimerExpired(self):
        assert (self.notify.debug("game timer expired"))

        self.disableKeys()  # do not allow any more input

    def setIdealRate(self, idealRate):
        self.notify.debug("setIdealRate( %d )" % idealRate)
        self.idealRate = idealRate
        self.idealForce = self.advantage * (4 + 0.4 * self.idealRate)

    def updateKeyPressRate(self):
        # decrement times to live for each key press entry in keyTTL
        for i in range(len(self.keyTTL)):
            self.keyTTL[i] -= PartyGlobals.TugOfWarKeyPressUpdateRate

        # remove all key presses that have run out of time to live
        # I think this only removes at most 1 item from the list, which is not
        # what we want, but worked for Trolley tug of war so I'm afraid to "fix" it
        for i in range(len(self.keyTTL)):
            if self.keyTTL[i] <= 0.0:
                a = self.keyTTL[0:i]
                del self.keyTTL
                self.keyTTL = a
                break

        self.keyRate = len(self.keyTTL)

        # if the user has matched the idealRate several times in a row, add
        # a little bit to their power
        if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1:
            self.rateMatchAward += 0.3
        else:
            self.rateMatchAward = 0.0

    def reportToServer(self):
        self.currentForce = self.computeForce(self.keyRate)
        self.sendUpdate("reportKeyRateForce",
                        [self.keyRate, self.currentForce])
        self.setSpeedGauge()
        self.setAnimState(base.localAvatar.doId, self.keyRate)

    def computeForce(self, keyRate):
        # return a force in the range 0-self.idealRate
        F = 0
        # if this is the last stage, make force directly proportional to keyrate
        if self.allOutMode:
            F = 0.75 * keyRate
        # otherwise, make force proportional to how close you are to ideal key rate
        else:
            stdDev = 0.25 * self.idealRate
            F = self.advantage * (
                self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(
                    math.e, -math.pow(keyRate - self.idealRate, 2) /
                    (2.0 * math.pow(stdDev, 2)))
        return F

    def setSpeedGauge(self):
        # update the power meter to show the toon's speed and the target speed
        self.powerMeter.setPower(self.keyRate)
        self.powerMeter.setTarget(self.idealRate)

        # change the color of the power meter to indicate how well the toon is doing
        # the color should be dark if you are doing badly, and green if you are doing well
        if not self.allOutMode:
            # tell the toon if he is pulling too fast or too slow
            self.powerMeter.updateTooSlowTooFast()
            index = float(self.currentForce) / self.idealForce
            bonus = 0.0
            if index > 1.0:
                bonus = max(1.0, index - 1.0)
                index = 1.0
            color = (0, 0.75 * index + 0.25 * bonus, 0.75 * (1 - index), 0.5)
            self.powerMeter.setBarColor(color)
        else:
            self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))

    def updateToonKeyRate(self, toonId, keyRate):
        # since we set the local toon's pulling animation locally, don't do it
        # here
        if toonId != base.localAvatar.doId:
            self.setAnimState(toonId, keyRate)

    def setAnimState(self, toonId, keyRate):
        if self.activityFSM.state != "Active":
            return
        toon = self.getAvatar(toonId)
        if not self.toonIdsToIsPullingFlags.has_key(toonId):
            if self.getTeam(toonId) == None:
                self.notify.warning(
                    "setAnimState called with toonId (%d) that wasn't in self.toonIds"
                    % toonId)
                return
            else:
                self.notify.warning(
                    "setAnimState called with toonId (%d) that was in self.toonIds but not in self.toonIdsToIsPullingFlags. Adding it."
                    % toonId)
                self.toonIdsToIsPullingFlags[toonId] = False

        if keyRate > 0 and not self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.loop('tug-o-war')
            else:
                self.notify.warning(
                    "toon %d is None, skipping toon.loop(tugowar)" % toonId)
            self.toonIdsToIsPullingFlags[toonId] = True
        if keyRate <= 0 and self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.pose('tug-o-war', 3)
                toon.startLookAround()
            else:
                self.notify.warning(
                    "toon %d is None, skipping toon.startLookAround" % toonId)
            self.toonIdsToIsPullingFlags[toonId] = False

    def enableKeys(self):
        self.notify.debug("enableKeys")
        # Change the order of the press handlers because we are only using 2 keys
        self.arrowKeys.setPressHandlers([
            lambda: self.__pressHandler(2),
            lambda: self.__pressHandler(3),
            lambda: self.__pressHandler(1),
            lambda: self.__pressHandler(0),
        ])
        self.arrowKeys.setReleaseHandlers([
            lambda: self.__releaseHandler(2),
            lambda: self.__releaseHandler(3),
            lambda: self.__releaseHandler(1),
            lambda: self.__releaseHandler(0),
        ])
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def disableKeys(self):
        self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
        self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)

    # callbacks for when the buttons are pressed and released
    def __pressHandler(self, index):
        if index == self.buttons[0]:
            self.arrows[index].setColor(
                PartyGlobals.TugOfWarHilightedArrowColor)
            self.keyTTL.insert(0, PartyGlobals.TugOfWarKeyPressTimeToLive)
            self.buttons.reverse()

    def __releaseHandler(self, index):
        if index in self.buttons:
            self.arrows[index].setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def updateToonPositions(self, offset):
        # Since the timer expires locally, we may still get a few
        # messages from the AI that were on the wire when we left
        # the play state, just ignore it
        if self.activityFSM.state != "Active":
            return

        # adjust the camera angle
        if self.isLocalToonPlaying:
            camera.lookAt(self.root, offset, 0.0,
                          PartyGlobals.TugOfWarCameraLookAtHeightOffset)

        # this client sets the position of all toons playing
        for toonId in self.getToonIdsAsList():
            if hasattr(self,"fallenToons") and \
               toonId not in self.fallenToons:
                toon = self.getAvatar(toonId)
                if toon is not None:
                    origPos = self.toonIdsToStartPositions[toonId]
                    curPos = toon.getPos(self.root)
                    newPos = Point3(origPos[0] + offset, curPos[1], curPos[2])
                    # finish any existing animation interval
                    if self.toonIdsToAnimIntervals[toonId] != None:
                        if self.toonIdsToAnimIntervals[toonId].isPlaying():
                            self.toonIdsToAnimIntervals[toonId].finish()
                            self.checkIfFallen(toonId)

                    if toonId not in self.fallenToons:
                        self.toonIdsToAnimIntervals[toonId] = Sequence(
                            LerpPosInterval(
                                toon,
                                duration=PartyGlobals.
                                TugOfWarKeyPressReportRate,
                                pos=newPos,
                                other=self.root,
                            ), Func(self.checkIfFallen, toonId))
                        self.toonIdsToAnimIntervals[toonId].start()

    def checkIfFallen(self, toonId):
        # check if toon has fallen
        if hasattr(self,"fallenToons") and \
           toonId not in self.fallenToons:
            toon = self.getAvatar(toonId)
            if toon:
                curPos = toon.getPos(self.root)
                team = self.getTeam(toonId)
                if ((team == PartyGlobals.TeamActivityTeams.LeftTeam
                     and curPos[0] > -2.0)
                        or (team == PartyGlobals.TeamActivityTeams.RightTeam
                            and curPos[0] < 2.0)):
                    # throw all toons from this side in the water
                    losingTeam = self.getTeam(toonId)
                    self.throwTeamInWater(losingTeam)
                    # tell AI that a team fell in the water
                    self.sendUpdate("reportFallIn", [losingTeam])

    def throwTeamInWater(self, losingTeam):
        self.notify.debug("throwTeamInWater( %s )" %
                          PartyGlobals.TeamActivityTeams.getString(losingTeam))
        splashSet = False
        for toonId in self.toonIds[losingTeam]:
            # throw toon in water
            self.fallenToons.append(toonId)
            toon = self.getAvatar(toonId)
            # getting a a crash of popping from empty list
            #fallenPosIndex = self.unusedFallenPositionsIndices.pop(0)
            fallenPosIndex = self.toonIds[losingTeam].index(toonId)
            if (fallenPosIndex < 0) or (fallenPosIndex >= 4):
                fallenPosIndex = 0
            newPos = self.fallenPositions[fallenPosIndex]

            # animate the toons falling into the water
            if self.toonIdsToAnimIntervals[toonId] is not None:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()

            # Fall into water
            if toon:
                parallel = Parallel(
                    ActorInterval(actor=toon,
                                  animName='slip-forward',
                                  duration=2.0),
                    LerpPosInterval(toon,
                                    duration=2.0,
                                    pos=newPos,
                                    other=self.root),
                )
            else:
                self.notify.warning("toon %d is none, skipping slip-forward" %
                                    toonId)
                parallel = Parallel()

            # only setup splash for the first toon
            if not splashSet:
                splashSet = True
                parallel.append(self.splashInterval)

            if toon:
                self.toonIdsToAnimIntervals[toonId] = Sequence(
                    parallel,
                    Func(toon.loop, 'neutral'),
                )
            else:
                self.notify.warning(
                    "toon %d is none, skipping toon.loop(neutral)" % toonId)
                self.toonIdsToAnimIntervals[toonId] = parallel
            self.toonIdsToAnimIntervals[toonId].start()

    def setAdvantage(self, advantage):
        DistributedPartyTeamActivity.setAdvantage(self, advantage)

        if self.isLocalToonPlaying:
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
class DistributedPlayerSimpleShip(DistributedSimpleShip):
    RepairSpotFadeAfter = 2.0
    RepairSpotFadeDur = 3.0

    def __init__(self, cr):
        DistributedSimpleShip.__init__(self, cr)
        self._respawnLocation = None
        self.checkAnchor = None
        self.lastAttacked = None
        self.threatLevel = 0
        self.openPort = 0
        self.allowCrewState = True
        self.allowFriendState = True
        self.allowGuildState = False
        self.allowPublicState = False
        self._repairSpotMgr = ShipRepairSpotMgr(self.cr)
        self._team = PiratesGlobals.PLAYER_TEAM
        self.badInitTeam = None
        self.prevLocStack = None

    def generate(self):
        DistributedSimpleShip.generate(self)
        self._repairSpotWoodPile = None
        self._repairSpotWoodPiles = {}
        self._repairSpotHole = None
        self._repairSpotHoleFixed = None
        self._repairSpotHoles = {}
        self._repairSpotIvals = {}
        self._wheelInUse = StateVar(False)

    def announceGenerate(self):
        self._respawnLocation = None
        self._respawnResponseDelayedCall = None
        DistributedSimpleShip.announceGenerate(self)
        self._repairSpotMgr.setShipId(self.doId)
        if self.badInitTeam != None:
            self._verifyTeam(self.badInitTeam)

    def disable(self):
        self._wheelInUse.destroy()
        if self._respawnResponseDelayedCall:
            self._respawnResponseDelayedCall.destroy()
            self._respawnResponseDelayedCall = None

        if self.checkAnchor:
            self.checkAnchor.remove()
            self.checkAnchor = None

        self._repairSpotMgr.destroy()
        for ival in self._repairSpotIvals.itervalues():
            ival.pause()

        del self._repairSpotIvals
        self.prevLocStack = None
        DistributedSimpleShip.disable(self)

    def calculateLook(self):
        team = self.getTeam()
        if team == PiratesGlobals.PLAYER_TEAM:
            if self.getSiegeTeam() == 1:
                self.style = ShipGlobals.Styles.French
            elif self.getSiegeTeam() == 2:
                self.style = ShipGlobals.Styles.Spanish

    def getNPCship(self):
        return False

    def setShipClass(self, shipClass):
        DistributedSimpleShip.setShipClass(self, shipClass)
        self._repairSpotMgr.updateShipClass(self.shipClass)

    def setHealthState(self, health):
        DistributedSimpleShip.setHealthState(self, health)
        self._repairSpotMgr.updateHealth(self.healthState)

    def setMastStates(self, mainMast1, mainMast2, mainMast3, aftMast,
                      foreMast):
        DistributedSimpleShip.setMastStates(self, mainMast1, mainMast2,
                                            mainMast3, aftMast, foreMast)
        self._repairSpotMgr.updateSpeed(100.0 * self.Sp / self.maxSp)

    def setArmorStates(self, rear, left, right):
        DistributedSimpleShip.setArmorStates(self, rear, left, right)
        self._repairSpotMgr.updateArmor((rear + left + right) / 3.0)

    def setWillFullyRepairShip(self, willFullyRepairShip):
        self._repairSpotMgr.updateWillBeFullHealth(willFullyRepairShip)

    def setupLocalStats(self):
        DistributedSimpleShip.setupLocalStats(self)

    def setOpenPort(self, portId):
        oldPort = self.openPort
        self.openPort = portId
        if localAvatar.ship and localAvatar.ship.getDoId() == self.getDoId():
            messenger.send('LocalAvatar_Ship_OpenPort_Update',
                           [portId, oldPort])

    def getOpenPort(self):
        return self.openPort

    def isAtOpenPort(self):
        portDoId = localAvatar.getPort()
        portObj = base.cr.doId2do.get(portDoId, None)
        if self.threatLevel < EnemyGlobals.SHIP_THREAT_NAVY_HUNTERS:
            return 1
        elif portObj and portObj.uniqueId == EnemyGlobals.OPEN_PORT_DICT.get(
                self.openPort):
            return 1
        else:
            return 0

    def setThreatLevel(self, threatLevel):
        if threatLevel != self.threatLevel:
            self.threatLevel = threatLevel
            self.updateNametag()
            if localAvatar.ship and localAvatar.ship.getDoId() == self.getDoId(
            ):
                messenger.send('LocalAvatar_Ship_ThreatLevel_Update',
                               [threatLevel])

            self.checkAbleDropAnchor()

    def getThreatLevel(self):
        if base.config.GetBool('want-ship-threat', 1):
            return self.threatLevel
        else:
            return EnemyGlobals.SHIP_THREAT_ATTACK_BACK

    def getOpenPort(self):
        return self.openPort

    def sunkAShipFanfare(self, shipToAttackDoId):
        if localAvatar.ship and localAvatar.ship == self:
            if localAvatar.ship.getSiegeTeam():
                return None

            attackMessage = HighSeasGlobals.getShipSunkMessage()
            if attackMessage:
                base.localAvatar.guiMgr.queueInstructionMessage(
                    attackMessage[0],
                    attackMessage[1],
                    None,
                    1.0,
                    messageCategory=MessageGlobals.MSG_CAT_SUNK_SHIP)

    def setSiegeTeam(self, team):
        different = team != self.getSiegeTeam()
        DistributedSimpleShip.setSiegeTeam(self, team)
        if different:
            self._doSiegeAndPVPTeamColors()
            self._repairSpotMgr.updateSiegeTeam(team)
            minimapObj = self.getMinimapObject()
            if minimapObj:
                minimapObj.setSiegeTeam(team)

    setSiegeTeam = report(types=['args'],
                          dConfigParam='shipdeploy')(setSiegeTeam)

    def _doSiegeAndPVPTeamColors(self):
        if self.getPVPTeam():
            self._doPVPTeamColors()
        elif self.getSiegeTeam():
            pass

    def _doPVPTeamColors(self):
        pass

    def getWheelInUseSV(self):
        return self._wheelInUse

    def setWheelInUse(self, wheelInUse):
        DistributedSimpleShip.setWheelInUse(self, wheelInUse)
        self._wheelInUse.set(wheelInUse)

    def canTakeWheel(self, wheel, av):
        available = True
        if self.queryGameState() in ('Pinned', 'Sinking', 'Sunk',
                                     'OtherShipBoarded'):
            base.localAvatar.guiMgr.createWarning(PLocalizer.ShipPinnedWarning,
                                                  PiratesGuiGlobals.TextFG6)
            available = False
        elif self.isFishing and base.localAvatar.getDoId() != self.ownerId:
            base.localAvatar.guiMgr.createWarning(
                PLocalizer.OnlyCaptainCanUseWarning, PiratesGuiGlobals.TextFG6)
            available = False
        elif wheel.getUserId() and base.localAvatar.getDoId() != self.ownerId:
            base.localAvatar.guiMgr.createWarning(
                PLocalizer.AlreadyInUseWarning, PiratesGuiGlobals.TextFG6)
            available = False

        return available

    def setRespawnLocation(self, parentId, zoneId):
        self._respawnLocation = (parentId, zoneId)

    def setLocation(self, parentId, zoneId):
        DistributedSimpleShip.setLocation(self, parentId, zoneId)
        if self._respawnLocation is not None and self._respawnLocation == (
                parentId, zoneId):
            self._respawnLocation = None
            if not self._respawnResponseDelayedCall:
                self._respawnResponseDelayedCall = FrameDelayedCall(
                    'PlayerShip-respawnLocation-gridInterestComplete',
                    Functor(base.cr.setAllInterestsCompleteCallback,
                            self._sendRespawnLocationResponse))

    def _sendRespawnLocationResponse(self):
        self.sendUpdate('clientReachedRespawnLocation')
        self._respawnResponseDelayedCall = None

    def recoverFromSunk(self):
        self.lastAttacked = None
        DistributedSimpleShip.recoverFromSunk(self)

    def attacked(self):
        self.lastAttacked = globalClock.getFrameTime()
        if self.getSiegeTeam() and not (self.checkAnchor):
            self.checkAbleDropAnchor()

    def attackTimerRemaining(self):
        timer = 0
        if self.lastAttacked:
            timer = int(30 - globalClock.getFrameTime() - self.lastAttacked)
        return timer

    def _DistributedPlayerSimpleShip__recheckAbleDropAnchor(self, task):
        self.checkAnchor = None
        self.checkAbleDropAnchor()

    def checkAbleDropAnchor(self):
        PiratesGuiGlobals = PiratesGuiGlobals
        import pirates.piratesgui
        if localAvatar.doId == self.steeringAvId:
            if self.shipStatusDisplay:
                if localAvatar.getPort():
                    remaining = self.attackTimerRemaining()
                    if self.getSiegeTeam() and remaining > 0:
                        self.shipStatusDisplay.disableAnchorButton()
                        localAvatar.guiMgr.createWarning(
                            PLocalizer.CannotDockYet % remaining,
                            PiratesGuiGlobals.TextFG6)
                        self.checkAnchor = taskMgr.doMethodLater(
                            remaining, self.
                            _DistributedPlayerSimpleShip__recheckAbleDropAnchor,
                            'checkAnchor')
                    elif self.isAtOpenPort():
                        self.shipStatusDisplay.enableAnchorButton()
                    else:
                        self.shipStatusDisplay.disableAnchorButton()
                        self.shipStatusDisplay.tellWrongPort()
                else:
                    self.shipStatusDisplay.disableAnchorButton()
                    self.shipStatusDisplay.hideWrongPort()

    def _addRepairSpotModels(self):
        if not self._repairSpotWoodPile:
            self._repairSpotWoodPile = loader.loadModel(
                'models/props/repair_spot_wood')
            collFloors = self._repairSpotWoodPile.find('**/collision_floor')
            if not collFloors.isEmpty():
                collideMask = collFloors.getCollideMask()
                collideMask ^= PiratesGlobals.FloorBitmask
                collideMask |= PiratesGlobals.ShipFloorBitmask
                collFloors.setCollideMask(collideMask)

        for locIndex in PVPGlobals.ShipClass2repairLocators[
                self.modelClass].getValue():
            locName = PVPGlobals.RepairSpotLocatorNames[locIndex]
            self._repairSpotWoodPiles[locName] = self.getModelRoot(
            ).attachNewNode('repairSpotWoodPile-%s' % locName)
            self._repairSpotWoodPile.instanceTo(
                self._repairSpotWoodPiles[locName])
            locator = self.getLocator(locName)
            self._repairSpotWoodPiles[locName].setPosHpr(
                locator.getPos(), locator.getHpr())

    def _removeRepairSpotModels(self):
        for woodPile in self._repairSpotWoodPiles.itervalues():
            woodPile.detachNode()

        self._repairSpotWoodPiles = {}

    def _placeRepairSpotModel(self, locIndex, model):
        locName = PVPGlobals.RepairSpotLocatorNames[locIndex]
        parentNode = self.getModelRoot().attachNewNode('repairSpotHole-%s' %
                                                       locName)
        parentNode.setTransparency(1, 100)
        model.instanceTo(parentNode)
        locator = self.getLocator(locName)
        parentNode.setPosHpr(locator.getPos(), locator.getHpr())
        self._repairSpotHoles[locIndex] = parentNode

    def _removeRepairSpotModel(self, locIndex):
        if locIndex in self._repairSpotHoles:
            self._repairSpotHoles[locIndex].detachNode()
            del self._repairSpotHoles[locIndex]

    def _fadeOutRepairSpotModel(self, locIndex):
        if locIndex in self._repairSpotIvals:
            self._repairSpotIvals[locIndex].pause()

        self._repairSpotHoles[locIndex].setTransparency(1, 100)
        ival = IG.Sequence(
            IG.Wait(DistributedPlayerSimpleShip.RepairSpotFadeAfter),
            IG.LerpColorScaleInterval(
                self._repairSpotHoles[locIndex],
                DistributedPlayerSimpleShip.RepairSpotFadeDur,
                Vec4(1.0, 1.0, 1.0, 0.0),
                blendType='easeInOut'))
        ival.start()
        self._repairSpotIvals[locIndex] = ival

    def _addRepairSpotHoles(self):
        if not self._repairSpotHole:
            repairSpotHoleModels = loader.loadModel(
                'models/props/repair_spot_hole')
            self._repairSpotHole = repairSpotHoleModels.find('**/floor_hole')
            self._repairSpotHoleFixed = repairSpotHoleModels.find(
                '**/floor_hole_fixed')

        for locIndex in PVPGlobals.ShipClass2repairLocators[
                self.modelClass].getValue():
            self._removeRepairSpotModel(locIndex)
            self._placeRepairSpotModel(locIndex, self._repairSpotHole)

    def _removeRepairSpotHoles(self):
        for locIndex in PVPGlobals.ShipClass2repairLocators[
                self.modelClass].getValue():
            self._removeRepairSpotModel(locIndex)
            if self._repairSpotHoleFixed:
                self._placeRepairSpotModel(locIndex, self._repairSpotHoleFixed)
                self._fadeOutRepairSpotModel(locIndex)
                self._repairSpotIvals[locIndex] = IG.Sequence(
                    self._repairSpotIvals[locIndex],
                    IG.Func(self._removeRepairSpotModel, locIndex))

    def b_setAllowCrewState(self, state):
        self.d_setAllowCrewState(state)
        self.setAllowCrewState(state)

    def b_setAllowFriendState(self, state):
        self.d_setAllowFriendState(state)
        self.setAllowFriendState(state)

    def b_setAllowGuildState(self, state):
        self.d_setAllowGuildState(state)
        self.setAllowGuildState(state)

    def b_setAllowPublicState(self, state):
        self.d_setAllowPublicState(state)
        self.setAllowPublicState(state)

    def d_setAllowCrewState(self, state):
        self.sendUpdate('setAllowCrewState', [state])

    def d_setAllowFriendState(self, state):
        self.sendUpdate('setAllowFriendState', [state])

    def d_setAllowGuildState(self, state):
        self.sendUpdate('setAllowGuildState', [state])

    def d_setAllowPublicState(self, state):
        self.sendUpdate('setAllowPublicState', [state])

    def setAllowCrewState(self, state):
        self.allowCrewState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowCrew(state)

    def setAllowFriendState(self, state):
        self.allowFriendState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowFriends(state)

    def setAllowGuildState(self, state):
        self.allowGuildState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowGuild(state)

    def setAllowPublicState(self, state):
        self.allowPublicState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowPublic(state)

    def getAllowCrewState(self):
        return self.allowCrewState

    def getAllowFriendState(self):
        return self.allowFriendState

    def getAllowGuildState(self):
        return self.allowGuildState

    def getAllowPublicState(self):
        return self.allowPublicState

    def hasSpace(self, avId=0, bandMgrId=0, bandId=0, guildId=0):
        if avId == self.ownerId:
            return True

        if self.isInCrew(avId):
            return True

        if self.isInCrew(self.ownerId) and len(self.crew) >= self.maxCrew:
            return False

        if len(self.crew) >= self.maxCrew - 1:
            return False

        return True

    def confirmSameCrewTeleport(self,
                                toFrom,
                                incomingAvId=0,
                                bandMgrId=0,
                                bandId=0,
                                guildId=0):
        if toFrom == 'from':
            return True
        elif not self.isGenerated():
            self.notify.warning('confirmSameCrewTeleport(%s)' %
                                localAvatar.getShipString())
            return False

        if incomingAvId == self.ownerId:
            return True

        if bandMgrId and bandId and self.getAllowCrewState() and (
                bandMgrId, bandId) == self.getBandId():
            return True

        if localAvatar.doId == self.ownerId and self.getAllowFriendState(
        ) and self.cr.identifyFriend(incomingAvId):
            return True

        if guildId and self.getAllowGuildState(
        ) and guildId == self.getGuildId():
            return True

        if self.getAllowPublicState():
            return True

        return False

    confirmSameCrewTeleport = report(
        types=['frameCount', 'deltaStamp',
               'args'], dConfigParam='shipboard')(confirmSameCrewTeleport)

    def getMinimapObject(self):
        if not (self.minimapObj) and not self.isDisabled():
            self.minimapObj = MinimapPlayerShip(self)

        return self.minimapObj

    def setTeam(self, team):
        if not self._verifyTeam(team):
            return None

        DistributedSimpleShip.setTeam(self, team)

    def _verifyTeam(self, team):
        if team == PiratesGlobals.INVALID_TEAM:
            doId = '<no doId>'
            if hasattr(self, 'doId'):
                doId = self.doId
            else:
                self.badInitTeam = team
            base.cr.centralLogger.writeClientEvent('bad ship team: %s' % doId)
            self.notify.warning('bad ship team: %s' % doId)
            return False

        return True

    def d_setLocation(self, parentId, zoneId):
        theStack = StackTrace(start=1)
        if self.prevLocStack and len(
                theStack.trace) == len(self.prevLocStack.trace) and map(
                    lambda x: x[1], theStack.trace) == map(
                        lambda x: x[1], self.prevLocStack.trace):
            base.cr.centralLogger.writeClientEvent('bad ship team: %s setLoc' %
                                                   self.doId)
        else:
            base.cr.centralLogger.writeClientEvent(
                'bad ship team: %s' % self.doId +
                theStack.compact()[1:len(theStack.compact())])
            self.prevLocStack = theStack
        DistributedSimpleShip.d_setLocation(self, parentId, zoneId)
Example #22
0
# Embedded file name: toontown.cogdominium.CogdoBoardroomGameConsts
from direct.fsm.StatePush import StateVar
from otp.level.EntityStateVarSet import EntityStateVarSet
from toontown.cogdominium.CogdoEntityTypes import CogdoBoardroomGameSettings
Settings = EntityStateVarSet(CogdoBoardroomGameSettings)
GameDuration = StateVar(60.0)
FinishDuration = StateVar(10.0)
Example #23
0
class TaskManager:
    notify = directNotify.newCategory("TaskManager")

    taskTimerVerbose = ConfigVariableBool('task-timer-verbose', False)
    extendedExceptions = ConfigVariableBool('extended-exceptions', False)
    pStatsTasks = ConfigVariableBool('pstats-tasks', False)

    MaxEpochSpeed = 1.0/30.0

    def __init__(self):
        self.mgr = AsyncTaskManager.getGlobalPtr()

        self.resumeFunc = None
        self.globalClock = self.mgr.getClock()
        self.stepping = False
        self.running = False
        self.destroyed = False
        self.fKeyboardInterrupt = False
        self.interruptCount = 0
        if signal:
            self.__prevHandler = signal.default_int_handler

        self._frameProfileQueue = []

        # this will be set when it's safe to import StateVar
        self._profileFrames = None
        self._frameProfiler = None
        self._profileTasks = None
        self._taskProfiler = None
        self._taskProfileInfo = ScratchPad(
            taskId = None,
            profiled = False,
            session = None,
            )

    def finalInit(self):
        # This function should be called once during startup, after
        # most things are imported.
        from direct.fsm.StatePush import StateVar
        self._profileTasks = StateVar(False)
        self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
        self._profileFrames = StateVar(False)
        self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())

    def destroy(self):
        # This should be safe to call multiple times.
        self.running = False
        self.notify.info("TaskManager.destroy()")
        self.destroyed = True
        self._frameProfileQueue.clear()
        self.mgr.cleanup()

    def setClock(self, clockObject):
        self.mgr.setClock(clockObject)
        self.globalClock = clockObject

    clock = property(lambda self: self.mgr.getClock(), setClock)

    def invokeDefaultHandler(self, signalNumber, stackFrame):
        print('*** allowing mid-frame keyboard interrupt.')
        # Restore default interrupt handler
        if signal:
            signal.signal(signal.SIGINT, self.__prevHandler)
        # and invoke it
        raise KeyboardInterrupt

    def keyboardInterruptHandler(self, signalNumber, stackFrame):
        self.fKeyboardInterrupt = 1
        self.interruptCount += 1
        if self.interruptCount == 1:
            print('* interrupt by keyboard')
        elif self.interruptCount == 2:
            print('** waiting for end of frame before interrupting...')
            # The user must really want to interrupt this process
            # Next time around invoke the default handler
            signal.signal(signal.SIGINT, self.invokeDefaultHandler)

    def getCurrentTask(self):
        """ Returns the task currently executing on this thread, or
        None if this is being called outside of the task manager. """

        return Thread.getCurrentThread().getCurrentTask()

    def hasTaskChain(self, chainName):
        """ Returns true if a task chain with the indicated name has
        already been defined, or false otherwise.  Note that
        setupTaskChain() will implicitly define a task chain if it has
        not already been defined, or modify an existing one if it has,
        so in most cases there is no need to check this method
        first. """

        return self.mgr.findTaskChain(chainName) is not None

    def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
                       threadPriority = None, frameBudget = None,
                       frameSync = None, timeslicePriority = None):
        """Defines a new task chain.  Each task chain executes tasks
        potentially in parallel with all of the other task chains (if
        numThreads is more than zero).  When a new task is created, it
        may be associated with any of the task chains, by name (or you
        can move a task to another task chain with
        task.setTaskChain()).  You can have any number of task chains,
        but each must have a unique name.

        numThreads is the number of threads to allocate for this task
        chain.  If it is 1 or more, then the tasks on this task chain
        will execute in parallel with the tasks on other task chains.
        If it is greater than 1, then the tasks on this task chain may
        execute in parallel with themselves (within tasks of the same
        sort value).

        If tickClock is True, then this task chain will be responsible
        for ticking the global clock each frame (and thereby
        incrementing the frame counter).  There should be just one
        task chain responsible for ticking the clock, and usually it
        is the default, unnamed task chain.

        threadPriority specifies the priority level to assign to
        threads on this task chain.  It may be one of TPLow, TPNormal,
        TPHigh, or TPUrgent.  This is passed to the underlying
        threading system to control the way the threads are scheduled.

        frameBudget is the maximum amount of time (in seconds) to
        allow this task chain to run per frame.  Set it to -1 to mean
        no limit (the default).  It's not directly related to
        threadPriority.

        frameSync is true to force the task chain to sync to the
        clock.  When this flag is false, the default, the task chain
        will finish all of its tasks and then immediately start from
        the first task again, regardless of the clock frame.  When it
        is true, the task chain will finish all of its tasks and then
        wait for the clock to tick to the next frame before resuming
        the first task.  This only makes sense for threaded tasks
        chains; non-threaded task chains are automatically
        synchronous.

        timeslicePriority is False in the default mode, in which each
        task runs exactly once each frame, round-robin style,
        regardless of the task's priority value; or True to change the
        meaning of priority so that certain tasks are run less often,
        in proportion to their time used and to their priority value.
        See AsyncTaskManager.setTimeslicePriority() for more.
        """

        chain = self.mgr.makeTaskChain(chainName)
        if numThreads is not None:
            chain.setNumThreads(numThreads)
        if tickClock is not None:
            chain.setTickClock(tickClock)
        if threadPriority is not None:
            chain.setThreadPriority(threadPriority)
        if frameBudget is not None:
            chain.setFrameBudget(frameBudget)
        if frameSync is not None:
            chain.setFrameSync(frameSync)
        if timeslicePriority is not None:
            chain.setTimeslicePriority(timeslicePriority)

    def hasTaskNamed(self, taskName):
        """Returns true if there is at least one task, active or
        sleeping, with the indicated name. """

        return bool(self.mgr.findTask(taskName))

    def getTasksNamed(self, taskName):
        """Returns a list of all tasks, active or sleeping, with the
        indicated name. """
        return list(self.mgr.findTasks(taskName))

    def getTasksMatching(self, taskPattern):
        """Returns a list of all tasks, active or sleeping, with a
        name that matches the pattern, which can include standard
        shell globbing characters like \\*, ?, and []. """

        return list(self.mgr.findTasksMatching(GlobPattern(taskPattern)))

    def getAllTasks(self):
        """Returns list of all tasks, active and sleeping, in
        arbitrary order. """
        return list(self.mgr.getTasks())

    def getTasks(self):
        """Returns list of all active tasks in arbitrary order. """
        return list(self.mgr.getActiveTasks())

    def getDoLaters(self):
        """Returns list of all sleeping tasks in arbitrary order. """
        return list(self.mgr.getSleepingTasks())

    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
                      sort = None, priority = None, taskChain = None,
                      uponDeath = None, appendTask = False, owner = None):

        """Adds a task to be performed at some time in the future.
        This is identical to `add()`, except that the specified
        delayTime is applied to the Task object first, which means
        that the task will not begin executing until at least the
        indicated delayTime (in seconds) has elapsed.

        After delayTime has elapsed, the task will become active, and
        will run in the soonest possible frame thereafter.  If you
        wish to specify a task that will run in the next frame, use a
        delayTime of 0.
        """

        if delayTime < 0:
            assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))

        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
        task.setDelay(delayTime)
        self.mgr.add(task)
        return task

    do_method_later = doMethodLater

    def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
            priority = None, uponDeath = None, appendTask = False,
            taskChain = None, owner = None, delay = None):
        """
        Add a new task to the taskMgr.  The task will begin executing
        immediately, or next frame if its sort value has already
        passed this frame.

        Parameters:
            funcOrTask: either an existing Task object (not already
                added to the task manager), or a callable function
                object. If this is a function, a new Task object will be
                created and returned. You may also pass in a coroutine
                object.

            name (str): the name to assign to the Task.  Required,
                unless you are passing in a Task object that already has
                a name.

            extraArgs (list): the list of arguments to pass to the task
                function.  If this is omitted, the list is just the task
                object itself.

            appendTask (bool): If this is true, then the task object
                itself will be appended to the end of the extraArgs list
                before calling the function.

            sort (int): the sort value to assign the task.  The default
                sort is 0.  Within a particular task chain, it is
                guaranteed that the tasks with a lower sort value will
                all run before tasks with a higher sort value run.

            priority (int): the priority at which to run the task.  The
                default priority is 0.  Higher priority tasks are run
                sooner, and/or more often.  For historical purposes, if
                you specify a priority without also specifying a sort,
                the priority value is understood to actually be a sort
                value. (Previously, there was no priority value, only a
                sort value, and it was called "priority".)

            uponDeath (bool): a function to call when the task
                terminates, either because it has run to completion, or
                because it has been explicitly removed.

            taskChain (str): the name of the task chain to assign the
                task to.

            owner: an optional Python object that is declared as the
                "owner" of this task for maintenance purposes.  The
                owner must have two methods:
                ``owner._addTask(self, task)``, which is called when the
                task begins, and ``owner._clearTask(self, task)``, which
                is called when the task terminates.  This is all the
                ownermeans.

            delay: an optional amount of seconds to wait before starting
                the task (equivalent to doMethodLater).

        Returns:
            The new Task object that has been added, or the original
            Task object that was passed in.
        """

        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
        if delay is not None:
            task.setDelay(delay)
        self.mgr.add(task)
        return task

    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
        if isinstance(funcOrTask, AsyncTask):
            task = funcOrTask
        elif hasattr(funcOrTask, '__call__') or \
             hasattr(funcOrTask, 'cr_await') or \
             isinstance(funcOrTask, types.GeneratorType):
            # It's a function, coroutine, or something emulating a coroutine.
            task = PythonTask(funcOrTask)
            if name is None:
                name = getattr(funcOrTask, '__qualname__', None) or \
                       getattr(funcOrTask, '__name__', None)
        else:
            self.notify.error(
                'add: Tried to add a task that was not a Task or a func')

        if hasattr(task, 'setArgs'):
            # It will only accept arguments if it's a PythonTask.
            if extraArgs is None:
                extraArgs = []
                appendTask = True
            task.setArgs(extraArgs, appendTask)
        elif extraArgs is not None:
            self.notify.error(
                'Task %s does not accept arguments.' % (repr(task)))

        if name is not None:
            task.setName(name)
        assert task.hasName()

        # For historical reasons, if priority is specified but not
        # sort, it really means sort.
        if priority is not None and sort is None:
            task.setSort(priority)
        else:
            if priority is not None:
                task.setPriority(priority)
            if sort is not None:
                task.setSort(sort)

        if taskChain is not None:
            task.setTaskChain(taskChain)

        if owner is not None:
            task.setOwner(owner)

        if uponDeath is not None:
            task.setUponDeath(uponDeath)

        return task

    def remove(self, taskOrName):
        """Removes a task from the task manager.  The task is stopped,
        almost as if it had returned task.done.  (But if the task is
        currently executing, it will finish out its current frame
        before being removed.)  You may specify either an explicit
        Task object, or the name of a task.  If you specify a name,
        all tasks with the indicated name are removed.  Returns the
        number of tasks removed. """

        if isinstance(taskOrName, AsyncTask):
            return self.mgr.remove(taskOrName)
        elif isinstance(taskOrName, list):
            for task in taskOrName:
                self.remove(task)
        else:
            tasks = self.mgr.findTasks(taskOrName)
            return self.mgr.remove(tasks)

    def removeTasksMatching(self, taskPattern):
        """Removes all tasks whose names match the pattern, which can
        include standard shell globbing characters like \\*, ?, and [].
        See also :meth:`remove()`.

        Returns the number of tasks removed.
        """
        tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
        return self.mgr.remove(tasks)

    def step(self):
        """Invokes the task manager for one frame, and then returns.
        Normally, this executes each task exactly once, though task
        chains that are in sub-threads or that have frame budgets
        might execute their tasks differently. """

        startFrameTime = self.globalClock.getRealTime()

        # Replace keyboard interrupt handler during task list processing
        # so we catch the keyboard interrupt but don't handle it until
        # after task list processing is complete.
        self.fKeyboardInterrupt = 0
        self.interruptCount = 0

        if signal:
            self.__prevHandler = signal.signal(signal.SIGINT, self.keyboardInterruptHandler)

        try:
            self.mgr.poll()

            # This is the spot for an internal yield function
            nextTaskTime = self.mgr.getNextWakeTime()
            self.doYield(startFrameTime, nextTaskTime)

        finally:
            # Restore previous interrupt handler
            if signal:
                signal.signal(signal.SIGINT, self.__prevHandler)
                self.__prevHandler = signal.default_int_handler

        if self.fKeyboardInterrupt:
            raise KeyboardInterrupt

    def run(self):
        """Starts the task manager running.  Does not return until an
        exception is encountered (including KeyboardInterrupt). """

        if PandaSystem.getPlatform() == 'emscripten':
            return

        # Set the clock to have last frame's time in case we were
        # Paused at the prompt for a long time
        t = self.globalClock.getFrameTime()
        timeDelta = t - self.globalClock.getRealTime()
        self.globalClock.setRealTime(t)
        messenger.send("resetClock", [timeDelta])

        if self.resumeFunc is not None:
            self.resumeFunc()

        if self.stepping:
            self.step()
        else:
            self.running = True
            while self.running:
                try:
                    if len(self._frameProfileQueue) > 0:
                        numFrames, session, callback = self._frameProfileQueue.pop(0)
                        def _profileFunc(numFrames=numFrames):
                            self._doProfiledFrames(numFrames)
                        session.setFunc(_profileFunc)
                        session.run()
                        _profileFunc = None
                        if callback:
                            callback()
                        session.release()
                    else:
                        self.step()
                except KeyboardInterrupt:
                    self.stop()
                except SystemExit:
                    self.stop()
                    raise
                except IOError as ioError:
                    code, message = self._unpackIOError(ioError)
                    # Since upgrading to Python 2.4.1, pausing the execution
                    # often gives this IOError during the sleep function:
                    #     IOError: [Errno 4] Interrupted function call
                    # So, let's just handle that specific exception and stop.
                    # All other IOErrors should still get raised.
                    # Only problem: legit IOError 4s will be obfuscated.
                    if code == 4:
                        self.stop()
                    else:
                        raise
                except Exception as e:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        from direct.showbase import ExceptionVarDump
                        if ExceptionVarDump.wantStackDumpLog and \
                           ExceptionVarDump.dumpOnExceptionInit:
                            ExceptionVarDump._varDump__print(e)
                        raise
                except:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        raise

        self.mgr.stopThreads()

    def _unpackIOError(self, ioError):
        # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
        # this needs to be in its own method, exceptions that occur inside
        # a nested try block are not caught by the inner try block's except
        try:
            (code, message) = ioError
        except:
            code = 0
            message = ioError
        return code, message

    def stop(self):
        # Set a flag so we will stop before beginning next frame
        self.running = False

    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
        if not isinstance(task, PythonTask):
            return 0

        method = task.getFunction()
        if isinstance(method, types.MethodType):
            function = method.__func__
        else:
            function = method
        if function == oldMethod:
            newMethod = types.MethodType(newFunction, method.__self__)
            task.setFunction(newMethod)
            # Found a match
            return 1
        return 0

    def replaceMethod(self, oldMethod, newFunction):
        numFound = 0
        for task in self.getAllTasks():
            numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
        return numFound

    def popupControls(self):
        # Don't use a regular import, to prevent ModuleFinder from picking
        # it up as a dependency when building a .p3d package.
        TaskManagerPanel = importlib.import_module('direct.tkpanels.TaskManagerPanel')
        return TaskManagerPanel.TaskManagerPanel(self)

    def getProfileSession(self, name=None):
        # call to get a profile session that you can modify before passing to profileFrames()
        if name is None:
            name = 'taskMgrFrameProfile'

        # Defer this import until we need it: some Python
        # distributions don't provide the profile and pstats modules.
        PS = importlib.import_module('direct.showbase.ProfileSession')
        return PS.ProfileSession(name)

    def profileFrames(self, num=None, session=None, callback=None):
        if num is None:
            num = 1
        if session is None:
            session = self.getProfileSession()
        # make sure the profile session doesn't get destroyed before we're done with it
        session.acquire()
        self._frameProfileQueue.append((num, session, callback))

    def _doProfiledFrames(self, numFrames):
        for i in range(numFrames):
            self.step()

    def getProfileFrames(self):
        return self._profileFrames.get()

    def getProfileFramesSV(self):
        return self._profileFrames

    def setProfileFrames(self, profileFrames):
        self._profileFrames.set(profileFrames)
        if (not self._frameProfiler) and profileFrames:
            # import here due to import dependencies
            FP = importlib.import_module('direct.task.FrameProfiler')
            self._frameProfiler = FP.FrameProfiler()

    def getProfileTasks(self):
        return self._profileTasks.get()

    def getProfileTasksSV(self):
        return self._profileTasks

    def setProfileTasks(self, profileTasks):
        self._profileTasks.set(profileTasks)
        if (not self._taskProfiler) and profileTasks:
            # import here due to import dependencies
            TP = importlib.import_module('direct.task.TaskProfiler')
            self._taskProfiler = TP.TaskProfiler()

    def logTaskProfiles(self, name=None):
        if self._taskProfiler:
            self._taskProfiler.logProfiles(name)

    def flushTaskProfiles(self, name=None):
        if self._taskProfiler:
            self._taskProfiler.flush(name)

    def _setProfileTask(self, task):
        if self._taskProfileInfo.session:
            self._taskProfileInfo.session.release()
            self._taskProfileInfo.session = None
        self._taskProfileInfo = ScratchPad(
            taskFunc = task.getFunction(),
            taskArgs = task.getArgs(),
            task = task,
            profiled = False,
            session = None,
            )

        # Temporarily replace the task's own function with our
        # _profileTask method.
        task.setFunction(self._profileTask)
        task.setArgs([self._taskProfileInfo], True)

    def _profileTask(self, profileInfo, task):
        # This is called instead of the task function when we have
        # decided to profile a task.

        assert profileInfo.task == task
        # don't profile the same task twice in a row
        assert not profileInfo.profiled

        # Restore the task's proper function for next time.
        appendTask = False
        taskArgs = profileInfo.taskArgs
        if taskArgs and taskArgs[-1] == task:
            appendTask = True
            taskArgs = taskArgs[:-1]
        task.setArgs(taskArgs, appendTask)
        task.setFunction(profileInfo.taskFunc)

        # Defer this import until we need it: some Python
        # distributions don't provide the profile and pstats modules.
        PS = importlib.import_module('direct.showbase.ProfileSession')
        profileSession = PS.ProfileSession('profiled-task-%s' % task.getName(),
                                           Functor(profileInfo.taskFunc, *profileInfo.taskArgs))
        ret = profileSession.run()

        # set these values *after* profiling in case we're profiling the TaskProfiler
        profileInfo.session = profileSession
        profileInfo.profiled = True

        return ret

    def _hasProfiledDesignatedTask(self):
        # have we run a profile of the designated task yet?
        return self._taskProfileInfo.profiled

    def _getLastTaskProfileSession(self):
        return self._taskProfileInfo.session

    def _getRandomTask(self):
        # Figure out when the next frame is likely to expire, so we
        # won't grab any tasks that are sleeping for a long time.
        now = self.globalClock.getFrameTime()
        avgFrameRate = self.globalClock.getAverageFrameRate()
        if avgFrameRate < .00001:
            avgFrameDur = 0.
        else:
            avgFrameDur = (1. / self.globalClock.getAverageFrameRate())
        next = now + avgFrameDur

        # Now grab a task at random, until we find one that we like.
        tasks = self.mgr.getTasks()
        i = random.randrange(tasks.getNumTasks())
        task = tasks.getTask(i)
        while not isinstance(task, PythonTask) or \
              task.getWakeTime() > next:
            tasks.removeTask(i)
            i = random.randrange(tasks.getNumTasks())
            task = tasks.getTask(i)
        return task

    def __repr__(self):
        return str(self.mgr)

    # In the event we want to do frame time managment, this is the
    # function to replace or overload.
    def doYield(self, frameStartTime, nextScheduledTaskTime):
        pass

    #def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
    #    minFinTime = frameStartTime + self.MaxEpochSpeed
    #    if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
    #        print(' Adjusting Time')
    #        minFinTime = nextScheduledTaskTime
    #    delta = minFinTime - self.globalClock.getRealTime()
    #    while delta > 0.002:
    #        print ' sleep %s'% (delta)
    #        time.sleep(delta)
    #        delta = minFinTime - self.globalClock.getRealTime()

    if __debug__:
        # to catch memory leaks during the tests at the bottom of the file
        def _startTrackingMemLeaks(self):
            pass

        def _stopTrackingMemLeaks(self):
            pass

        def _checkMemLeaks(self):
            pass

    def _runTests(self):
        if __debug__:
            tm = TaskManager()
            tm.setClock(ClockObject())
            tm.setupTaskChain("default", tickClock = True)

            # check for memory leaks after every test
            tm._startTrackingMemLeaks()
            tm._checkMemLeaks()

            # run-once task
            l = []
            def _testDone(task, l=l):
                l.append(None)
                return task.done
            tm.add(_testDone, 'testDone')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 1
            _testDone = None
            tm._checkMemLeaks()

            # remove by name
            def _testRemoveByName(task):
                return task.done
            tm.add(_testRemoveByName, 'testRemoveByName')
            assert tm.remove('testRemoveByName') == 1
            assert tm.remove('testRemoveByName') == 0
            _testRemoveByName = None
            tm._checkMemLeaks()

            # duplicate named tasks
            def _testDupNamedTasks(task):
                return task.done
            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
            assert tm.remove('testRemoveByName') == 0
            _testDupNamedTasks = None
            tm._checkMemLeaks()

            # continued task
            l = []
            def _testCont(task, l = l):
                l.append(None)
                return task.cont
            tm.add(_testCont, 'testCont')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            tm.remove('testCont')
            _testCont = None
            tm._checkMemLeaks()

            # continue until done task
            l = []
            def _testContDone(task, l = l):
                l.append(None)
                if len(l) >= 2:
                    return task.done
                else:
                    return task.cont
            tm.add(_testContDone, 'testContDone')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            tm.step()
            assert len(l) == 2
            assert not tm.hasTaskNamed('testContDone')
            _testContDone = None
            tm._checkMemLeaks()

            # hasTaskNamed
            def _testHasTaskNamed(task):
                return task.done
            tm.add(_testHasTaskNamed, 'testHasTaskNamed')
            assert tm.hasTaskNamed('testHasTaskNamed')
            tm.step()
            assert not tm.hasTaskNamed('testHasTaskNamed')
            _testHasTaskNamed = None
            tm._checkMemLeaks()

            # task sort
            l = []
            def _testPri1(task, l = l):
                l.append(1)
                return task.cont
            def _testPri2(task, l = l):
                l.append(2)
                return task.cont
            tm.add(_testPri1, 'testPri1', sort = 1)
            tm.add(_testPri2, 'testPri2', sort = 2)
            tm.step()
            assert len(l) == 2
            assert l == [1, 2,]
            tm.step()
            assert len(l) == 4
            assert l == [1, 2, 1, 2,]
            tm.remove('testPri1')
            tm.remove('testPri2')
            _testPri1 = None
            _testPri2 = None
            tm._checkMemLeaks()

            # task extraArgs
            l = []
            def _testExtraArgs(arg1, arg2, l=l):
                l.extend([arg1, arg2,])
                return done
            tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
            tm.step()
            assert len(l) == 2
            assert l == [4, 5,]
            _testExtraArgs = None
            tm._checkMemLeaks()

            # task appendTask
            l = []
            def _testAppendTask(arg1, arg2, task, l=l):
                l.extend([arg1, arg2,])
                return task.done
            tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
            tm.step()
            assert len(l) == 2
            assert l == [4, 5,]
            _testAppendTask = None
            tm._checkMemLeaks()

            # task uponDeath
            l = []
            def _uponDeathFunc(task, l=l):
                l.append(task.name)
            def _testUponDeath(task):
                return done
            tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
            tm.step()
            assert len(l) == 1
            assert l == ['testUponDeath']
            _testUponDeath = None
            _uponDeathFunc = None
            tm._checkMemLeaks()

            # task owner
            class _TaskOwner:
                def _addTask(self, task):
                    self.addedTaskName = task.name
                def _clearTask(self, task):
                    self.clearedTaskName = task.name
            to = _TaskOwner()
            l = []
            def _testOwner(task):
                return done
            tm.add(_testOwner, 'testOwner', owner=to)
            tm.step()
            assert getattr(to, 'addedTaskName', None) == 'testOwner'
            assert getattr(to, 'clearedTaskName', None) == 'testOwner'
            _testOwner = None
            del to
            _TaskOwner = None
            tm._checkMemLeaks()


            doLaterTests = [0,]

            # doLater
            l = []
            def _testDoLater1(task, l=l):
                l.append(1)
            def _testDoLater2(task, l=l):
                l.append(2)
            def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .03:
                    assert l == [1, 2,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
            tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
            _testDoLater1 = None
            _testDoLater2 = None
            _monitorDoLater = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater sort
            l = []
            def _testDoLaterPri1(task, l=l):
                l.append(1)
            def _testDoLaterPri2(task, l=l):
                l.append(2)
            def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [1, 2,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
            tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
            _testDoLaterPri1 = None
            _testDoLaterPri2 = None
            _monitorDoLaterPri = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater extraArgs
            l = []
            def _testDoLaterExtraArgs(arg1, l=l):
                l.append(arg1)
            def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [3,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
            _testDoLaterExtraArgs = None
            _monitorDoLaterExtraArgs = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater appendTask
            l = []
            def _testDoLaterAppendTask(arg1, task, l=l):
                assert task.name == 'testDoLaterAppendTask'
                l.append(arg1)
            def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [4,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
                             extraArgs=[4,], appendTask=True)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
            _testDoLaterAppendTask = None
            _monitorDoLaterAppendTask = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater uponDeath
            l = []
            def _testUponDeathFunc(task, l=l):
                assert task.name == 'testDoLaterUponDeath'
                l.append(10)
            def _testDoLaterUponDeath(arg1, l=l):
                return done
            def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [10,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
                             uponDeath=_testUponDeathFunc)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
            _testUponDeathFunc = None
            _testDoLaterUponDeath = None
            _monitorDoLaterUponDeath = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater owner
            class _DoLaterOwner:
                def _addTask(self, task):
                    self.addedTaskName = task.name
                def _clearTask(self, task):
                    self.clearedTaskName = task.name
            doLaterOwner = _DoLaterOwner()
            l = []
            def _testDoLaterOwner(l=l):
                pass
            def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
                                     doLaterTests=doLaterTests):
                if task.time > .02:
                    assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
                    assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
                             owner=doLaterOwner)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
            _testDoLaterOwner = None
            _monitorDoLaterOwner = None
            del doLaterOwner
            _DoLaterOwner = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # run the doLater tests
            while doLaterTests[0] > 0:
                tm.step()
            del doLaterTests
            tm._checkMemLeaks()

            # getTasks
            def _testGetTasks(task):
                return task.cont
            # No doLaterProcessor in the new world.
            assert len(tm.getTasks()) == 0
            tm.add(_testGetTasks, 'testGetTasks1')
            assert len(tm.getTasks()) == 1
            assert (tm.getTasks()[0].name == 'testGetTasks1' or
                    tm.getTasks()[1].name == 'testGetTasks1')
            tm.add(_testGetTasks, 'testGetTasks2')
            tm.add(_testGetTasks, 'testGetTasks3')
            assert len(tm.getTasks()) == 3
            tm.remove('testGetTasks2')
            assert len(tm.getTasks()) == 2
            tm.remove('testGetTasks1')
            tm.remove('testGetTasks3')
            assert len(tm.getTasks()) == 0
            _testGetTasks = None
            tm._checkMemLeaks()

            # getDoLaters
            def _testGetDoLaters():
                pass
            assert len(tm.getDoLaters()) == 0
            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
            assert len(tm.getDoLaters()) == 1
            assert tm.getDoLaters()[0].name == 'testDoLater1'
            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
            assert len(tm.getDoLaters()) == 3
            tm.remove('testDoLater2')
            assert len(tm.getDoLaters()) == 2
            tm.remove('testDoLater1')
            tm.remove('testDoLater3')
            assert len(tm.getDoLaters()) == 0
            _testGetDoLaters = None
            tm._checkMemLeaks()

            # duplicate named doLaters removed via taskMgr.remove
            def _testDupNameDoLaters():
                pass
            # the doLaterProcessor is always running
            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
            assert len(tm.getDoLaters()) == 2
            tm.remove('testDupNameDoLater')
            assert len(tm.getDoLaters()) == 0
            _testDupNameDoLaters = None
            tm._checkMemLeaks()

            # duplicate named doLaters removed via remove()
            def _testDupNameDoLatersRemove():
                pass
            # the doLaterProcessor is always running
            dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
            dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
            assert len(tm.getDoLaters()) == 2
            dl2.remove()
            assert len(tm.getDoLaters()) == 1
            dl1.remove()
            assert len(tm.getDoLaters()) == 0
            _testDupNameDoLatersRemove = None
            # nameDict etc. isn't cleared out right away with task.remove()
            tm._checkMemLeaks()

            # getTasksNamed
            def _testGetTasksNamed(task):
                return task.cont
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
            assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
            tm.remove('testGetTasksNamed')
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
            _testGetTasksNamed = None
            tm._checkMemLeaks()

            # removeTasksMatching
            def _testRemoveTasksMatching(task):
                return task.cont
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
            tm.removeTasksMatching('testRemoveTasksMatching')
            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
            tm.removeTasksMatching('testRemoveTasksMatching*')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
            tm.removeTasksMatching('testRemoveTasksMatching?a')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
            _testRemoveTasksMatching = None
            tm._checkMemLeaks()

            # create Task object and add to mgr
            l = []
            def _testTaskObj(task, l=l):
                l.append(None)
                return task.cont
            t = Task(_testTaskObj)
            tm.add(t, 'testTaskObj')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            tm.remove('testTaskObj')
            tm.step()
            assert len(l) == 2
            _testTaskObj = None
            tm._checkMemLeaks()

            # remove Task via task.remove()
            l = []
            def _testTaskObjRemove(task, l=l):
                l.append(None)
                return task.cont
            t = Task(_testTaskObjRemove)
            tm.add(t, 'testTaskObjRemove')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            t.remove()
            tm.step()
            assert len(l) == 2
            del t
            _testTaskObjRemove = None
            tm._checkMemLeaks()

            # this test fails, and it's not clear what the correct behavior should be.
            # sort passed to Task.__init__ is always overridden by taskMgr.add()
            # even if no sort is specified, and calling Task.setSort() has no
            # effect on the taskMgr's behavior.
            # set/get Task sort
            #l = []
            #def _testTaskObjSort(arg, task, l=l):
            #    l.append(arg)
            #    return task.cont
            #t1 = Task(_testTaskObjSort, sort=1)
            #t2 = Task(_testTaskObjSort, sort=2)
            #tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
            #tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
            #tm.step()
            #assert len(l) == 2
            #assert l == ['a', 'b']
            #assert t1.getSort() == 1
            #assert t2.getSort() == 2
            #t1.setSort(3)
            #assert t1.getSort() == 3
            #tm.step()
            #assert len(l) == 4
            #assert l == ['a', 'b', 'b', 'a',]
            #t1.remove()
            #t2.remove()
            #tm.step()
            #assert len(l) == 4
            #del t1
            #del t2
            #_testTaskObjSort = None
            #tm._checkMemLeaks()

            del l
            tm.destroy()
            del tm
class TaskManager():
    notify = directNotify.newCategory('TaskManager')
    extendedExceptions = False
    MaxEpochSpeed = 1.0 / 30.0

    def __init__(self):
        self.mgr = AsyncTaskManager.getGlobalPtr()
        self.resumeFunc = None
        self.globalClock = self.mgr.getClock()
        self.stepping = False
        self.running = False
        self.destroyed = False
        self.fKeyboardInterrupt = False
        self.interruptCount = 0
        self._frameProfileQueue = Queue()
        self._profileFrames = None
        self._frameProfiler = None
        self._profileTasks = None
        self._taskProfiler = None
        self._taskProfileInfo = ScratchPad(taskId=None,
                                           profiled=False,
                                           session=None)
        return

    def finalInit(self):
        from direct.fsm.StatePush import StateVar
        self._profileTasks = StateVar(False)
        self.setProfileTasks(
            ConfigVariableBool('profile-task-spikes', 0).getValue())
        self._profileFrames = StateVar(False)
        self.setProfileFrames(
            ConfigVariableBool('profile-frames', 0).getValue())

    def destroy(self):
        self.running = False
        self.notify.info('TaskManager.destroy()')
        self.destroyed = True
        self._frameProfileQueue.clear()
        self.mgr.cleanup()

    def setClock(self, clockObject):
        self.mgr.setClock(clockObject)
        self.globalClock = clockObject

    def invokeDefaultHandler(self, signalNumber, stackFrame):
        print '*** allowing mid-frame keyboard interrupt.'
        if signal:
            signal.signal(signal.SIGINT, signal.default_int_handler)
        raise KeyboardInterrupt

    def keyboardInterruptHandler(self, signalNumber, stackFrame):
        self.fKeyboardInterrupt = 1
        self.interruptCount += 1
        if self.interruptCount == 1:
            print '* interrupt by keyboard'
        else:
            if self.interruptCount == 2:
                print '** waiting for end of frame before interrupting...'
                signal.signal(signal.SIGINT, self.invokeDefaultHandler)

    def getCurrentTask(self):
        return Thread.getCurrentThread().getCurrentTask()

    def hasTaskChain(self, chainName):
        return self.mgr.findTaskChain(chainName) != None

    def setupTaskChain(self,
                       chainName,
                       numThreads=None,
                       tickClock=None,
                       threadPriority=None,
                       frameBudget=None,
                       frameSync=None,
                       timeslicePriority=None):
        chain = self.mgr.makeTaskChain(chainName)
        if numThreads is not None:
            chain.setNumThreads(numThreads)
        if tickClock is not None:
            chain.setTickClock(tickClock)
        if threadPriority is not None:
            chain.setThreadPriority(threadPriority)
        if frameBudget is not None:
            chain.setFrameBudget(frameBudget)
        if frameSync is not None:
            chain.setFrameSync(frameSync)
        if timeslicePriority is not None:
            chain.setTimeslicePriority(timeslicePriority)
        return

    def hasTaskNamed(self, taskName):
        return bool(self.mgr.findTask(taskName))

    def getTasksNamed(self, taskName):
        return self.__makeTaskList(self.mgr.findTasks(taskName))

    def getTasksMatching(self, taskPattern):
        return self.__makeTaskList(
            self.mgr.findTasksMatching(GlobPattern(taskPattern)))

    def getAllTasks(self):
        return self.__makeTaskList(self.mgr.getTasks())

    def getTasks(self):
        return self.__makeTaskList(self.mgr.getActiveTasks())

    def getDoLaters(self):
        return self.__makeTaskList(self.mgr.getSleepingTasks())

    def __makeTaskList(self, taskCollection):
        l = []
        for i in range(taskCollection.getNumTasks()):
            l.append(taskCollection.getTask(i))

        return l

    def doMethodLater(self,
                      delayTime,
                      funcOrTask,
                      name,
                      extraArgs=None,
                      sort=None,
                      priority=None,
                      taskChain=None,
                      uponDeath=None,
                      appendTask=False,
                      owner=None):
        if delayTime < 0:
            pass
        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs,
                                taskChain, appendTask, owner, uponDeath)
        task.setDelay(delayTime)
        self.mgr.add(task)
        return task

    def add(self,
            funcOrTask,
            name=None,
            sort=None,
            extraArgs=None,
            priority=None,
            uponDeath=None,
            appendTask=False,
            taskChain=None,
            owner=None):
        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs,
                                taskChain, appendTask, owner, uponDeath)
        self.mgr.add(task)
        return task

    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs,
                    taskChain, appendTask, owner, uponDeath):
        if isinstance(funcOrTask, AsyncTask):
            task = funcOrTask
        else:
            if hasattr(funcOrTask, '__call__'):
                task = PythonTask(funcOrTask)
            else:
                self.notify.error(
                    'add: Tried to add a task that was not a Task or a func')
        if hasattr(task, 'setArgs'):
            if extraArgs is None:
                extraArgs = []
                appendTask = True
            task.setArgs(extraArgs, appendTask)
        else:
            if extraArgs is not None:
                self.notify.error('Task %s does not accept arguments.' %
                                  repr(task))
        if name is not None:
            task.setName(name)
        if priority is not None and sort is None:
            task.setSort(priority)
        else:
            if priority is not None:
                task.setPriority(priority)
            if sort is not None:
                task.setSort(sort)
        if taskChain is not None:
            task.setTaskChain(taskChain)
        if owner is not None:
            task.setOwner(owner)
        if uponDeath is not None:
            task.setUponDeath(uponDeath)
        return task

    def remove(self, taskOrName):
        if isinstance(taskOrName, types.StringTypes):
            tasks = self.mgr.findTasks(taskOrName)
            return self.mgr.remove(tasks)
        if isinstance(taskOrName, AsyncTask):
            return self.mgr.remove(taskOrName)
        if isinstance(taskOrName, types.ListType):
            for task in taskOrName:
                self.remove(task)

        else:
            self.notify.error('remove takes a string or a Task')

    def removeTasksMatching(self, taskPattern):
        tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
        return self.mgr.remove(tasks)

    def step(self):
        self.fKeyboardInterrupt = 0
        self.interruptCount = 0
        if signal:
            signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
        startFrameTime = self.globalClock.getRealTime()
        self.mgr.poll()
        nextTaskTime = self.mgr.getNextWakeTime()
        self.doYield(startFrameTime, nextTaskTime)
        if signal:
            signal.signal(signal.SIGINT, signal.default_int_handler)
        if self.fKeyboardInterrupt:
            raise KeyboardInterrupt

    def run(self):
        t = self.globalClock.getFrameTime()
        timeDelta = t - self.globalClock.getRealTime()
        self.globalClock.setRealTime(t)
        messenger.send('resetClock', [timeDelta])
        if self.resumeFunc != None:
            self.resumeFunc()
        if self.stepping:
            self.step()
        else:
            self.running = True
            while self.running:
                try:
                    if len(self._frameProfileQueue):
                        numFrames, session, callback = self._frameProfileQueue.pop(
                        )

                        def _profileFunc(numFrames=numFrames):
                            self._doProfiledFrames(numFrames)

                        session.setFunc(_profileFunc)
                        session.run()
                        _profileFunc = None
                        if callback:
                            callback()
                        session.release()
                    else:
                        self.step()
                except KeyboardInterrupt:
                    self.stop()
                except SystemExit:
                    self.stop()
                    raise
                except IOError as ioError:
                    code, message = self._unpackIOError(ioError)
                    if code == 4:
                        self.stop()
                    else:
                        raise
                except Exception as e:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        if ExceptionVarDump.wantStackDumpLog and ExceptionVarDump.dumpOnExceptionInit:
                            ExceptionVarDump._varDump__print(e)
                        raise
                except:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        raise

        self.mgr.stopThreads()
        return

    def _unpackIOError(self, ioError):
        try:
            code, message = ioError
        except:
            code = 0
            message = ioError

        return (code, message)

    def stop(self):
        self.running = False

    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
        if not isinstance(task, PythonTask):
            return 0
        method = task.getFunction()
        if type(method) == types.MethodType:
            function = method.im_func
        else:
            function = method
        if function == oldMethod:
            newMethod = types.MethodType(newFunction, method.im_self,
                                         method.im_class)
            task.setFunction(newMethod)
            return 1
        return 0

    def replaceMethod(self, oldMethod, newFunction):
        numFound = 0
        for task in self.getAllTasks():
            numFound += self.__tryReplaceTaskMethod(task, oldMethod,
                                                    newFunction)

        return numFound

    def popupControls(self):
        from direct.tkpanels import TaskManagerPanel
        return TaskManagerPanel.TaskManagerPanel(self)

    def getProfileSession(self, name=None):
        if name is None:
            name = 'taskMgrFrameProfile'
        from direct.showbase.ProfileSession import ProfileSession
        return ProfileSession(name)

    def profileFrames(self, num=None, session=None, callback=None):
        if num is None:
            num = 1
        if session is None:
            session = self.getProfileSession()
        session.acquire()
        self._frameProfileQueue.push((num, session, callback))
        return

    def _doProfiledFrames(self, numFrames):
        for i in xrange(numFrames):
            result = self.step()

        return result

    def getProfileFrames(self):
        return self._profileFrames.get()

    def getProfileFramesSV(self):
        return self._profileFrames

    def setProfileFrames(self, profileFrames):
        self._profileFrames.set(profileFrames)
        if not self._frameProfiler and profileFrames:
            from direct.task.FrameProfiler import FrameProfiler
            self._frameProfiler = FrameProfiler()

    def getProfileTasks(self):
        return self._profileTasks.get()

    def getProfileTasksSV(self):
        return self._profileTasks

    def setProfileTasks(self, profileTasks):
        self._profileTasks.set(profileTasks)
        if not self._taskProfiler and profileTasks:
            from direct.task.TaskProfiler import TaskProfiler
            self._taskProfiler = TaskProfiler()

    def logTaskProfiles(self, name=None):
        if self._taskProfiler:
            self._taskProfiler.logProfiles(name)

    def flushTaskProfiles(self, name=None):
        if self._taskProfiler:
            self._taskProfiler.flush(name)

    def _setProfileTask(self, task):
        if self._taskProfileInfo.session:
            self._taskProfileInfo.session.release()
            self._taskProfileInfo.session = None
        self._taskProfileInfo = ScratchPad(taskFunc=task.getFunction(),
                                           taskArgs=task.getArgs(),
                                           task=task,
                                           profiled=False,
                                           session=None)
        task.setFunction(self._profileTask)
        task.setArgs([self._taskProfileInfo], True)
        return

    def _profileTask(self, profileInfo, task):
        appendTask = False
        taskArgs = profileInfo.taskArgs
        if taskArgs and taskArgs[-1] == task:
            appendTask = True
            taskArgs = taskArgs[:-1]
        task.setArgs(taskArgs, appendTask)
        task.setFunction(profileInfo.taskFunc)
        from direct.showbase.ProfileSession import ProfileSession
        profileSession = ProfileSession(
            'profiled-task-%s' % task.getName(),
            Functor(profileInfo.taskFunc, *profileInfo.taskArgs))
        ret = profileSession.run()
        profileInfo.session = profileSession
        profileInfo.profiled = True
        return ret

    def _hasProfiledDesignatedTask(self):
        return self._taskProfileInfo.profiled

    def _getLastTaskProfileSession(self):
        return self._taskProfileInfo.session

    def _getRandomTask(self):
        now = globalClock.getFrameTime()
        avgFrameRate = globalClock.getAverageFrameRate()
        if avgFrameRate < 1e-05:
            avgFrameDur = 0.0
        else:
            avgFrameDur = 1.0 / globalClock.getAverageFrameRate()
        next = now + avgFrameDur
        tasks = self.mgr.getTasks()
        i = random.randrange(tasks.getNumTasks())
        task = tasks.getTask(i)
        while not isinstance(task, PythonTask) or task.getWakeTime() > next:
            tasks.removeTask(i)
            i = random.randrange(tasks.getNumTasks())
            task = tasks.getTask(i)

        return task

    def __repr__(self):
        return str(self.mgr)

    def doYield(self, frameStartTime, nextScheduledTaskTime):
        pass

    def _runTests(self):
        pass
Example #25
0
class TaskManager:
    notify = directNotify.newCategory("TaskManager")

    extendedExceptions = False
    MaxEpochSpeed = 1.0/30.0

    def __init__(self):
        self.mgr = AsyncTaskManager.getGlobalPtr()

        self.resumeFunc = None
        self.globalClock = self.mgr.getClock()
        self.stepping = False
        self.running = False
        self.destroyed = False
        self.fKeyboardInterrupt = False
        self.interruptCount = 0

        self._frameProfileQueue = Queue()

        # this will be set when it's safe to import StateVar
        self._profileFrames = None
        self._frameProfiler = None
        self._profileTasks = None
        self._taskProfiler = None
        self._taskProfileInfo = ScratchPad(
            taskId = None,
            profiled = False,
            session = None,
            )

    def finalInit(self):
        # This function should be called once during startup, after
        # most things are imported.
        from direct.fsm.StatePush import StateVar
        self._profileTasks = StateVar(False)
        self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
        self._profileFrames = StateVar(False)
        self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())

    def destroy(self):
        # This should be safe to call multiple times.
        self.running = False
        self.notify.info("TaskManager.destroy()")
        self.destroyed = True
        self._frameProfileQueue.clear()
        self.mgr.cleanup()

    def setClock(self, clockObject):
        self.mgr.setClock(clockObject)
        self.globalClock = clockObject

    clock = property(lambda self: self.mgr.getClock(), setClock)

    def invokeDefaultHandler(self, signalNumber, stackFrame):
        print('*** allowing mid-frame keyboard interrupt.')
        # Restore default interrupt handler
        if signal:
            signal.signal(signal.SIGINT, signal.default_int_handler)
        # and invoke it
        raise KeyboardInterrupt

    def keyboardInterruptHandler(self, signalNumber, stackFrame):
        self.fKeyboardInterrupt = 1
        self.interruptCount += 1
        if self.interruptCount == 1:
            print('* interrupt by keyboard')
        elif self.interruptCount == 2:
            print('** waiting for end of frame before interrupting...')
            # The user must really want to interrupt this process
            # Next time around invoke the default handler
            signal.signal(signal.SIGINT, self.invokeDefaultHandler)

    def getCurrentTask(self):
        """ Returns the task currently executing on this thread, or
        None if this is being called outside of the task manager. """

        return Thread.getCurrentThread().getCurrentTask()

    def hasTaskChain(self, chainName):
        """ Returns true if a task chain with the indicated name has
        already been defined, or false otherwise.  Note that
        setupTaskChain() will implicitly define a task chain if it has
        not already been defined, or modify an existing one if it has,
        so in most cases there is no need to check this method
        first. """

        return (self.mgr.findTaskChain(chainName) != None)

    def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
                       threadPriority = None, frameBudget = None,
                       frameSync = None, timeslicePriority = None):
        """Defines a new task chain.  Each task chain executes tasks
        potentially in parallel with all of the other task chains (if
        numThreads is more than zero).  When a new task is created, it
        may be associated with any of the task chains, by name (or you
        can move a task to another task chain with
        task.setTaskChain()).  You can have any number of task chains,
        but each must have a unique name.

        numThreads is the number of threads to allocate for this task
        chain.  If it is 1 or more, then the tasks on this task chain
        will execute in parallel with the tasks on other task chains.
        If it is greater than 1, then the tasks on this task chain may
        execute in parallel with themselves (within tasks of the same
        sort value).

        If tickClock is True, then this task chain will be responsible
        for ticking the global clock each frame (and thereby
        incrementing the frame counter).  There should be just one
        task chain responsible for ticking the clock, and usually it
        is the default, unnamed task chain.

        threadPriority specifies the priority level to assign to
        threads on this task chain.  It may be one of TPLow, TPNormal,
        TPHigh, or TPUrgent.  This is passed to the underlying
        threading system to control the way the threads are scheduled.

        frameBudget is the maximum amount of time (in seconds) to
        allow this task chain to run per frame.  Set it to -1 to mean
        no limit (the default).  It's not directly related to
        threadPriority.

        frameSync is true to force the task chain to sync to the
        clock.  When this flag is false, the default, the task chain
        will finish all of its tasks and then immediately start from
        the first task again, regardless of the clock frame.  When it
        is true, the task chain will finish all of its tasks and then
        wait for the clock to tick to the next frame before resuming
        the first task.  This only makes sense for threaded tasks
        chains; non-threaded task chains are automatically
        synchronous.

        timeslicePriority is False in the default mode, in which each
        task runs exactly once each frame, round-robin style,
        regardless of the task's priority value; or True to change the
        meaning of priority so that certain tasks are run less often,
        in proportion to their time used and to their priority value.
        See AsyncTaskManager.setTimeslicePriority() for more.
        """

        chain = self.mgr.makeTaskChain(chainName)
        if numThreads is not None:
            chain.setNumThreads(numThreads)
        if tickClock is not None:
            chain.setTickClock(tickClock)
        if threadPriority is not None:
            chain.setThreadPriority(threadPriority)
        if frameBudget is not None:
            chain.setFrameBudget(frameBudget)
        if frameSync is not None:
            chain.setFrameSync(frameSync)
        if timeslicePriority is not None:
            chain.setTimeslicePriority(timeslicePriority)

    def hasTaskNamed(self, taskName):
        """Returns true if there is at least one task, active or
        sleeping, with the indicated name. """

        return bool(self.mgr.findTask(taskName))

    def getTasksNamed(self, taskName):
        """Returns a list of all tasks, active or sleeping, with the
        indicated name. """
        return self.__makeTaskList(self.mgr.findTasks(taskName))

    def getTasksMatching(self, taskPattern):
        """Returns a list of all tasks, active or sleeping, with a
        name that matches the pattern, which can include standard
        shell globbing characters like *, ?, and []. """

        return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))

    def getAllTasks(self):
        """Returns list of all tasks, active and sleeping, in
        arbitrary order. """
        return self.__makeTaskList(self.mgr.getTasks())

    def getTasks(self):
        """Returns list of all active tasks in arbitrary order. """
        return self.__makeTaskList(self.mgr.getActiveTasks())

    def getDoLaters(self):
        """Returns list of all sleeping tasks in arbitrary order. """
        return self.__makeTaskList(self.mgr.getSleepingTasks())

    def __makeTaskList(self, taskCollection):
        l = []
        for i in range(taskCollection.getNumTasks()):
            l.append(taskCollection.getTask(i))
        return l

    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
                      sort = None, priority = None, taskChain = None,
                      uponDeath = None, appendTask = False, owner = None):

        """Adds a task to be performed at some time in the future.
        This is identical to add(), except that the specified
        delayTime is applied to the Task object first, which means
        that the task will not begin executing until at least the
        indicated delayTime (in seconds) has elapsed.

        After delayTime has elapsed, the task will become active, and
        will run in the soonest possible frame thereafter.  If you
        wish to specify a task that will run in the next frame, use a
        delayTime of 0.
        """

        if delayTime < 0:
            assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))

        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
        task.setDelay(delayTime)
        self.mgr.add(task)
        return task

    do_method_later = doMethodLater

    def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
            priority = None, uponDeath = None, appendTask = False,
            taskChain = None, owner = None):

        """
        Add a new task to the taskMgr.  The task will begin executing
        immediately, or next frame if its sort value has already
        passed this frame.

        The parameters are:

        funcOrTask - either an existing Task object (not already added
        to the task manager), or a callable function object.  If this
        is a function, a new Task object will be created and returned.

        name - the name to assign to the Task.  Required, unless you
        are passing in a Task object that already has a name.

        extraArgs - the list of arguments to pass to the task
        function.  If this is omitted, the list is just the task
        object itself.

        appendTask - a boolean flag.  If this is true, then the task
        object itself will be appended to the end of the extraArgs
        list before calling the function.

        sort - the sort value to assign the task.  The default sort is
        0.  Within a particular task chain, it is guaranteed that the
        tasks with a lower sort value will all run before tasks with a
        higher sort value run.

        priority - the priority at which to run the task.  The default
        priority is 0.  Higher priority tasks are run sooner, and/or
        more often.  For historical purposes, if you specify a
        priority without also specifying a sort, the priority value is
        understood to actually be a sort value.  (Previously, there
        was no priority value, only a sort value, and it was called
        "priority".)

        uponDeath - a function to call when the task terminates,
        either because it has run to completion, or because it has
        been explicitly removed.

        taskChain - the name of the task chain to assign the task to.

        owner - an optional Python object that is declared as the
        "owner" of this task for maintenance purposes.  The owner must
        have two methods: owner._addTask(self, task), which is called
        when the task begins, and owner._clearTask(self, task), which
        is called when the task terminates.  This is all the owner
        means.

        The return value of add() is the new Task object that has been
        added, or the original Task object that was passed in.

        """

        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
        self.mgr.add(task)
        return task

    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
        if isinstance(funcOrTask, AsyncTask):
            task = funcOrTask
        elif hasattr(funcOrTask, '__call__'):
            task = PythonTask(funcOrTask)
        else:
            self.notify.error(
                'add: Tried to add a task that was not a Task or a func')

        if hasattr(task, 'setArgs'):
            # It will only accept arguments if it's a PythonTask.
            if extraArgs is None:
                extraArgs = []
                appendTask = True
            task.setArgs(extraArgs, appendTask)
        elif extraArgs is not None:
            self.notify.error(
                'Task %s does not accept arguments.' % (repr(task)))

        if name is not None:
            task.setName(name)
        assert task.hasName()

        # For historical reasons, if priority is specified but not
        # sort, it really means sort.
        if priority is not None and sort is None:
            task.setSort(priority)
        else:
            if priority is not None:
                task.setPriority(priority)
            if sort is not None:
                task.setSort(sort)

        if taskChain is not None:
            task.setTaskChain(taskChain)

        if owner is not None:
            task.setOwner(owner)

        if uponDeath is not None:
            task.setUponDeath(uponDeath)

        return task

    def remove(self, taskOrName):
        """Removes a task from the task manager.  The task is stopped,
        almost as if it had returned task.done.  (But if the task is
        currently executing, it will finish out its current frame
        before being removed.)  You may specify either an explicit
        Task object, or the name of a task.  If you specify a name,
        all tasks with the indicated name are removed.  Returns the
        number of tasks removed. """

        if isinstance(taskOrName, AsyncTask):
            return self.mgr.remove(taskOrName)
        elif isinstance(taskOrName, list):
            for task in taskOrName:
                self.remove(task)
        else:
            tasks = self.mgr.findTasks(taskOrName)
            return self.mgr.remove(tasks)

    def removeTasksMatching(self, taskPattern):
        """Removes all tasks whose names match the pattern, which can
        include standard shell globbing characters like *, ?, and [].
        See also remove().

        Returns the number of tasks removed.
        """
        tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
        return self.mgr.remove(tasks)

    def step(self):
        """Invokes the task manager for one frame, and then returns.
        Normally, this executes each task exactly once, though task
        chains that are in sub-threads or that have frame budgets
        might execute their tasks differently. """

        # Replace keyboard interrupt handler during task list processing
        # so we catch the keyboard interrupt but don't handle it until
        # after task list processing is complete.
        self.fKeyboardInterrupt = 0
        self.interruptCount = 0
        if signal:
            signal.signal(signal.SIGINT, self.keyboardInterruptHandler)

        startFrameTime = self.globalClock.getRealTime()

        self.mgr.poll()

        # This is the spot for an internal yield function
        nextTaskTime = self.mgr.getNextWakeTime()
        self.doYield(startFrameTime, nextTaskTime)

        # Restore default interrupt handler
        if signal:
            signal.signal(signal.SIGINT, signal.default_int_handler)
        if self.fKeyboardInterrupt:
            raise KeyboardInterrupt

    def run(self):
        """Starts the task manager running.  Does not return until an
        exception is encountered (including KeyboardInterrupt). """

        if PandaSystem.getPlatform() == 'emscripten':
            return

        # Set the clock to have last frame's time in case we were
        # Paused at the prompt for a long time
        t = self.globalClock.getFrameTime()
        timeDelta = t - self.globalClock.getRealTime()
        self.globalClock.setRealTime(t)
        messenger.send("resetClock", [timeDelta])

        if self.resumeFunc != None:
            self.resumeFunc()

        if self.stepping:
            self.step()
        else:
            self.running = True
            while self.running:
                try:
                    if len(self._frameProfileQueue):
                        numFrames, session, callback = self._frameProfileQueue.pop()
                        def _profileFunc(numFrames=numFrames):
                            self._doProfiledFrames(numFrames)
                        session.setFunc(_profileFunc)
                        session.run()
                        _profileFunc = None
                        if callback:
                            callback()
                        session.release()
                    else:
                        self.step()
                except KeyboardInterrupt:
                    self.stop()
                except SystemExit:
                    self.stop()
                    raise
                except IOError as ioError:
                    code, message = self._unpackIOError(ioError)
                    # Since upgrading to Python 2.4.1, pausing the execution
                    # often gives this IOError during the sleep function:
                    #     IOError: [Errno 4] Interrupted function call
                    # So, let's just handle that specific exception and stop.
                    # All other IOErrors should still get raised.
                    # Only problem: legit IOError 4s will be obfuscated.
                    if code == 4:
                        self.stop()
                    else:
                        raise
                except Exception as e:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        if (ExceptionVarDump.wantStackDumpLog and
                            ExceptionVarDump.dumpOnExceptionInit):
                            ExceptionVarDump._varDump__print(e)
                        raise
                except:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        raise

        self.mgr.stopThreads()

    def _unpackIOError(self, ioError):
        # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
        # this needs to be in its own method, exceptions that occur inside
        # a nested try block are not caught by the inner try block's except
        try:
            (code, message) = ioError
        except:
            code = 0
            message = ioError
        return code, message

    def stop(self):
        # Set a flag so we will stop before beginning next frame
        self.running = False

    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
        if not isinstance(task, PythonTask):
            return 0

        method = task.getFunction()
        if (type(method) == types.MethodType):
            function = method.__func__
        else:
            function = method
        if (function == oldMethod):
            newMethod = types.MethodType(newFunction,
                                         method.__self__,
                                         method.__self__.__class__)
            task.setFunction(newMethod)
            # Found a match
            return 1
        return 0

    def replaceMethod(self, oldMethod, newFunction):
        numFound = 0
        for task in self.getAllTasks():
            numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
        return numFound

    def popupControls(self):
        # Don't use a regular import, to prevent ModuleFinder from picking
        # it up as a dependency when building a .p3d package.
        TaskManagerPanel = importlib.import_module('direct.tkpanels.TaskManagerPanel')
        return TaskManagerPanel.TaskManagerPanel(self)

    def getProfileSession(self, name=None):
        # call to get a profile session that you can modify before passing to profileFrames()
        if name is None:
            name = 'taskMgrFrameProfile'

        # Defer this import until we need it: some Python
        # distributions don't provide the profile and pstats modules.
        PS = importlib.import_module('direct.showbase.ProfileSession')
        return PS.ProfileSession(name)

    def profileFrames(self, num=None, session=None, callback=None):
        if num is None:
            num = 1
        if session is None:
            session = self.getProfileSession()
        # make sure the profile session doesn't get destroyed before we're done with it
        session.acquire()
        self._frameProfileQueue.push((num, session, callback))

    def _doProfiledFrames(self, numFrames):
        for i in range(numFrames):
            result = self.step()
        return result

    def getProfileFrames(self):
        return self._profileFrames.get()

    def getProfileFramesSV(self):
        return self._profileFrames

    def setProfileFrames(self, profileFrames):
        self._profileFrames.set(profileFrames)
        if (not self._frameProfiler) and profileFrames:
            # import here due to import dependencies
            FP = importlib.import_module('direct.task.FrameProfiler')
            self._frameProfiler = FP.FrameProfiler()

    def getProfileTasks(self):
        return self._profileTasks.get()

    def getProfileTasksSV(self):
        return self._profileTasks

    def setProfileTasks(self, profileTasks):
        self._profileTasks.set(profileTasks)
        if (not self._taskProfiler) and profileTasks:
            # import here due to import dependencies
            TP = importlib.import_module('direct.task.TaskProfiler')
            self._taskProfiler = TP.TaskProfiler()

    def logTaskProfiles(self, name=None):
        if self._taskProfiler:
            self._taskProfiler.logProfiles(name)

    def flushTaskProfiles(self, name=None):
        if self._taskProfiler:
            self._taskProfiler.flush(name)

    def _setProfileTask(self, task):
        if self._taskProfileInfo.session:
            self._taskProfileInfo.session.release()
            self._taskProfileInfo.session = None
        self._taskProfileInfo = ScratchPad(
            taskFunc = task.getFunction(),
            taskArgs = task.getArgs(),
            task = task,
            profiled = False,
            session = None,
            )

        # Temporarily replace the task's own function with our
        # _profileTask method.
        task.setFunction(self._profileTask)
        task.setArgs([self._taskProfileInfo], True)

    def _profileTask(self, profileInfo, task):
        # This is called instead of the task function when we have
        # decided to profile a task.

        assert profileInfo.task == task
        # don't profile the same task twice in a row
        assert not profileInfo.profiled

        # Restore the task's proper function for next time.
        appendTask = False
        taskArgs = profileInfo.taskArgs
        if taskArgs and taskArgs[-1] == task:
            appendTask = True
            taskArgs = taskArgs[:-1]
        task.setArgs(taskArgs, appendTask)
        task.setFunction(profileInfo.taskFunc)

        # Defer this import until we need it: some Python
        # distributions don't provide the profile and pstats modules.
        PS = importlib.import_module('direct.showbase.ProfileSession')
        profileSession = PS.ProfileSession('profiled-task-%s' % task.getName(),
                                           Functor(profileInfo.taskFunc, *profileInfo.taskArgs))
        ret = profileSession.run()

        # set these values *after* profiling in case we're profiling the TaskProfiler
        profileInfo.session = profileSession
        profileInfo.profiled = True

        return ret

    def _hasProfiledDesignatedTask(self):
        # have we run a profile of the designated task yet?
        return self._taskProfileInfo.profiled

    def _getLastTaskProfileSession(self):
        return self._taskProfileInfo.session

    def _getRandomTask(self):
        # Figure out when the next frame is likely to expire, so we
        # won't grab any tasks that are sleeping for a long time.
        now = self.globalClock.getFrameTime()
        avgFrameRate = self.globalClock.getAverageFrameRate()
        if avgFrameRate < .00001:
            avgFrameDur = 0.
        else:
            avgFrameDur = (1. / self.globalClock.getAverageFrameRate())
        next = now + avgFrameDur

        # Now grab a task at random, until we find one that we like.
        tasks = self.mgr.getTasks()
        i = random.randrange(tasks.getNumTasks())
        task = tasks.getTask(i)
        while not isinstance(task, PythonTask) or \
              task.getWakeTime() > next:
            tasks.removeTask(i)
            i = random.randrange(tasks.getNumTasks())
            task = tasks.getTask(i)
        return task

    def __repr__(self):
        return str(self.mgr)

    # In the event we want to do frame time managment, this is the
    # function to replace or overload.
    def doYield(self, frameStartTime, nextScheduledTaskTime):
        pass

    """
    def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
        minFinTime = frameStartTime + self.MaxEpochSpeed
        if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
            print ' Adjusting Time'
            minFinTime = nextScheduledTaskTime
        delta = minFinTime - self.globalClock.getRealTime()
        while(delta > 0.002):
            print ' sleep %s'% (delta)
            time.sleep(delta)
            delta = minFinTime - self.globalClock.getRealTime()
    """

    if __debug__:
        # to catch memory leaks during the tests at the bottom of the file
        def _startTrackingMemLeaks(self):
            pass

        def _stopTrackingMemLeaks(self):
            pass

        def _checkMemLeaks(self):
            pass

    def _runTests(self):
        if __debug__:
            tm = TaskManager()
            tm.setClock(ClockObject())
            tm.setupTaskChain("default", tickClock = True)

            # check for memory leaks after every test
            tm._startTrackingMemLeaks()
            tm._checkMemLeaks()

            # run-once task
            l = []
            def _testDone(task, l=l):
                l.append(None)
                return task.done
            tm.add(_testDone, 'testDone')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 1
            _testDone = None
            tm._checkMemLeaks()

            # remove by name
            def _testRemoveByName(task):
                return task.done
            tm.add(_testRemoveByName, 'testRemoveByName')
            assert tm.remove('testRemoveByName') == 1
            assert tm.remove('testRemoveByName') == 0
            _testRemoveByName = None
            tm._checkMemLeaks()

            # duplicate named tasks
            def _testDupNamedTasks(task):
                return task.done
            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
            assert tm.remove('testRemoveByName') == 0
            _testDupNamedTasks = None
            tm._checkMemLeaks()

            # continued task
            l = []
            def _testCont(task, l = l):
                l.append(None)
                return task.cont
            tm.add(_testCont, 'testCont')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            tm.remove('testCont')
            _testCont = None
            tm._checkMemLeaks()

            # continue until done task
            l = []
            def _testContDone(task, l = l):
                l.append(None)
                if len(l) >= 2:
                    return task.done
                else:
                    return task.cont
            tm.add(_testContDone, 'testContDone')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            tm.step()
            assert len(l) == 2
            assert not tm.hasTaskNamed('testContDone')
            _testContDone = None
            tm._checkMemLeaks()

            # hasTaskNamed
            def _testHasTaskNamed(task):
                return task.done
            tm.add(_testHasTaskNamed, 'testHasTaskNamed')
            assert tm.hasTaskNamed('testHasTaskNamed')
            tm.step()
            assert not tm.hasTaskNamed('testHasTaskNamed')
            _testHasTaskNamed = None
            tm._checkMemLeaks()

            # task sort
            l = []
            def _testPri1(task, l = l):
                l.append(1)
                return task.cont
            def _testPri2(task, l = l):
                l.append(2)
                return task.cont
            tm.add(_testPri1, 'testPri1', sort = 1)
            tm.add(_testPri2, 'testPri2', sort = 2)
            tm.step()
            assert len(l) == 2
            assert l == [1, 2,]
            tm.step()
            assert len(l) == 4
            assert l == [1, 2, 1, 2,]
            tm.remove('testPri1')
            tm.remove('testPri2')
            _testPri1 = None
            _testPri2 = None
            tm._checkMemLeaks()

            # task extraArgs
            l = []
            def _testExtraArgs(arg1, arg2, l=l):
                l.extend([arg1, arg2,])
                return done
            tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
            tm.step()
            assert len(l) == 2
            assert l == [4, 5,]
            _testExtraArgs = None
            tm._checkMemLeaks()

            # task appendTask
            l = []
            def _testAppendTask(arg1, arg2, task, l=l):
                l.extend([arg1, arg2,])
                return task.done
            tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
            tm.step()
            assert len(l) == 2
            assert l == [4, 5,]
            _testAppendTask = None
            tm._checkMemLeaks()

            # task uponDeath
            l = []
            def _uponDeathFunc(task, l=l):
                l.append(task.name)
            def _testUponDeath(task):
                return done
            tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
            tm.step()
            assert len(l) == 1
            assert l == ['testUponDeath']
            _testUponDeath = None
            _uponDeathFunc = None
            tm._checkMemLeaks()

            # task owner
            class _TaskOwner:
                def _addTask(self, task):
                    self.addedTaskName = task.name
                def _clearTask(self, task):
                    self.clearedTaskName = task.name
            to = _TaskOwner()
            l = []
            def _testOwner(task):
                return done
            tm.add(_testOwner, 'testOwner', owner=to)
            tm.step()
            assert getattr(to, 'addedTaskName', None) == 'testOwner'
            assert getattr(to, 'clearedTaskName', None) == 'testOwner'
            _testOwner = None
            del to
            _TaskOwner = None
            tm._checkMemLeaks()


            doLaterTests = [0,]

            # doLater
            l = []
            def _testDoLater1(task, l=l):
                l.append(1)
            def _testDoLater2(task, l=l):
                l.append(2)
            def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .03:
                    assert l == [1, 2,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
            tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
            _testDoLater1 = None
            _testDoLater2 = None
            _monitorDoLater = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater sort
            l = []
            def _testDoLaterPri1(task, l=l):
                l.append(1)
            def _testDoLaterPri2(task, l=l):
                l.append(2)
            def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [1, 2,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
            tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
            _testDoLaterPri1 = None
            _testDoLaterPri2 = None
            _monitorDoLaterPri = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater extraArgs
            l = []
            def _testDoLaterExtraArgs(arg1, l=l):
                l.append(arg1)
            def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [3,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
            _testDoLaterExtraArgs = None
            _monitorDoLaterExtraArgs = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater appendTask
            l = []
            def _testDoLaterAppendTask(arg1, task, l=l):
                assert task.name == 'testDoLaterAppendTask'
                l.append(arg1)
            def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [4,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
                             extraArgs=[4,], appendTask=True)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
            _testDoLaterAppendTask = None
            _monitorDoLaterAppendTask = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater uponDeath
            l = []
            def _testUponDeathFunc(task, l=l):
                assert task.name == 'testDoLaterUponDeath'
                l.append(10)
            def _testDoLaterUponDeath(arg1, l=l):
                return done
            def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
                if task.time > .02:
                    assert l == [10,]
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
                             uponDeath=_testUponDeathFunc)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
            _testUponDeathFunc = None
            _testDoLaterUponDeath = None
            _monitorDoLaterUponDeath = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # doLater owner
            class _DoLaterOwner:
                def _addTask(self, task):
                    self.addedTaskName = task.name
                def _clearTask(self, task):
                    self.clearedTaskName = task.name
            doLaterOwner = _DoLaterOwner()
            l = []
            def _testDoLaterOwner(l=l):
                pass
            def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
                                     doLaterTests=doLaterTests):
                if task.time > .02:
                    assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
                    assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
                    doLaterTests[0] -= 1
                    return task.done
                return task.cont
            tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
                             owner=doLaterOwner)
            doLaterTests[0] += 1
            # make sure we run this task after the doLaters if they all occur on the same frame
            tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
            _testDoLaterOwner = None
            _monitorDoLaterOwner = None
            del doLaterOwner
            _DoLaterOwner = None
            # don't check until all the doLaters are finished
            #tm._checkMemLeaks()

            # run the doLater tests
            while doLaterTests[0] > 0:
                tm.step()
            del doLaterTests
            tm._checkMemLeaks()

            # getTasks
            def _testGetTasks(task):
                return task.cont
            # No doLaterProcessor in the new world.
            assert len(tm.getTasks()) == 0
            tm.add(_testGetTasks, 'testGetTasks1')
            assert len(tm.getTasks()) == 1
            assert (tm.getTasks()[0].name == 'testGetTasks1' or
                    tm.getTasks()[1].name == 'testGetTasks1')
            tm.add(_testGetTasks, 'testGetTasks2')
            tm.add(_testGetTasks, 'testGetTasks3')
            assert len(tm.getTasks()) == 3
            tm.remove('testGetTasks2')
            assert len(tm.getTasks()) == 2
            tm.remove('testGetTasks1')
            tm.remove('testGetTasks3')
            assert len(tm.getTasks()) == 0
            _testGetTasks = None
            tm._checkMemLeaks()

            # getDoLaters
            def _testGetDoLaters():
                pass
            assert len(tm.getDoLaters()) == 0
            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
            assert len(tm.getDoLaters()) == 1
            assert tm.getDoLaters()[0].name == 'testDoLater1'
            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
            assert len(tm.getDoLaters()) == 3
            tm.remove('testDoLater2')
            assert len(tm.getDoLaters()) == 2
            tm.remove('testDoLater1')
            tm.remove('testDoLater3')
            assert len(tm.getDoLaters()) == 0
            _testGetDoLaters = None
            tm._checkMemLeaks()

            # duplicate named doLaters removed via taskMgr.remove
            def _testDupNameDoLaters():
                pass
            # the doLaterProcessor is always running
            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
            assert len(tm.getDoLaters()) == 2
            tm.remove('testDupNameDoLater')
            assert len(tm.getDoLaters()) == 0
            _testDupNameDoLaters = None
            tm._checkMemLeaks()

            # duplicate named doLaters removed via remove()
            def _testDupNameDoLatersRemove():
                pass
            # the doLaterProcessor is always running
            dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
            dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
            assert len(tm.getDoLaters()) == 2
            dl2.remove()
            assert len(tm.getDoLaters()) == 1
            dl1.remove()
            assert len(tm.getDoLaters()) == 0
            _testDupNameDoLatersRemove = None
            # nameDict etc. isn't cleared out right away with task.remove()
            tm._checkMemLeaks()

            # getTasksNamed
            def _testGetTasksNamed(task):
                return task.cont
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
            assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
            tm.remove('testGetTasksNamed')
            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
            _testGetTasksNamed = None
            tm._checkMemLeaks()

            # removeTasksMatching
            def _testRemoveTasksMatching(task):
                return task.cont
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
            tm.removeTasksMatching('testRemoveTasksMatching')
            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
            tm.removeTasksMatching('testRemoveTasksMatching*')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
            tm.removeTasksMatching('testRemoveTasksMatching?a')
            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
            _testRemoveTasksMatching = None
            tm._checkMemLeaks()

            # create Task object and add to mgr
            l = []
            def _testTaskObj(task, l=l):
                l.append(None)
                return task.cont
            t = Task(_testTaskObj)
            tm.add(t, 'testTaskObj')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            tm.remove('testTaskObj')
            tm.step()
            assert len(l) == 2
            _testTaskObj = None
            tm._checkMemLeaks()

            # remove Task via task.remove()
            l = []
            def _testTaskObjRemove(task, l=l):
                l.append(None)
                return task.cont
            t = Task(_testTaskObjRemove)
            tm.add(t, 'testTaskObjRemove')
            tm.step()
            assert len(l) == 1
            tm.step()
            assert len(l) == 2
            t.remove()
            tm.step()
            assert len(l) == 2
            del t
            _testTaskObjRemove = None
            tm._checkMemLeaks()

            """
            # this test fails, and it's not clear what the correct behavior should be.
            # sort passed to Task.__init__ is always overridden by taskMgr.add()
            # even if no sort is specified, and calling Task.setSort() has no
            # effect on the taskMgr's behavior.
            # set/get Task sort
            l = []
            def _testTaskObjSort(arg, task, l=l):
                l.append(arg)
                return task.cont
            t1 = Task(_testTaskObjSort, sort=1)
            t2 = Task(_testTaskObjSort, sort=2)
            tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
            tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
            tm.step()
            assert len(l) == 2
            assert l == ['a', 'b']
            assert t1.getSort() == 1
            assert t2.getSort() == 2
            t1.setSort(3)
            assert t1.getSort() == 3
            tm.step()
            assert len(l) == 4
            assert l == ['a', 'b', 'b', 'a',]
            t1.remove()
            t2.remove()
            tm.step()
            assert len(l) == 4
            del t1
            del t2
            _testTaskObjSort = None
            tm._checkMemLeaks()
            """

            del l
            tm.destroy()
            del tm
 def getValue(self):
     return StateVar.get(self)
 def setValue(self, value):
     StateVar.set(self, value)
class DistributedPlayerSimpleShip(DistributedSimpleShip):
    RepairSpotFadeAfter = 2.0
    RepairSpotFadeDur = 3.0

    def __init__(self, cr):
        DistributedSimpleShip.__init__(self, cr)
        self._respawnLocation = None
        self.checkAnchor = None
        self.lastAttacked = None
        self.threatLevel = 0
        self.openPort = 0
        self.allowCrewState = True
        self.allowFriendState = True
        self.allowGuildState = False
        self.allowPublicState = False
        self._repairSpotMgr = ShipRepairSpotMgr(self.cr)
        self._team = PiratesGlobals.PLAYER_TEAM
        self.badInitTeam = None
        self.prevLocStack = None
        self.customHull = 0
        self.customRigging = 0
        self.customSailPattern = 0
        self.customSailLogo = 0
        self.customProw = 0
        self.customCannon = 0
        self.boardingPanel = None

    def generate(self):
        DistributedSimpleShip.generate(self)
        self._repairSpotWoodPile = None
        self._repairSpotWoodPiles = {}
        self._repairSpotHole = None
        self._repairSpotHoleFixed = None
        self._repairSpotHoles = {}
        self._repairSpotIvals = {}
        self._wheelInUse = StateVar(False)

    def announceGenerate(self):
        self._respawnLocation = None
        self._respawnResponseDelayedCall = None
        DistributedSimpleShip.announceGenerate(self)
        self._repairSpotMgr.setShipId(self.doId)
        if self.badInitTeam != None:
            self._verifyTeam(self.badInitTeam)

    def disable(self):
        if self.boardingPanel:
            self.boardingPanel.destroy()
            self.boardingPanel = None

        self._wheelInUse.destroy()
        if self._respawnResponseDelayedCall:
            self._respawnResponseDelayedCall.destroy()
            self._respawnResponseDelayedCall = None

        if self.checkAnchor:
            self.checkAnchor.remove()
            self.checkAnchor = None

        self._repairSpotMgr.destroy()
        for ival in self._repairSpotIvals.itervalues():
            ival.pause()

        self._repairSpotIvals = None
        self.prevLocStack = None
        self.prevLocStack = None
        DistributedSimpleShip.disable(self)

    def calculateLook(self):
        team = self.getTeam()
        if team == PiratesGlobals.PLAYER_TEAM:
            if self.getSiegeTeam() == 1:
                self.style = ShipGlobals.Styles.French
            elif self.getSiegeTeam() == 2:
                self.style = ShipGlobals.Styles.Spanish

    def loadStats(self):
        DistributedSimpleShip.loadStats(self)
        speedUpgrade = 1.0
        turningUpgrade = 1.0
        if self.customHull:
            speedUpgrade = ShipUpgradeGlobals.HULL_TYPES[
                self.customHull]['Speed']
            turningUpgrade = ShipUpgradeGlobals.HULL_TYPES[
                self.customHull]['Turning']
            self.setSpeedMods(baseMod=None,
                              speedUpgradeMod=speedUpgrade,
                              turnUpgradeMod=turningUpgrade)

    def setShipUpfitList(self, dataList):
        counter = 0
        while dataList:
            newData = dataList[0]
            dataList = dataList[1:]
            if counter == 0:
                self.customHull = newData
            elif counter == 1:
                self.customRigging = newData
            elif counter == 2:
                self.customSailPattern = newData
            elif counter == 3:
                self.customSailLogo = newData

            counter += 1
        self.setupLocalStats()
        messenger.send('ShipChanged-%s' % self.doId)

    def getSkillBoost(self, skillId):
        if self.customRigging:
            riggingInfo = ShipUpgradeGlobals.RIGGING_TYPES.get(
                self.customRigging)
            boostLevel = ShipUpgradeGlobals.RIGGING_TYPES.get(
                self.customRigging, {}).get('SkillBoosts', {}).get(skillId, 0)
            return boostLevel

        return 0

    def buildShip(self):
        hullMaterial = None
        sailMaterial = None
        sailPattern = None
        invertLogo = False
        logo = ShipGlobals.Logos.Undefined
        if self.customHull:
            hullType = ShipUpgradeGlobals.HULL_TYPES.get(self.customHull)
            if hullType:
                hullMaterial = hullType['StyleIndex']

        if self.customRigging:
            riggingType = ShipUpgradeGlobals.RIGGING_TYPES.get(
                self.customRigging)
            if riggingType:
                sailMaterial = riggingType['StyleIndex']

        if self.customSailPattern:
            patternType = ShipUpgradeGlobals.SAILCOLOR_TYPES.get(
                self.customSailPattern)
            if patternType:
                sailPattern = patternType['StyleIndex']

        logo = self.getLogo()
        if self.customSailLogo:
            logoType = ShipUpgradeGlobals.LOGO_TYPES.get(self.customSailLogo)
            if logoType:
                logo = logoType['StyleIndex']
                invertLogo = logoType['Invert']

        team = self.getTeam()
        if team == PiratesGlobals.PLAYER_TEAM:
            if self.getSiegeTeam() == 1:
                sailPattern = ShipGlobals.Styles.French
            elif self.getSiegeTeam() == 2:
                sailPattern = ShipGlobals.Styles.Spanish

        self.model = base.shipFactory.getShip(
            self.shipClass,
            self.getStyle(),
            detailLevel=base.options.terrain_detail_level,
            hullMaterial=hullMaterial,
            sailMaterial=sailMaterial,
            sailPattern=sailPattern,
            logo=logo,
            invertLogo=invertLogo)

    def getNPCship(self):
        return False

    def setShipClass(self, shipClass):
        DistributedSimpleShip.setShipClass(self, shipClass)
        self._repairSpotMgr.updateShipClass(self.shipClass)

    def setHealthState(self, health):
        DistributedSimpleShip.setHealthState(self, health)
        self._repairSpotMgr.updateHealth(self.healthState)

    def setMastStates(self, mainMast1, mainMast2, mainMast3, aftMast,
                      foreMast):
        DistributedSimpleShip.setMastStates(self, mainMast1, mainMast2,
                                            mainMast3, aftMast, foreMast)
        self._repairSpotMgr.updateSpeed(100.0 * self.Sp / self.maxSp)

    def setArmorStates(self, rear, left, right):
        DistributedSimpleShip.setArmorStates(self, rear, left, right)
        self._repairSpotMgr.updateArmor((rear + left + right) / 3.0)

    def setWillFullyRepairShip(self, willFullyRepairShip):
        self._repairSpotMgr.updateWillBeFullHealth(willFullyRepairShip)

    def setupLocalStats(self):
        DistributedSimpleShip.setupLocalStats(self)
        if self.customHull:
            tempArmorState = [
                100.0 * self.armor[0] / self.maxArmor[0],
                100.0 * self.armor[1] / self.maxArmor[1],
                100.0 * self.armor[2] / self.maxArmor[2]
            ]
            tempHealthState = 100.0 * self.Hp / self.maxHp
            tempMastState = []
            for mastIndex in range(len(self.maxMastHealth)):
                if self.maxMastHealth[mastIndex] > 0.0:
                    tempMastState.append(100.0 * self.mastHealth[mastIndex] /
                                         self.maxMastHealth[mastIndex])
                    continue
                tempMastState.append(0.0)

            armorUpgrade = ShipUpgradeGlobals.HULL_TYPES[
                self.customHull]['Armor']
            self.maxHp *= armorUpgrade
            self.Hp *= armorUpgrade
            newArmor = []
            for entry in self.maxArmor:
                newArmor.append(entry * armorUpgrade)

            self.maxArmor = newArmor
            newMastHealth = []
            for entry in self.maxMastHealth:
                newMastHealth.append(entry * armorUpgrade)

            self.maxMastHealth = newMastHealth
            self.maxSp *= armorUpgrade
            cargoUpgrade = ShipUpgradeGlobals.HULL_TYPES[
                self.customHull]['Cargo']
            self.setMaxCargo(int(cargoUpgrade * self.maxCargo))

    def setOpenPort(self, portId):
        oldPort = self.openPort
        self.openPort = portId
        if localAvatar.ship and localAvatar.ship.getDoId() == self.getDoId():
            messenger.send('LocalAvatar_Ship_OpenPort_Update',
                           [portId, oldPort])

    def getOpenPort(self):
        return self.openPort

    def isAtOpenPort(self):
        portDoId = localAvatar.getPort()
        portObj = base.cr.doId2do.get(portDoId, None)
        if self.threatLevel < EnemyGlobals.SHIP_THREAT_NAVY_HUNTERS:
            return 1
        elif portObj and portObj.uniqueId == EnemyGlobals.OPEN_PORT_DICT.get(
                self.openPort):
            return 1
        else:
            return 0

    def setThreatLevel(self, threatLevel):
        if threatLevel != self.threatLevel:
            self.threatLevel = threatLevel
            self.updateNametag()
            if localAvatar.ship and localAvatar.ship.getDoId() == self.getDoId(
            ):
                messenger.send('LocalAvatar_Ship_ThreatLevel_Update',
                               [threatLevel])

            self.checkAbleDropAnchor()

    def getThreatLevel(self):
        if base.config.GetBool('want-ship-threat', 1):
            return self.threatLevel
        else:
            return EnemyGlobals.SHIP_THREAT_ATTACK_BACK

    def getOpenPort(self):
        return self.openPort

    def sunkAShipFanfare(self, shipToAttackDoId):
        if localAvatar.ship and localAvatar.ship == self:
            if localAvatar.ship.getSiegeTeam():
                return None

            attackMessage = HighSeasGlobals.getShipSunkMessage()
            if attackMessage:
                base.localAvatar.guiMgr.queueInstructionMessage(
                    attackMessage[0],
                    attackMessage[1],
                    None,
                    1.0,
                    messageCategory=MessageGlobals.MSG_CAT_SUNK_SHIP)

    def setSiegeTeam(self, team):
        different = team != self.getSiegeTeam()
        DistributedSimpleShip.setSiegeTeam(self, team)
        if different:
            self._doSiegeAndPVPTeamColors()
            self._repairSpotMgr.updateSiegeTeam(team)
            minimapObj = self.getMinimapObject()
            if minimapObj:
                minimapObj.setSiegeTeam(team)

    setSiegeTeam = report(types=['args'],
                          dConfigParam='shipdeploy')(setSiegeTeam)

    def _doSiegeAndPVPTeamColors(self):
        if self.getPVPTeam():
            self._doPVPTeamColors()
        elif self.getSiegeTeam():
            pass

    def _doPVPTeamColors(self):
        pass

    def getWheelInUseSV(self):
        return self._wheelInUse

    def setWheelInUse(self, wheelInUse):
        DistributedSimpleShip.setWheelInUse(self, wheelInUse)
        self._wheelInUse.set(wheelInUse)

    def canTakeWheel(self, wheel, av):
        available = True
        if self.queryGameState() in ('Pinned', 'Sinking', 'Sunk',
                                     'OtherShipBoarded'):
            base.localAvatar.guiMgr.createWarning(PLocalizer.ShipPinnedWarning,
                                                  PiratesGuiGlobals.TextFG6)
            available = False
        elif self.isFishing and base.localAvatar.getDoId() != self.ownerId:
            base.localAvatar.guiMgr.createWarning(
                PLocalizer.OnlyCaptainCanUseWarning, PiratesGuiGlobals.TextFG6)
            available = False
        elif wheel.getUserId() and base.localAvatar.getDoId() != self.ownerId:
            base.localAvatar.guiMgr.createWarning(
                PLocalizer.AlreadyInUseWarning, PiratesGuiGlobals.TextFG6)
            available = False

        return available

    def setRespawnLocation(self, parentId, zoneId):
        self._respawnLocation = (parentId, zoneId)

    def setLocation(self, parentId, zoneId):
        DistributedSimpleShip.setLocation(self, parentId, zoneId)
        if self._respawnLocation is not None and self._respawnLocation == (
                parentId, zoneId):
            self.updateCurrentZone()
            self._respawnLocation = None
            if not self._respawnResponseDelayedCall:
                self._respawnResponseDelayedCall = FrameDelayedCall(
                    'PlayerShip-respawnLocation-gridInterestComplete',
                    Functor(base.cr.setAllInterestsCompleteCallback,
                            self._sendRespawnLocationResponse))

    def _sendRespawnLocationResponse(self):
        self.sendUpdate('clientReachedRespawnLocation')
        self._respawnResponseDelayedCall = None

    def recoverFromSunk(self):
        self.lastAttacked = None
        DistributedSimpleShip.recoverFromSunk(self)

    def attacked(self):
        self.lastAttacked = globalClock.getFrameTime()
        if self.getSiegeTeam() and not (self.checkAnchor):
            self.checkAbleDropAnchor()

    def attackTimerRemaining(self):
        timer = 0
        if self.lastAttacked:
            timer = int(30 - globalClock.getFrameTime() - self.lastAttacked)

        return timer

    def _DistributedPlayerSimpleShip__recheckAbleDropAnchor(self, task):
        self.checkAnchor = None
        self.checkAbleDropAnchor()

    def checkAbleDropAnchor(self):
        PiratesGuiGlobals = PiratesGuiGlobals
        import pirates.piratesgui
        if localAvatar.doId == self.steeringAvId:
            if self.shipStatusDisplay:
                if localAvatar.getPort():
                    remaining = self.attackTimerRemaining()
                    if self.getSiegeTeam() and remaining > 0:
                        self.shipStatusDisplay.disableAnchorButton()
                        localAvatar.guiMgr.createWarning(
                            PLocalizer.CannotDockYet % remaining,
                            PiratesGuiGlobals.TextFG6)
                        self.checkAnchor = taskMgr.doMethodLater(
                            remaining, self.
                            _DistributedPlayerSimpleShip__recheckAbleDropAnchor,
                            'checkAnchor')
                    elif self.isAtOpenPort():
                        self.shipStatusDisplay.enableAnchorButton()
                    else:
                        self.shipStatusDisplay.disableAnchorButton()
                        self.shipStatusDisplay.tellWrongPort()
                else:
                    self.shipStatusDisplay.disableAnchorButton()
                    self.shipStatusDisplay.hideWrongPort()

    def _addRepairSpotModels(self):
        if not self._repairSpotWoodPile:
            self._repairSpotWoodPile = loader.loadModel(
                'models/props/repair_spot_wood')
            collFloors = self._repairSpotWoodPile.find('**/collision_floor')
            if not collFloors.isEmpty():
                collideMask = collFloors.getCollideMask()
                collideMask ^= PiratesGlobals.FloorBitmask
                collideMask |= PiratesGlobals.ShipFloorBitmask
                collFloors.setCollideMask(collideMask)

        for locIndex in PVPGlobals.ShipClass2repairLocators[
                self.modelClass].getValue():
            locName = PVPGlobals.RepairSpotLocatorNames[locIndex]
            self._repairSpotWoodPiles[locName] = self.getModelRoot(
            ).attachNewNode('repairSpotWoodPile-%s' % locName)
            self._repairSpotWoodPile.instanceTo(
                self._repairSpotWoodPiles[locName])
            locator = self.getLocator(locName)
            self._repairSpotWoodPiles[locName].setPosHpr(
                locator.getPos(), locator.getHpr())

    def _removeRepairSpotModels(self):
        for woodPile in self._repairSpotWoodPiles.itervalues():
            woodPile.detachNode()

        self._repairSpotWoodPiles = {}

    def _placeRepairSpotModel(self, locIndex, model):
        locName = PVPGlobals.RepairSpotLocatorNames[locIndex]
        parentNode = self.getModelRoot().attachNewNode('repairSpotHole-%s' %
                                                       locName)
        parentNode.setTransparency(1, 100)
        model.instanceTo(parentNode)
        locator = self.getLocator(locName)
        parentNode.setPosHpr(locator.getPos(), locator.getHpr())
        self._repairSpotHoles[locIndex] = parentNode

    def _removeRepairSpotModel(self, locIndex):
        if locIndex in self._repairSpotHoles:
            self._repairSpotHoles[locIndex].detachNode()
            del self._repairSpotHoles[locIndex]

    def _fadeOutRepairSpotModel(self, locIndex):
        if locIndex in self._repairSpotIvals:
            self._repairSpotIvals[locIndex].pause()

        self._repairSpotHoles[locIndex].setTransparency(1, 100)
        ival = IG.Sequence(
            IG.Wait(DistributedPlayerSimpleShip.RepairSpotFadeAfter),
            IG.LerpColorScaleInterval(
                self._repairSpotHoles[locIndex],
                DistributedPlayerSimpleShip.RepairSpotFadeDur,
                Vec4(1.0, 1.0, 1.0, 0.0),
                blendType='easeInOut'))
        ival.start()
        self._repairSpotIvals[locIndex] = ival

    def _addRepairSpotHoles(self):
        if not self._repairSpotHole:
            repairSpotHoleModels = loader.loadModel(
                'models/props/repair_spot_hole')
            self._repairSpotHole = repairSpotHoleModels.find('**/floor_hole')
            self._repairSpotHoleFixed = repairSpotHoleModels.find(
                '**/floor_hole_fixed')

        for locIndex in PVPGlobals.ShipClass2repairLocators[
                self.modelClass].getValue():
            self._removeRepairSpotModel(locIndex)
            self._placeRepairSpotModel(locIndex, self._repairSpotHole)

    def _removeRepairSpotHoles(self):
        for locIndex in PVPGlobals.ShipClass2repairLocators[
                self.modelClass].getValue():
            self._removeRepairSpotModel(locIndex)
            if self._repairSpotHoleFixed:
                self._placeRepairSpotModel(locIndex, self._repairSpotHoleFixed)
                self._fadeOutRepairSpotModel(locIndex)
                self._repairSpotIvals[locIndex] = IG.Sequence(
                    self._repairSpotIvals[locIndex],
                    IG.Func(self._removeRepairSpotModel, locIndex))
                continue

    def b_setAllowCrewState(self, state):
        self.d_setAllowCrewState(state)
        self.setAllowCrewState(state)

    def b_setAllowFriendState(self, state):
        self.d_setAllowFriendState(state)
        self.setAllowFriendState(state)

    def b_setAllowGuildState(self, state):
        self.d_setAllowGuildState(state)
        self.setAllowGuildState(state)

    def b_setAllowPublicState(self, state):
        self.d_setAllowPublicState(state)
        self.setAllowPublicState(state)

    def d_setAllowCrewState(self, state):
        self.sendUpdate('setAllowCrewState', [state])

    def d_setAllowFriendState(self, state):
        self.sendUpdate('setAllowFriendState', [state])

    def d_setAllowGuildState(self, state):
        self.sendUpdate('setAllowGuildState', [state])

    def d_setAllowPublicState(self, state):
        self.sendUpdate('setAllowPublicState', [state])

    def setAllowCrewState(self, state):
        self.allowCrewState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowCrew(state)

    def setAllowFriendState(self, state):
        self.allowFriendState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowFriends(state)

    def setAllowGuildState(self, state):
        self.allowGuildState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowGuild(state)

    def setAllowPublicState(self, state):
        self.allowPublicState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowPublic(state)

    def getAllowCrewState(self):
        return self.allowCrewState

    def getAllowFriendState(self):
        return self.allowFriendState

    def getAllowGuildState(self):
        return self.allowGuildState

    def getAllowPublicState(self):
        return self.allowPublicState

    def hasSpace(self, avId=0, bandMgrId=0, bandId=0, guildId=0):
        if avId == self.ownerId:
            return True

        if self.isInCrew(avId):
            return True

        if self.isInCrew(self.ownerId) and len(self.crew) >= self.maxCrew:
            return False

        if len(self.crew) >= self.maxCrew - 1:
            return False

        return True

    def confirmSameCrewTeleport(self,
                                toFrom,
                                incomingAvId=0,
                                bandMgrId=0,
                                bandId=0,
                                guildId=0):
        if toFrom == 'from':
            return True
        elif not self.isGenerated():
            self.notify.warning('confirmSameCrewTeleport(%s)' %
                                localAvatar.getShipString())
            return False

        if incomingAvId == self.ownerId:
            return True

        if bandMgrId and bandId and self.getAllowCrewState() and (
                bandMgrId, bandId) == self.getBandId():
            return True

        if localAvatar.doId == self.ownerId and self.getAllowFriendState(
        ) and self.cr.identifyFriend(incomingAvId):
            return True

        if guildId and self.getAllowGuildState(
        ) and guildId == self.getGuildId():
            return True

        if self.getAllowPublicState():
            return True

        return False

    confirmSameCrewTeleport = report(
        types=['frameCount', 'deltaStamp',
               'args'], dConfigParam='shipboard')(confirmSameCrewTeleport)

    def localPirateArrived(self, av):
        DistributedSimpleShip.localPirateArrived(self, av)
        self.enableOnDeckInteractions()
        mapObj = self.getMinimapObject()
        if mapObj:
            mapObj.setAsLocalAvShip(av.getCrewShipId() == self.doId)

    def showBoardingChoice(self, shipToBoard):
        if not self.boardingPanel:
            shipInfo = shipToBoard.getShipInfo()
            globalClockDelta = globalClockDelta
            import direct.distributed.ClockDelta
            dt = globalClockDelta.localElapsedTime(shipToBoard.sinkTimestamp)
            time = shipToBoard.sinkTime - dt
            self.boardingPanel = ShipFrameBoard.ShipFrameBoard(
                shipName=shipInfo[1],
                shipClass=shipInfo[2],
                mastInfo=shipInfo[3],
                parent=base.a2dTopCenter,
                pos=(-0.45000000000000001, 0, -0.5),
                time=time,
                command=self._DistributedPlayerSimpleShip__handleBoardingChoice
            )
            self._boardingTimer = taskMgr.doMethodLater(
                time, self._boardingChoiceTimeout, 'boardingTimer')

        self.boardingPanel.show()

    def _boardingChoiceTimeout(self, task):
        self.removeBoardingChoice()

    def hideBoardingChoice(self):
        if self.boardingPanel:
            self.boardingPanel.hide()

    def removeBoardingChoice(self):
        if self.boardingPanel:
            self.boardingPanel.destroy()
            self.boardingPanel = None

        if self._boardingTimer:
            self._boardingTimer.remove()
            self._boardingTimer = None

    def _DistributedPlayerSimpleShip__handleBoardingChoice(self, wishToBoard):
        self.removeBoardingChoice()
        self.d_setBoardingChoice(int(wishToBoard))

    def d_setBoardingChoice(self, choice):
        self.sendUpdate('setBoardingChoice', [choice])

    def getMinimapObject(self):
        if not (self.minimapObj) and not self.isDisabled():
            self.minimapObj = MinimapPlayerShip(self)

        return self.minimapObj

    def setTeam(self, team):
        if not self._verifyTeam(team):
            return None

        DistributedSimpleShip.setTeam(self, team)

    def _verifyTeam(self, team):
        if team == PiratesGlobals.INVALID_TEAM:
            doId = '<no doId>'
            if hasattr(self, 'doId'):
                doId = self.doId
            else:
                self.badInitTeam = team
            base.cr.centralLogger.writeClientEvent('bad ship team: %s' % doId)
            self.notify.warning('bad ship team: %s' % doId)
            return False

        return True

    def d_setLocation(self, parentId, zoneId):
        theStack = StackTrace(start=1)
        if self.prevLocStack and len(
                theStack.trace) == len(self.prevLocStack.trace) and map(
                    lambda x: x[1], theStack.trace) == map(
                        lambda x: x[1], self.prevLocStack.trace):
            base.cr.centralLogger.writeClientEvent('bad ship team: %s setLoc' %
                                                   self.doId)
        else:
            base.cr.centralLogger.writeClientEvent(
                'bad ship team: %s' % self.doId +
                theStack.compact()[1:len(theStack.compact())])
            self.prevLocStack = theStack
        DistributedSimpleShip.d_setLocation(self, parentId, zoneId)
 def __init__(self, cr):
     DistributedObject.DistributedObject.__init__(self, cr)
     self.toons = []
     self.activeIntervals = {}
     self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg')
     self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg')
     self.suits = []
     self.reserveSuits = []
     self.joiningReserves = []
     self.distBldgDoId = None
     self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0)
     self.currentFloor = -1
     self.elevatorName = self.__uniqueName('elevator')
     self.floorModel = None
     self.elevatorOutOpen = 0
     self.BottomFloor_SuitPositions = [Point3(0, 15, 0),
      Point3(10, 20, 0),
      Point3(-7, 24, 0),
      Point3(-10, 0, 0)]
     self.BottomFloor_SuitHs = [75,
      170,
      -91,
      -44]
     self.Cubicle_SuitPositions = [Point3(0, 18, 0),
      Point3(10, 12, 0),
      Point3(-9, 11, 0),
      Point3(-3, 13, 0)]
     self.Cubicle_SuitHs = [170,
      56,
      -52,
      10]
     self.BossOffice_SuitPositions = [Point3(0, 15, 0),
      Point3(10, 20, 0),
      Point3(-10, 6, 0),
      Point3(-17, 30, 0)]
     self.BossOffice_SuitHs = [170,
      120,
      12,
      38]
     self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 1)
     self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom()
     self.brResults = [[], []]
     self.barrelRoomIntroTrack = None
     self.penthouseOutroTrack = None
     self.penthouseOutroChatDoneTrack = None
     self.penthouseIntroTrack = None
     self.waitMusic = base.loadMusic('phase_7/audio/bgm/encntr_toon_winning_indoor.ogg')
     self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.ogg')
     self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInterior', [State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']),
      State.State('Elevator', self.enterElevator, self.exitElevator, ['Game', 'BattleIntro', 'BarrelRoomIntro']),
      State.State('Game', self.enterGame, self.exitGame, ['Resting', 'Failed', 'BattleIntro', 'BarrelRoomIntro', 'Elevator']),
      State.State('BarrelRoomIntro', self.enterBarrelRoomIntro, self.exitBarrelRoomIntro, ['CollectBarrels', 'Off']),
      State.State('CollectBarrels', self.enterCollectBarrels, self.exitCollectBarrels, ['BarrelRoomReward', 'Off']),
      State.State('BarrelRoomReward', self.enterBarrelRoomReward, self.exitBarrelRoomReward, ['Battle',
       'ReservesJoining',
       'BattleIntro',
       'Off']),
      State.State('BattleIntro', self.enterBattleIntro, self.exitBattleIntro, ['Battle', 'ReservesJoining', 'Off']),
      State.State('Battle', self.enterBattle, self.exitBattle, ['Resting', 'Reward', 'ReservesJoining']),
      State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']),
      State.State('Resting', self.enterResting, self.exitResting, ['Elevator']),
      State.State('Reward', self.enterReward, self.exitReward, ['Off']),
      State.State('Failed', self.enterFailed, self.exitFailed, ['Off']),
      State.State('Off', self.enterOff, self.exitOff, ['Elevator', 'WaitForAllToonsInside', 'Battle'])], 'Off', 'Off')
     self.fsm.enterInitialState()
     self._haveEntranceElevator = StateVar(False)
     self._stashEntranceElevator = StateVar(False)
     self._stashEntranceElevatorFC = FunctionCall(self._doStashEntranceElevator, self._haveEntranceElevator, self._stashEntranceElevator)
     self._entranceElevCallbacks = []
     self._doEntranceElevCallbacksFC = FunctionCall(self._doEntranceElevCallbacks, self._haveEntranceElevator)
     self.cage = None
     self.shopOwnerNpcId = None
     self.shopOwnerNpc = None
     self._movie = None
     self.SOSToonName = None
     self.FOType = None
Example #30
0
class TaskManager():
    __module__ = __name__
    notify = directNotify.newCategory('TaskManager')
    extendedExceptions = False
    MaxEpochSpeed = 1.0 / 30.0

    def __init__(self):
        self.mgr = AsyncTaskManager.getGlobalPtr()
        self.resumeFunc = None
        self.globalClock = self.mgr.getClock()
        self.stepping = False
        self.running = False
        self.destroyed = False
        self.fKeyboardInterrupt = False
        self.interruptCount = 0
        self._frameProfileQueue = Queue()
        self._profileFrames = None
        self._frameProfiler = None
        self._profileTasks = None
        self._taskProfiler = None
        self._taskProfileInfo = ScratchPad(taskId=None, profiled=False, session=None)
        return

    def finalInit(self):
        from direct.fsm.StatePush import StateVar
        self._profileTasks = StateVar(False)
        self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
        self._profileFrames = StateVar(False)
        self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())

    def destroy(self):
        self.notify.info('TaskManager.destroy()')
        self.destroyed = True
        self._frameProfileQueue.clear()
        self.mgr.cleanup()

    def setClock(self, clockObject):
        self.mgr.setClock(clockObject)
        self.globalClock = clockObject

    def invokeDefaultHandler(self, signalNumber, stackFrame):
        print '*** allowing mid-frame keyboard interrupt.'
        signal.signal(signal.SIGINT, signal.default_int_handler)
        raise KeyboardInterrupt

    def keyboardInterruptHandler(self, signalNumber, stackFrame):
        self.fKeyboardInterrupt = 1
        self.interruptCount += 1
        if self.interruptCount == 1:
            print '* interrupt by keyboard'
        elif self.interruptCount == 2:
            print '** waiting for end of frame before interrupting...'
            signal.signal(signal.SIGINT, self.invokeDefaultHandler)

    def getCurrentTask(self):
        return Thread.getCurrentThread().getCurrentTask()

    def hasTaskChain(self, chainName):
        return self.mgr.findTaskChain(chainName) != None

    def setupTaskChain(self, chainName, numThreads = None, tickClock = None, threadPriority = None, frameBudget = None, frameSync = None, timeslicePriority = None):
        chain = self.mgr.makeTaskChain(chainName)
        if numThreads is not None:
            chain.setNumThreads(numThreads)
        if tickClock is not None:
            chain.setTickClock(tickClock)
        if threadPriority is not None:
            chain.setThreadPriority(threadPriority)
        if frameBudget is not None:
            chain.setFrameBudget(frameBudget)
        if frameSync is not None:
            chain.setFrameSync(frameSync)
        if timeslicePriority is not None:
            chain.setTimeslicePriority(timeslicePriority)
        return

    def hasTaskNamed(self, taskName):
        return bool(self.mgr.findTask(taskName))

    def getTasksNamed(self, taskName):
        return self.__makeTaskList(self.mgr.findTasks(taskName))

    def getTasksMatching(self, taskPattern):
        return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))

    def getAllTasks(self):
        return self.__makeTaskList(self.mgr.getTasks())

    def getTasks(self):
        return self.__makeTaskList(self.mgr.getActiveTasks())

    def getDoLaters(self):
        return self.__makeTaskList(self.mgr.getSleepingTasks())

    def __makeTaskList(self, taskCollection):
        l = []
        for i in range(taskCollection.getNumTasks()):
            l.append(taskCollection.getTask(i))

        return l

    def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None, sort = None, priority = None, taskChain = None, uponDeath = None, appendTask = False, owner = None):
        if delayTime < 0:
            pass
        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
        task.setDelay(delayTime)
        self.mgr.add(task)
        return task

    def add(self, funcOrTask, name = None, sort = None, extraArgs = None, priority = None, uponDeath = None, appendTask = False, taskChain = None, owner = None):
        task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
        self.mgr.add(task)
        return task

    def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
        if isinstance(funcOrTask, AsyncTask):
            task = funcOrTask
        elif hasattr(funcOrTask, '__call__'):
            task = PythonTask(funcOrTask)
        else:
            self.notify.error('add: Tried to add a task that was not a Task or a func')
        if hasattr(task, 'setArgs'):
            if extraArgs is None:
                extraArgs = []
                appendTask = True
            task.setArgs(extraArgs, appendTask)
        elif extraArgs is not None:
            self.notify.error('Task %s does not accept arguments.' % repr(task))
        if name is not None:
            task.setName(name)
        if priority is not None and sort is None:
            task.setSort(priority)
        else:
            if priority is not None:
                task.setPriority(priority)
            if sort is not None:
                task.setSort(sort)
        if taskChain is not None:
            task.setTaskChain(taskChain)
        if owner is not None:
            task.setOwner(owner)
        if uponDeath is not None:
            task.setUponDeath(uponDeath)
        return task

    def remove(self, taskOrName):
        if isinstance(taskOrName, types.StringTypes):
            tasks = self.mgr.findTasks(taskOrName)
            return self.mgr.remove(tasks)
        elif isinstance(taskOrName, AsyncTask):
            return self.mgr.remove(taskOrName)
        elif isinstance(taskOrName, types.ListType):
            for task in taskOrName:
                self.remove(task)

        else:
            self.notify.error('remove takes a string or a Task')

    def removeTasksMatching(self, taskPattern):
        tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
        return self.mgr.remove(tasks)

    def step(self):
        self.fKeyboardInterrupt = 0
        self.interruptCount = 0
        signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
        startFrameTime = self.globalClock.getRealTime()
        self.mgr.poll()
        nextTaskTime = self.mgr.getNextWakeTime()
        self.doYield(startFrameTime, nextTaskTime)
        signal.signal(signal.SIGINT, signal.default_int_handler)
        if self.fKeyboardInterrupt:
            raise KeyboardInterrupt

    def run(self):
        t = self.globalClock.getFrameTime()
        timeDelta = t - self.globalClock.getRealTime()
        self.globalClock.setRealTime(t)
        messenger.send('resetClock', [timeDelta])
        if self.resumeFunc != None:
            self.resumeFunc()
        if self.stepping:
            self.step()
        else:
            self.running = True
            while self.running:
                try:
                    if len(self._frameProfileQueue):
                        numFrames, session, callback = self._frameProfileQueue.pop()

                        def _profileFunc(numFrames = numFrames):
                            self._doProfiledFrames(numFrames)

                        session.setFunc(_profileFunc)
                        session.run()
                        _profileFunc = None
                        if callback:
                            callback()
                        session.release()
                    else:
                        self.step()
                except KeyboardInterrupt:
                    self.stop()
                except IOError as ioError:
                    code, message = self._unpackIOError(ioError)
                    if code == 4:
                        self.stop()
                    else:
                        raise
                except Exception as e:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        if ExceptionVarDump.wantStackDumpLog and ExceptionVarDump.dumpOnExceptionInit:
                            ExceptionVarDump._varDump__print(e)
                        raise
                except:
                    if self.extendedExceptions:
                        self.stop()
                        print_exc_plus()
                    else:
                        raise

        self.mgr.stopThreads()
        return

    def _unpackIOError(self, ioError):
        try:
            code, message = ioError
        except:
            code = 0
            message = ioError

        return (code, message)

    def stop(self):
        self.running = False

    def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
        if not isinstance(task, PythonTask):
            return 0
        method = task.getFunction()
        if type(method) == types.MethodType:
            function = method.im_func
        else:
            function = method
        if function == oldMethod:
            import new
            newMethod = new.instancemethod(newFunction, method.im_self, method.im_class)
            task.setFunction(newMethod)
            return 1
        return 0

    def replaceMethod(self, oldMethod, newFunction):
        numFound = 0
        for task in self.getAllTasks():
            numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)

        return numFound

    def popupControls(self):
        from direct.tkpanels import TaskManagerPanel
        return TaskManagerPanel.TaskManagerPanel(self)

    def getProfileSession(self, name = None):
        if name is None:
            name = 'taskMgrFrameProfile'
        from direct.showbase.ProfileSession import ProfileSession
        return ProfileSession(name)

    def profileFrames(self, num = None, session = None, callback = None):
        if num is None:
            num = 1
        if session is None:
            session = self.getProfileSession()
        session.acquire()
        self._frameProfileQueue.push((num, session, callback))
        return

    def _doProfiledFrames(self, numFrames):
        for i in xrange(numFrames):
            result = self.step()

        return result

    def getProfileFrames(self):
        return self._profileFrames.get()

    def getProfileFramesSV(self):
        return self._profileFrames

    def setProfileFrames(self, profileFrames):
        self._profileFrames.set(profileFrames)
        if not self._frameProfiler and profileFrames:
            from direct.task.FrameProfiler import FrameProfiler
            self._frameProfiler = FrameProfiler()

    def getProfileTasks(self):
        return self._profileTasks.get()

    def getProfileTasksSV(self):
        return self._profileTasks

    def setProfileTasks(self, profileTasks):
        self._profileTasks.set(profileTasks)
        if not self._taskProfiler and profileTasks:
            from direct.task.TaskProfiler import TaskProfiler
            self._taskProfiler = TaskProfiler()

    def logTaskProfiles(self, name = None):
        if self._taskProfiler:
            self._taskProfiler.logProfiles(name)

    def flushTaskProfiles(self, name = None):
        if self._taskProfiler:
            self._taskProfiler.flush(name)

    def _setProfileTask(self, task):
        if self._taskProfileInfo.session:
            self._taskProfileInfo.session.release()
            self._taskProfileInfo.session = None
        self._taskProfileInfo = ScratchPad(taskFunc=task.getFunction(), taskArgs=task.getArgs(), task=task, profiled=False, session=None)
        task.setFunction(self._profileTask)
        task.setArgs([self._taskProfileInfo], True)
        return

    def _profileTask(self, profileInfo, task):
        appendTask = False
        taskArgs = profileInfo.taskArgs
        if taskArgs and taskArgs[-1] == task:
            appendTask = True
            taskArgs = taskArgs[:-1]
        task.setArgs(taskArgs, appendTask)
        task.setFunction(profileInfo.taskFunc)
        from direct.showbase.ProfileSession import ProfileSession
        profileSession = ProfileSession('profiled-task-%s' % task.getName(), Functor(profileInfo.taskFunc, *profileInfo.taskArgs))
        ret = profileSession.run()
        profileInfo.session = profileSession
        profileInfo.profiled = True
        return ret

    def _hasProfiledDesignatedTask(self):
        return self._taskProfileInfo.profiled

    def _getLastTaskProfileSession(self):
        return self._taskProfileInfo.session

    def _getRandomTask(self):
        now = globalClock.getFrameTime()
        avgFrameRate = globalClock.getAverageFrameRate()
        if avgFrameRate < 1e-05:
            avgFrameDur = 0.0
        else:
            avgFrameDur = 1.0 / globalClock.getAverageFrameRate()
        next = now + avgFrameDur
        tasks = self.mgr.getTasks()
        i = random.randrange(tasks.getNumTasks())
        task = tasks.getTask(i)
        while not isinstance(task, PythonTask) or task.getWakeTime() > next:
            tasks.removeTask(i)
            i = random.randrange(tasks.getNumTasks())
            task = tasks.getTask(i)

        return task

    def __repr__(self):
        return str(self.mgr)

    def doYield(self, frameStartTime, nextScheduledTaskTime):
        pass

    def _runTests(self):
        pass# decompiled 0 files: 0 okay, 1 failed, 0 verify failed
Example #31
0
class DistributedCogdoInterior(DistributedObject.DistributedObject):
    """
    """

    if __debug__:
        notify = DirectNotifyGlobal.directNotify.newCategory(
            'DistributedCogdoInterior')

    id = 0

    cageHeights = [11.36, 0.01]

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)

        self.toons = []
        self.activeIntervals = {}

        self.openSfx = base.loader.loadSfx(
            "phase_5/audio/sfx/elevator_door_open.mp3")
        self.closeSfx = base.loader.loadSfx(
            "phase_5/audio/sfx/elevator_door_close.mp3")

        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []

        self.distBldgDoId = None

        self._CogdoGameRepeat = ConfigVariableBool('cogdo-game-repeat',
                                                   0).getValue()

        # we increment this each time we come out of an elevator:
        self.currentFloor = -1

        self.elevatorName = self.__uniqueName('elevator')
        self.floorModel = None

        self.elevatorOutOpen = 0

        # initial cog positions vary based on the cog office model
        self.BottomFloor_SuitPositions = [
            Point3(0, 15, 0),
            Point3(10, 20, 0),
            Point3(-7, 24, 0),
            Point3(-10, 0, 0)
        ]
        self.BottomFloor_SuitHs = [75, 170, -91, -44]  # Heading angles

        self.Cubicle_SuitPositions = [
            Point3(0, 18, 0),
            Point3(10, 12, 0),
            Point3(-9, 11, 0),
            Point3(-3, 13, 0)
        ]
        self.Cubicle_SuitHs = [170, 56, -52, 10]

        self.BossOffice_SuitPositions = [
            Point3(0, 15, 0),
            Point3(10, 20, 0),
            Point3(-10, 6, 0),
            Point3(-17, 30, 0),
        ]
        self.BossOffice_SuitHs = [170, 120, 12, 38]

        self._wantBarrelRoom = ConfigVariableBool('cogdo-want-barrel-room',
                                                  0).getValue()
        self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom()
        self.brResults = [[], []]
        self.barrelRoomIntroTrack = None

        self.penthouseOutroTrack = None
        self.penthouseOutroChatDoneTrack = None
        self.penthouseIntroTrack = None

        self.waitMusic = base.loader.loadMusic(
            'phase_7/audio/bgm/encntr_toon_winning_indoor.mid')
        self.elevatorMusic = base.loader.loadMusic(
            'phase_7/audio/bgm/tt_elevator.mid')

        self.fsm = ClassicFSM.ClassicFSM(
            'DistributedCogdoInterior',
            [
                State.State('WaitForAllToonsInside',
                            self.enterWaitForAllToonsInside,
                            self.exitWaitForAllToonsInside, ['Elevator']),
                State.State('Elevator', self.enterElevator, self.exitElevator,
                            ['Game']),
                State.State('Game', self.enterGame, self.exitGame,
                            ['Resting', 'Failed', 'BattleIntro']),
                State.State('BarrelRoomIntro', self.enterBarrelRoomIntro,
                            self.exitBarrelRoomIntro,
                            ['CollectBarrels', 'Off']),
                State.State('CollectBarrels', self.enterCollectBarrels,
                            self.exitCollectBarrels,
                            ['BarrelRoomReward', 'Off']),
                State.State(
                    'BarrelRoomReward', self.enterBarrelRoomReward,
                    self.exitBarrelRoomReward,
                    ['Battle', 'ReservesJoining', 'BattleIntro', 'Off']),
                State.State('BattleIntro', self.enterBattleIntro,
                            self.exitBattleIntro,
                            ['Battle', 'ReservesJoining', 'Off']),
                State.State('Battle', self.enterBattle, self.exitBattle,
                            ['Resting', 'Reward', 'ReservesJoining']),
                State.State('ReservesJoining', self.enterReservesJoining,
                            self.exitReservesJoining, ['Battle']),
                State.State('Resting', self.enterResting, self.exitResting,
                            ['Elevator']),
                State.State('Reward', self.enterReward, self.exitReward,
                            ['Off']),
                State.State('Failed', self.enterFailed, self.exitFailed,
                            ['Off']),
                State.State('Off', self.enterOff, self.exitOff,
                            ['Elevator', 'WaitForAllToonsInside', 'Battle']),
            ],
            # Initial State
            'Off',
            # Final State
            'Off',
        )

        # make sure we're in the initial state
        self.fsm.enterInitialState()
        self._haveEntranceElevator = StateVar(False)
        self._stashEntranceElevator = StateVar(False)
        self._stashEntranceElevatorFC = FunctionCall(
            self._doStashEntranceElevator, self._haveEntranceElevator,
            self._stashEntranceElevator)
        self._entranceElevCallbacks = []
        self._doEntranceElevCallbacksFC = FunctionCall(
            self._doEntranceElevCallbacks, self._haveEntranceElevator)
        self.cage = None
        self.shopOwnerNpcId = None
        self.shopOwnerNpc = None
        self._movie = None
        self.SOSToonName = None
        self.FOType = None

    def setShopOwnerNpcId(self, npcId):
        self.shopOwnerNpcId = npcId

    def setSOSNpcId(self, npcId):
        self.SOSToonName = NPCToons.getNPCName(npcId)

    def setFOType(self, typeId):
        self.FOType = chr(typeId)

    def __uniqueName(self, name):
        DistributedCogdoInterior.id += 1
        return (name + '%d' % DistributedCogdoInterior.id)

    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.
        """
        assert (self.notify.debug("generate()"))
        DistributedObject.DistributedObject.generate(self)

        # listen for the generate event, which will be thrown after the
        # required fields are filled in
        self.announceGenerateName = self.uniqueName('generate')
        self.accept(self.announceGenerateName, self.handleAnnounceGenerate)

        # Load the elevator model
        self.elevatorModelIn = loader.loadModel(
            'phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
        self.leftDoorIn = self.elevatorModelIn.find('**/left-door')
        self.rightDoorIn = self.elevatorModelIn.find('**/right-door')

        self.elevatorModelOut = loader.loadModel(
            'phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
        self.leftDoorOut = self.elevatorModelOut.find('**/left-door')
        self.rightDoorOut = self.elevatorModelOut.find('**/right-door')

    def __makeShopOwnerNpc(self):
        if self.shopOwnerNpc:
            return
        self.shopOwnerNpc = NPCToons.createLocalNPC(self.shopOwnerNpcId)
        if not self.shopOwnerNpc:
            self.notify.warning(
                "No shopkeeper in this cogdominium, using FunnyFarm Sellbot FO NPCToons"
            )
            random.seed(self.doId)
            shopkeeper = random.randint(7001, 7009)
            self.shopOwnerNpc = NPCToons.createLocalNPC(shopkeeper)
        self.shopOwnerNpc.addActive()
        self.shopOwnerNpc.reparentTo(self.cage)
        self.shopOwnerNpc.setPosHpr(0, -2, 0, 180, 0, 0)
        self.shopOwnerNpc.loop('neutral')

    def setElevatorLights(self, elevatorModel):
        """
        Sets up the lights on the interior elevators to represent the
        number of floors in the building, and to light up the current
        floor number.
        """
        npc = elevatorModel.findAllMatches("**/floor_light_?;+s")
        for i in range(npc.getNumPaths()):
            np = npc.getPath(i)
            # Get the last character, and make it zero based:
            floor = int(np.getName()[-1:]) - 1

            if (floor == self.currentFloor):
                np.setColor(LIGHT_ON_COLOR)
            elif floor < self.layout.getNumGameFloors():
                if self.isBossFloor(self.currentFloor):
                    np.setColor(LIGHT_ON_COLOR)
                else:
                    np.setColor(LIGHT_OFF_COLOR)
            else:
                np.hide()

    def startAlertElevatorLightIval(self, elevatorModel):
        light = elevatorModel.find("**/floor_light_%s" %
                                   (self.currentFloor + 1))
        track = Sequence(Func(light.setColor, Vec4(1.0, 0.6, 0.6, 1.0)),
                         Wait(0.9), Func(light.setColor, LIGHT_ON_COLOR),
                         Wait(0.9))
        self.activeIntervals["alertElevatorLight"] = track
        track.loop()

    def stopAlertElevatorLightIval(self, elevatorModel):
        self.__finishInterval("alertElevatorLight")
        self.setElevatorLights(elevatorModel)

    def handleAnnounceGenerate(self, obj):
        """
        handleAnnounceGenerate is called after all of the required fields are
        filled in
        'obj' is another copy of self
        """
        self.ignore(self.announceGenerateName)

        self.cageDoorSfx = loader.loadSfx(
            'phase_5/audio/sfx/CHQ_SOS_cage_door.mp3')
        self.cageLowerSfx = loader.loadSfx(
            'phase_5/audio/sfx/CHQ_SOS_cage_lower.mp3')

        assert (self.notify.debug('joining DistributedCogdoInterior'))
        # Update the minigame AI to join our local toon doId
        self.sendUpdate('setAvatarJoined', [])

    def disable(self):
        assert (self.notify.debug('disable()'))
        self.fsm.requestFinalState()
        self.__cleanupIntervals()
        self.ignoreAll()
        self.__cleanup()
        self.__cleanupShopOwnerNpc()
        self.__cleanupPenthouseIntro()
        DistributedObject.DistributedObject.disable(self)

    def __cleanupShopOwnerNpc(self):
        if self.shopOwnerNpc:
            self.shopOwnerNpc.removeActive()
            self.shopOwnerNpc.delete()
            self.shopOwnerNpc = None

    def __cleanupPenthouseIntro(self):
        if hasattr(self, '_movie') and self._movie:
            self._movie.unload()
            self._movie = None

    def delete(self):
        assert (self.notify.debug('delete()'))
        self._stashEntranceElevatorFC.destroy()
        self._doEntranceElevCallbacksFC.destroy()
        self._haveEntranceElevator.destroy()
        self._stashEntranceElevator.destroy()
        self._entranceElevCallbacks = None
        del self.waitMusic
        del self.elevatorMusic
        del self.openSfx
        del self.closeSfx
        del self.fsm
        # No more battle multiplier
        base.localAvatar.inventory.setBattleCreditMultiplier(1)
        DistributedObject.DistributedObject.delete(self)

    def isBossFloor(self, floorNum):
        if self.layout.hasBossBattle():
            if self.layout.getBossBattleFloor() == floorNum:
                return True
        return False

    def __cleanup(self):
        self.toons = []
        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []
        # Clean up elevator models
        if (self.elevatorModelIn != None):
            self.elevatorModelIn.removeNode()
        if (self.elevatorModelOut != None):
            self.elevatorModelOut.removeNode()
        # Clean up current floor
        if (self.floorModel != None):
            self.floorModel.removeNode()
        # Clean up current cage
        if (self.cage != None):
            self.cage = None
        # Clean up current barrel room
        if (self.barrelRoom != None):
            self.barrelRoom.destroy()
            self.barrelRoom = None
        self.leftDoorIn = None
        self.rightDoorIn = None
        self.leftDoorOut = None
        self.rightDoorOut = None

    def __addToon(self, toon):
        assert (self.notify.debug('addToon(%d)' % toon.doId))
        self.accept(toon.uniqueName('disable'),
                    self.__handleUnexpectedExit,
                    extraArgs=[toon])

    def __handleUnexpectedExit(self, toon):
        self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId)
        self.__removeToon(toon, unexpected=1)

    def __removeToon(self, toon, unexpected=0):
        assert (self.notify.debug('removeToon() - toon: %d' % toon.doId))
        if (self.toons.count(toon) == 1):
            self.toons.remove(toon)
        self.ignore(toon.uniqueName('disable'))

    def __finishInterval(self, name):
        """ Force the specified interval to jump to the end
        """
        if (name in self.activeIntervals):
            interval = self.activeIntervals[name]
            if (interval.isPlaying()):
                assert(self.notify.debug('finishInterval(): %s' % \
                        interval.getName()))
                interval.finish()

    def __cleanupIntervals(self):
        for interval in list(self.activeIntervals.values()):
            interval.finish()
        self.activeIntervals = {}

    def __closeInElevator(self):
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)

    ##### Messages from the server #####

    def getZoneId(self):
        return self.zoneId

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def getExtZoneId(self):
        return self.extZoneId

    def setExtZoneId(self, extZoneId):
        self.extZoneId = extZoneId

    def getDistBldgDoId(self):
        return self.distBldgDoId

    def setDistBldgDoId(self, distBldgDoId):
        self.distBldgDoId = distBldgDoId

    def setNumFloors(self, numFloors):
        self.layout = CogdoLayout(numFloors)

    def getToonIds(self):
        toonIds = []
        for toon in self.toons:
            toonIds.append(toon.doId)
        return toonIds

    def setToons(self, toonIds, hack):
        assert (self.notify.debug('setToons(): %s' % toonIds))
        self.toonIds = toonIds
        oldtoons = self.toons
        self.toons = []
        for toonId in toonIds:
            if (toonId != 0):
                if (toonId in self.cr.doId2do):
                    toon = self.cr.doId2do[toonId]
                    toon.stopSmooth()
                    self.toons.append(toon)
                    if (oldtoons.count(toon) == 0):
                        assert(self.notify.debug('setToons() - new toon: %d' % \
                                toon.doId))
                        self.__addToon(toon)
                else:
                    self.notify.warning('setToons() - no toon: %d' % toonId)
        for toon in oldtoons:
            if (self.toons.count(toon) == 0):
                self.__removeToon(toon)

    def setSuits(self, suitIds, reserveIds, values):
        assert(self.notify.debug('setSuits(): active %s reserve %s values %s' \
                % (suitIds, reserveIds, values)))
        oldsuits = self.suits
        self.suits = []
        self.joiningReserves = []
        for suitId in suitIds:
            if (suitId in self.cr.doId2do):
                suit = self.cr.doId2do[suitId]
                self.suits.append(suit)
                # Set this on the client
                suit.fsm.request('Battle')
                # This will allow client to respond to setState() from the
                # server from here on out
                suit.buildingSuit = 1
                suit.reparentTo(render)
                if (oldsuits.count(suit) == 0):
                    assert(self.notify.debug('setSuits() suit: %d joining' % \
                        suit.doId))
                    self.joiningReserves.append(suit)
            else:
                self.notify.warning('setSuits() - no suit: %d' % suitId)
        self.reserveSuits = []
        assert (len(reserveIds) == len(values))
        for index in range(len(reserveIds)):
            suitId = reserveIds[index]
            if (suitId in self.cr.doId2do):
                suit = self.cr.doId2do[suitId]
                self.reserveSuits.append((suit, values[index]))
            else:
                self.notify.warning('setSuits() - no suit: %d' % suitId)

        if (len(self.joiningReserves) > 0):
            assert (self.notify.debug('setSuits() reserves joining'))
            self.fsm.request('ReservesJoining')

    def setState(self, state, timestamp):
        assert(self.notify.debug("setState(%s, %d)" % \
                                (state, timestamp)))
        self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])

    def stashElevatorIn(self, stash=True):
        self._stashEntranceElevator.set(stash)

    def getEntranceElevator(self, callback):
        if self._haveEntranceElevator.get():
            callback(self.elevIn)
        else:
            self._entranceElevCallbacks.append(callback)

    def _doEntranceElevCallbacks(self, haveElev):
        if haveElev:
            while len(self._entranceElevCallbacks):
                cbs = self._entranceElevCallbacks[:]
                self._entranceElevCallbacks = []
                for callback in cbs:
                    callback(self.elevIn)

    def _doStashEntranceElevator(self, haveElev, doStash):
        if haveElev:
            if doStash:
                self.elevIn.stash()
            else:
                self.elevIn.unstash()

    ##### Messages to the server #####

    def d_elevatorDone(self):
        assert (self.notify.debug('network:elevatorDone(%d)' %
                                  base.localAvatar.doId))
        self.sendUpdate('elevatorDone', [])

    def d_reserveJoinDone(self):
        assert (self.notify.debug('network:reserveJoinDone(%d)' %
                                  base.localAvatar.doId))
        self.sendUpdate('reserveJoinDone', [])

    # Specific State Functions

    ##### Off state #####

    def enterOff(self, ts=0):
        assert (self.notify.debug('enterOff()'))
        messenger.send('sellbotFieldOfficeChanged', [False])
        return None

    def exitOff(self):
        return None

    ##### WaitForAllToonsInside state #####

    def enterWaitForAllToonsInside(self, ts=0):
        assert (self.notify.debug('enterWaitForAllToonsInside()'))
        base.transitions.fadeOut(0)
        return None

    def exitWaitForAllToonsInside(self):
        return None

    def enterGame(self, ts=0):
        assert (self.notify.debug('enterElevator()'))
        base.cr.forbidCheesyEffects(1)

    def exitGame(self):
        base.cr.forbidCheesyEffects(0)

    ##### Elevator state #####

    def __playElevator(self, ts, name, callback):
        # Load the floor model

        SuitHs = []  # Heading angles
        SuitPositions = []

        if self.floorModel:
            self.floorModel.removeNode()
            self.floorModel = None
        if self.cage:
            self.cage = None

        if (self.currentFloor == 0):
            # bottom floor
            SuitHs = self.BottomFloor_SuitHs
            SuitPositions = self.BottomFloor_SuitPositions
        if self.isBossFloor(self.currentFloor):
            # Top floor
            self.barrelRoom.unload()
            self.floorModel = loader.loadModel(
                'phase_5/models/cogdominium/tt_m_ara_crg_penthouse')
            self.cage = self.floorModel.find('**/cage')
            pos = self.cage.getPos()
            self.cagePos = []
            for height in self.cageHeights:
                self.cagePos.append(Point3(pos[0], pos[1], height))

            self.cageDoor = self.floorModel.find('**/cage_door')
            self.cageDoor.wrtReparentTo(self.cage)
            if self.FOType:
                paintingModelName = PAINTING_DICT.get(self.FOType)
                for i in range(4):
                    paintingModel = loader.loadModel(
                        'phase_5/models/cogdominium/%s' % paintingModelName)
                    loc = self.floorModel.find('**/loc_painting%d' % (i + 1))
                    paintingModel.reparentTo(loc)
            SuitHs = self.BossOffice_SuitHs
            SuitPositions = self.BossOffice_SuitPositions
            self.__makeShopOwnerNpc()
        else:
            if self._wantBarrelRoom:
                self.barrelRoom.load()
                self.barrelRoom.hide()
            # middle floor
            SuitHs = self.Cubicle_SuitHs
            SuitPositions = self.Cubicle_SuitPositions

        if self.floorModel:
            self.floorModel.reparentTo(render)

            if self.isBossFloor(self.currentFloor):
                self.notify.debug('Load boss_suit_office')
                elevIn = self.floorModel.find(
                    CogdoGameConsts.PenthouseElevatorInPath).copyTo(render)
                elevOut = self.floorModel.find(
                    CogdoGameConsts.PenthouseElevatorOutPath)
                frame = self.elevatorModelOut.find('**/frame')
                if not frame.isEmpty():
                    frame.hide()
                frame = self.elevatorModelIn.find('**/frame')
                if not frame.isEmpty():
                    frame.hide()
                self.elevatorModelOut.reparentTo(elevOut)
            else:
                # We need to name this something more useful (and we'll need the
                # location of the opposite elevator as well)
                elevIn = self.floorModel.find('**/elevator-in')
                elevOut = self.floorModel.find('**/elevator-out')
        elif self._wantBarrelRoom and self.barrelRoom.isLoaded():
            elevIn = self.barrelRoom.dummyElevInNode
            elevOut = self.barrelRoom.model.find(
                CogdoBarrelRoomConsts.BarrelRoomElevatorOutPath)
            y = elevOut.getY(render)
            elevOut = elevOut.copyTo(render)
            elevOut.setY(render, y - 0.75)
        else:
            # TODO: TEMP
            floorModel = loader.loadModel(
                'phase_7/models/modules/boss_suit_office')
            elevIn = floorModel.find('**/elevator-in').copyTo(render)
            elevOut = floorModel.find('**/elevator-out').copyTo(render)
            floorModel.removeNode()
        self.elevIn = elevIn

        # store elevOut until it's needed
        self.elevOut = elevOut
        self._haveEntranceElevator.set(True)

        # Position the suits

        assert (len(self.suits) <= 4)
        for index in range(len(self.suits)):
            assert(self.notify.debug('setting suit: %d to pos: %s' % \
                (self.suits[index].doId, SuitPositions[index])))
            self.suits[index].setPos(SuitPositions[index])
            if (len(self.suits) > 2):
                self.suits[index].setH(SuitHs[index])
            else:
                self.suits[index].setH(
                    170
                )  # if there's 2 or 1 suits, make them face fwd since there's no other suits they would be to be talking to
            self.suits[index].loop('neutral')

        # Position the toons
        for toon in self.toons:
            toon.reparentTo(self.elevatorModelIn)
            assert (self.toonIds.count(toon.doId) == 1)
            index = self.toonIds.index(toon.doId)
            assert (index >= 0 and index <= 3)
            toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1],
                        ElevatorPoints[index][2])
            toon.setHpr(180, 0, 0)
            toon.loop('neutral')

        # Show the elevator and position it in the correct place for the floor
        self.elevatorModelIn.reparentTo(elevIn)
        # Start with the doors in closed position
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)

        # Position the camera behind the toons
        camera.reparentTo(self.elevatorModelIn)
        camera.setH(180)
        camera.setP(0)
        camera.setPos(0, 14, 4)

        # Play elevator music
        base.playMusic(self.elevatorMusic, looping=1, volume=0.8)

        # Ride the elevator, then open the doors.
        track = Sequence(
            Func(base.transitions.noTransitions),
            ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL),
            ElevatorUtils.getOpenInterval(self,
                                          self.leftDoorIn,
                                          self.rightDoorIn,
                                          self.openSfx,
                                          None,
                                          type=ELEVATOR_NORMAL),
            Func(camera.wrtReparentTo, render),
        )

        for toon in self.toons:
            track.append(Func(toon.wrtReparentTo, render))
        track.append(Func(callback))
        track.start(ts)
        self.activeIntervals[name] = track

    def enterElevator(self, ts=0):
        # Load model for the current floor and the suit models for the floor
        assert (self.notify.debug('enterElevator()'))

        if not self._CogdoGameRepeat:
            self.currentFloor += 1
        self.cr.playGame.getPlace().currentFloor = self.currentFloor
        self.setElevatorLights(self.elevatorModelIn)
        self.setElevatorLights(self.elevatorModelOut)

        # hide elevator from previous floor (if any)
        # unless it's the top floor, in that case leave it where it is
        if not self.isBossFloor(self.currentFloor):
            self.elevatorModelOut.detachNode()
            messenger.send('sellbotFieldOfficeChanged', [True])
        else:
            self._movie = CogdoElevatorMovie()
            self._movie.load()
            self._movie.play()

        self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone)

        # Get the floor multiplier
        mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
        # Now set the inventory battleCreditMult
        base.localAvatar.inventory.setBattleCreditMultiplier(mult)

    def __handleElevatorDone(self):
        assert (self.notify.debug('handleElevatorDone()'))
        self.d_elevatorDone()

    def exitElevator(self):
        self.elevatorMusic.stop()
        if self._movie:
            self._movie.end()
            self.__cleanupPenthouseIntro()
        self.__finishInterval(self.elevatorName)
        return None

    def __setupBarrelRoom(self):
        base.cr.playGame.getPlace().fsm.request('stopped')
        base.transitions.irisOut(0.0)
        self.elevatorModelIn.detachNode()
        self._showExitElevator()
        self.barrelRoom.show()
        self.barrelRoom.placeToonsAtEntrance(self.toons)

    def barrelRoomIntroDone(self):
        self.sendUpdate('toonBarrelRoomIntroDone', [])

    def enterBarrelRoomIntro(self, ts=0):
        if not self.isBossFloor(self.currentFloor):
            if self._wantBarrelRoom:
                self.__setupBarrelRoom()
                self.barrelRoomIntroTrack, trackName = self.barrelRoom.getIntroInterval(
                )
                self.barrelRoomIntroDoneEvent = trackName
                self.accept(self.barrelRoomIntroDoneEvent,
                            self.barrelRoomIntroDone)
                self.activeIntervals[trackName] = self.barrelRoomIntroTrack
                self.barrelRoomIntroTrack.start(ts)
            else:
                self._showExitElevator()

    def exitBarrelRoomIntro(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            self.ignore(self.barrelRoomIntroDoneEvent)
            if self.barrelRoomIntroTrack:
                self.barrelRoomIntroTrack.finish()
                DelayDelete.cleanupDelayDeletes(self.barrelRoomIntroTrack)
                self.barrelRoomIntroTrack = None
        return

    def __handleLocalToonLeftBarrelRoom(self):
        self.notify.info('Local toon teleported out of barrel room.')
        self.sendUpdate('toonLeftBarrelRoom', [])
        self.barrelRoom.deactivate()

    def enterCollectBarrels(self, ts=0):
        if not self.isBossFloor(self.currentFloor):
            if self._wantBarrelRoom:
                self.acceptOnce('localToonLeft',
                                self.__handleLocalToonLeftBarrelRoom)
                self.barrelRoom.activate()
                base.playMusic(self.waitMusic, looping=1, volume=0.7)

    def exitCollectBarrels(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            self.ignore('localToonLeft')
            self.barrelRoom.deactivate()
            self.waitMusic.stop()

    def __brRewardDone(self, task=None):
        self.notify.info('Toon finished watching the barrel room reward.')
        self.sendUpdate('toonBarrelRoomRewardDone', [])

    def setBarrelRoomReward(self, avIds, laffs):
        self.brResults = [avIds, laffs]
        self.barrelRoom.setRewardResults(self.brResults)

    def enterBarrelRoomReward(self, ts=0):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            base.cr.playGame.getPlace().fsm.request('stopped')
            self.startAlertElevatorLightIval(self.elevatorModelOut)
            track, trackName = self.barrelRoom.showRewardUi(
                self.brResults, callback=self.__brRewardDone)
            self.activeIntervals[trackName] = track
            track.start()
            self.barrelRoom.placeToonsNearBattle(self.toons)

    def exitBarrelRoomReward(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            base.cr.playGame.getPlace().fsm.request('walk')
            self.stopAlertElevatorLightIval(self.elevatorModelOut)
            self.barrelRoom.hideRewardUi()

    def enterBattleIntro(self, ts=0):
        self._movie = CogdoExecutiveSuiteIntro(self.shopOwnerNpc)
        self._movie.load()
        self._movie.play()

    def exitBattleIntro(self):
        self._movie.end()
        self.__cleanupPenthouseIntro()

    ##### Battle state #####

    def __playCloseElevatorOut(self, name, delay=0):
        # Close the elevator doors
        track = Sequence(
            Wait(delay + SUIT_LEAVE_ELEVATOR_TIME),
            Parallel(
                SoundInterval(self.closeSfx),
                LerpPosInterval(
                    self.leftDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL),
                    startPos=Point3(0, 0, 0),
                    blendType='easeOut'),
                LerpPosInterval(
                    self.rightDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL),
                    startPos=Point3(0, 0, 0),
                    blendType='easeOut')),
        )
        track.start()
        self.activeIntervals[name] = track

    def enterBattle(self, ts=0):
        assert (self.notify.debug('enterBattle()'))

        if self._wantBarrelRoom and self.elevatorOutOpen == 1:
            self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'),
                                        delay=2)
            camera.setPos(0, -15, 6)
            camera.headsUp(self.elevatorModelOut)

    def _showExitElevator(self):

        # now that we're in the barrel room, show the exit elevator
        # Show the elevator and position it in the correct place for the floor
        self.elevatorModelOut.reparentTo(self.elevOut)
        # Start with the doors in closed position
        self.leftDoorOut.setPos(3.5, 0, 0)
        self.rightDoorOut.setPos(-3.5, 0, 0)

        if not (self._wantBarrelRoom and self.elevatorOutOpen == 1):
            self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'))
            # Watch reserve suits as they walk from the elevator
            camera.setPos(0, -15, 6)
            camera.headsUp(self.elevatorModelOut)
        return None

    def exitBattle(self):
        if (self.elevatorOutOpen == 1):
            self.__finishInterval(self.uniqueName('close-out-elevator'))
            self.elevatorOutOpen = 0
        return None

    ##### ReservesJoining state #####

    def __playReservesJoining(self, ts, name, callback):
        # Position the joining suits
        index = 0
        assert (len(self.joiningReserves) <= 4)
        for suit in self.joiningReserves:
            suit.reparentTo(render)
            suit.setPos(
                self.elevatorModelOut,
                Point3(ElevatorPoints[index][0], ElevatorPoints[index][1],
                       ElevatorPoints[index][2]))
            index += 1
            suit.setH(180)
            suit.loop('neutral')

        if len(self.suits) == len(self.joiningReserves):
            camSequence = Sequence(Func(camera.wrtReparentTo, localAvatar),
                                   Func(camera.setPos, Point3(0, 5, 5)),
                                   Func(camera.headsUp, self.elevatorModelOut))
        else:
            camSequence = Sequence(
                Func(camera.wrtReparentTo, self.elevatorModelOut),
                Func(camera.setPos, Point3(0, -8, 2)),
                Func(camera.setHpr, Vec3(0, 10, 0)))

        # Aim the camera at the far elevator
        track = Sequence(
            Func(camera.wrtReparentTo, self.elevatorModelOut),
            Func(camera.setPos, Point3(0, -8, 2)),
            Func(camera.setHpr, Vec3(0, 10, 0)),

            # Open the elevator doors
            Parallel(
                SoundInterval(self.openSfx),
                LerpPosInterval(
                    self.leftDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    Point3(0, 0, 0),
                    startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL),
                    blendType='easeOut'),
                LerpPosInterval(
                    self.rightDoorOut,
                    ElevatorData[ELEVATOR_NORMAL]['closeTime'],
                    Point3(0, 0, 0),
                    startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL),
                    blendType='easeOut'),
            ),

            # Hold the camera angle for a couple of beats
            Wait(SUIT_HOLD_ELEVATOR_TIME),

            # Reparent the camera to render (enterWaitForInput will
            # position it properly again by the battle)
            Func(camera.wrtReparentTo, render),
            Func(callback),
        )
        track.start(ts)
        self.activeIntervals[name] = track

    def enterReservesJoining(self, ts=0):
        assert (self.notify.debug('enterReservesJoining()'))
        self.__playReservesJoining(ts, self.uniqueName('reserves-joining'),
                                   self.__handleReserveJoinDone)
        return None

    def __handleReserveJoinDone(self):
        assert (self.notify.debug('handleReserveJoinDone()'))
        self.joiningReserves = []
        self.elevatorOutOpen = 1
        self.d_reserveJoinDone()

    def exitReservesJoining(self):
        self.__finishInterval(self.uniqueName('reserves-joining'))
        return None

    ##### Resting state #####

    def enterResting(self, ts=0):
        assert (self.notify.debug('enterResting()'))
        self._showExitElevator()
        self._setAvPosFDC = FrameDelayedCall('setAvPos', self._setAvPosToExit)
        if self._wantBarrelRoom:
            self.barrelRoom.showBattleAreaLight(True)
        base.playMusic(self.waitMusic, looping=1, volume=0.7)
        self.__closeInElevator()
        self._haveEntranceElevator.set(False)
        self._stashEntranceElevator.set(False)
        return

    def _setAvPosToExit(self):
        base.localAvatar.setPos(self.elevOut, 0, -10, 0)
        base.localAvatar.setHpr(self.elevOut, 0, 0, 0)
        base.cr.playGame.getPlace().fsm.request('walk')

    def exitResting(self):
        self._setAvPosFDC.destroy()
        self.waitMusic.stop()
        return

    ##### Reward state #####

    def enterReward(self, ts=0):
        assert (self.notify.debug('enterReward()'))
        if self.isBossFloor(self.currentFloor):
            self.penthouseOutroTrack = self.__outroPenthouse()
            self.penthouseOutroTrack.start(ts)
        else:
            self.exitCogdoBuilding()
        return None

    def exitReward(self):
        self.notify.debug('exitReward')
        if self.penthouseOutroTrack:
            self.penthouseOutroTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.penthouseOutroTrack)
            self.penthouseOutroTrack = None
            if not self.penthouseOutroChatDoneTrack:
                self.notify.debug(
                    'exitReward: instanting outroPenthouseChatDone track')
                self.__outroPenthouseChatDone()
            self.penthouseOutroChatDoneTrack.finish()
            self.penthouseOutroChatDoneTrack = None
        return

    ##### Failed state #####

    def enterFailed(self, ts=0):
        self.exitCogdoBuilding()
        return None

    def exitFailed(self):
        self.notify.debug('exitFailed()')
        self.exitCogdoBuilding()
        return None

    def exitCogdoBuilding(self):
        if base.localAvatar.hp < 0:
            return
        base.localAvatar.b_setParent(ToontownGlobals.SPHidden)
        request = {
            'loader': ZoneUtil.getBranchLoaderName(self.extZoneId),
            'where': ZoneUtil.getToonWhereName(self.extZoneId),
            'how': 'elevatorIn',
            'hoodId': ZoneUtil.getHoodId(self.extZoneId),
            'zoneId': self.extZoneId,
            'shardId': None,
            'avId': -1,
            'bldgDoId': self.distBldgDoId
        }
        messenger.send('DSIDoneEvent', [request])
        return

    def displayBadges(self):
        numFloors = self.layout.getNumGameFloors()
        if numFloors > 5 or numFloors < 3:
            pass
        else:
            self.notify.warning('Invalid floor number for display badges.')
        for player in range(len(self.toons)):
            goldBadge = loader.loadModel(
                'phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy')
            goldBadge.setScale(1.2)
            goldNode = render.find('**/gold_0' + str(player + 1))
            goldBadge.reparentTo(goldNode)
            for floor in range(numFloors):
                silverBadge = loader.loadModel(
                    'phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam')
                silverBadge.setScale(1.2)
                silverNode = render.find('**/silver_0' + str(floor * 4 +
                                                             (player + 1)))
                silverBadge.reparentTo(silverNode)

    ##### Outro state #####

    def __outroPenthouse(self):
        avatar = base.localAvatar
        trackName = '__outroPenthouse-%d' % avatar.doId
        track = Parallel(name=trackName)
        base.cr.playGame.getPlace().fsm.request('stopped')
        speech = TTLocalizer.CogdoExecutiveSuiteToonThankYou % self.SOSToonName
        track.append(
            Sequence(
                Func(camera.wrtReparentTo, localAvatar),
                Func(camera.setPos, 0, -9, 9),
                Func(camera.lookAt, Point3(5, 15, 0)),
                Parallel(
                    self.cage.posInterval(0.75,
                                          self.cagePos[1],
                                          blendType='easeOut'),
                    SoundInterval(self.cageLowerSfx, duration=0.5)),
                Parallel(
                    self.cageDoor.hprInterval(0.5,
                                              VBase3(0, 90, 0),
                                              blendType='easeOut'),
                    Sequence(SoundInterval(self.cageDoorSfx), duration=0)),
                Wait(0.25), Func(self.shopOwnerNpc.wrtReparentTo, render),
                Func(self.shopOwnerNpc.setScale, 1),
                Func(self.shopOwnerNpc.loop, 'walk'),
                Func(self.shopOwnerNpc.headsUp, Point3(0, 10, 0)),
                ParallelEndTogether(
                    self.shopOwnerNpc.posInterval(1.5, Point3(0, 10, 0)),
                    self.shopOwnerNpc.hprInterval(0.5,
                                                  VBase3(180, 0, 0),
                                                  blendType='easeInOut')),
                Func(self.shopOwnerNpc.setChatAbsolute,
                     TTLocalizer.CagedToonYippee, CFSpeech),
                ActorInterval(self.shopOwnerNpc, 'jump'),
                Func(self.shopOwnerNpc.loop, 'neutral'),
                Func(self.shopOwnerNpc.headsUp, localAvatar),
                Func(self.shopOwnerNpc.setLocalPageChat, speech, 0),
                Func(camera.lookAt, self.shopOwnerNpc, Point3(0, 0, 2))))
        self.activeIntervals[trackName] = track
        self.accept('doneChatPage', self.__outroPenthouseChatDone)
        return track

    def __outroPenthouseChatDone(self, elapsed=None):
        self.shopOwnerNpc.setChatAbsolute(
            TTLocalizer.CogdoExecutiveSuiteToonBye, CFSpeech)
        self.ignore('doneChatPage')
        track = Parallel(
            Sequence(ActorInterval(self.shopOwnerNpc, 'wave'),
                     Func(self.shopOwnerNpc.loop, 'neutral')),
            Sequence(
                Wait(2.0),
                Func(self.exitCogdoBuilding),
                Func(base.camLens.setMinFov, ToontownGlobals.DefaultCameraFov),
            ),
        )
        track.start()
        self.penthouseOutroChatDoneTrack = track
class DistributedPlayerSimpleShip(DistributedSimpleShip):
    RepairSpotFadeAfter = 2.0
    RepairSpotFadeDur = 3.0

    def __init__(self, cr):
        DistributedSimpleShip.__init__(self, cr)
        self._respawnLocation = None
        self.checkAnchor = None
        self.lastAttacked = None
        self.threatLevel = 0
        self.openPort = 0
        self.allowCrewState = True
        self.allowFriendState = True
        self.allowGuildState = False
        self.allowPublicState = False
        self._repairSpotMgr = ShipRepairSpotMgr(self.cr)
        self._team = PiratesGlobals.PLAYER_TEAM
        self.badInitTeam = None
        self.prevLocStack = None


    def generate(self):
        DistributedSimpleShip.generate(self)
        self._repairSpotWoodPile = None
        self._repairSpotWoodPiles = { }
        self._repairSpotHole = None
        self._repairSpotHoleFixed = None
        self._repairSpotHoles = { }
        self._repairSpotIvals = { }
        self._wheelInUse = StateVar(False)


    def announceGenerate(self):
        self._respawnLocation = None
        self._respawnResponseDelayedCall = None
        DistributedSimpleShip.announceGenerate(self)
        self._repairSpotMgr.setShipId(self.doId)
        if self.badInitTeam != None:
            self._verifyTeam(self.badInitTeam)



    def disable(self):
        self._wheelInUse.destroy()
        if self._respawnResponseDelayedCall:
            self._respawnResponseDelayedCall.destroy()
            self._respawnResponseDelayedCall = None

        if self.checkAnchor:
            self.checkAnchor.remove()
            self.checkAnchor = None

        self._repairSpotMgr.destroy()
        for ival in self._repairSpotIvals.itervalues():
            ival.pause()

        del self._repairSpotIvals
        self.prevLocStack = None
        DistributedSimpleShip.disable(self)


    def calculateLook(self):
        team = self.getTeam()
        if team == PiratesGlobals.PLAYER_TEAM:
            if self.getSiegeTeam() == 1:
                self.style = ShipGlobals.Styles.French
            elif self.getSiegeTeam() == 2:
                self.style = ShipGlobals.Styles.Spanish




    def getNPCship(self):
        return False


    def setShipClass(self, shipClass):
        DistributedSimpleShip.setShipClass(self, shipClass)
        self._repairSpotMgr.updateShipClass(self.shipClass)


    def setHealthState(self, health):
        DistributedSimpleShip.setHealthState(self, health)
        self._repairSpotMgr.updateHealth(self.healthState)


    def setMastStates(self, mainMast1, mainMast2, mainMast3, aftMast, foreMast):
        DistributedSimpleShip.setMastStates(self, mainMast1, mainMast2, mainMast3, aftMast, foreMast)
        self._repairSpotMgr.updateSpeed(100.0 * self.Sp / self.maxSp)


    def setArmorStates(self, rear, left, right):
        DistributedSimpleShip.setArmorStates(self, rear, left, right)
        self._repairSpotMgr.updateArmor((rear + left + right) / 3.0)


    def setWillFullyRepairShip(self, willFullyRepairShip):
        self._repairSpotMgr.updateWillBeFullHealth(willFullyRepairShip)


    def setupLocalStats(self):
        DistributedSimpleShip.setupLocalStats(self)


    def setOpenPort(self, portId):
        oldPort = self.openPort
        self.openPort = portId
        if localAvatar.ship and localAvatar.ship.getDoId() == self.getDoId():
            messenger.send('LocalAvatar_Ship_OpenPort_Update', [
                portId,
                oldPort])



    def getOpenPort(self):
        return self.openPort


    def isAtOpenPort(self):
        portDoId = localAvatar.getPort()
        portObj = base.cr.doId2do.get(portDoId, None)
        if self.threatLevel < EnemyGlobals.SHIP_THREAT_NAVY_HUNTERS:
            return 1
        elif portObj and portObj.uniqueId == EnemyGlobals.OPEN_PORT_DICT.get(self.openPort):
            return 1
        else:
            return 0


    def setThreatLevel(self, threatLevel):
        if threatLevel != self.threatLevel:
            self.threatLevel = threatLevel
            self.updateNametag()
            if localAvatar.ship and localAvatar.ship.getDoId() == self.getDoId():
                messenger.send('LocalAvatar_Ship_ThreatLevel_Update', [
                    threatLevel])

            self.checkAbleDropAnchor()



    def getThreatLevel(self):
        if base.config.GetBool('want-ship-threat', 1):
            return self.threatLevel
        else:
            return EnemyGlobals.SHIP_THREAT_ATTACK_BACK


    def getOpenPort(self):
        return self.openPort


    def sunkAShipFanfare(self, shipToAttackDoId):
        if localAvatar.ship and localAvatar.ship == self:
            if localAvatar.ship.getSiegeTeam():
                return None

            attackMessage = HighSeasGlobals.getShipSunkMessage()
            if attackMessage:
                base.localAvatar.guiMgr.queueInstructionMessage(attackMessage[0], attackMessage[1], None, 1.0, messageCategory = MessageGlobals.MSG_CAT_SUNK_SHIP)




    def setSiegeTeam(self, team):
        different = team != self.getSiegeTeam()
        DistributedSimpleShip.setSiegeTeam(self, team)
        if different:
            self._doSiegeAndPVPTeamColors()
            self._repairSpotMgr.updateSiegeTeam(team)
            minimapObj = self.getMinimapObject()
            if minimapObj:
                minimapObj.setSiegeTeam(team)



    setSiegeTeam = report(types = [
        'args'], dConfigParam = 'shipdeploy')(setSiegeTeam)

    def _doSiegeAndPVPTeamColors(self):
        if self.getPVPTeam():
            self._doPVPTeamColors()
        elif self.getSiegeTeam():
            pass



    def _doPVPTeamColors(self):
        pass


    def getWheelInUseSV(self):
        return self._wheelInUse


    def setWheelInUse(self, wheelInUse):
        DistributedSimpleShip.setWheelInUse(self, wheelInUse)
        self._wheelInUse.set(wheelInUse)


    def canTakeWheel(self, wheel, av):
        available = True
        if self.queryGameState() in ('Pinned', 'Sinking', 'Sunk', 'OtherShipBoarded'):
            base.localAvatar.guiMgr.createWarning(PLocalizer.ShipPinnedWarning, PiratesGuiGlobals.TextFG6)
            available = False
        elif self.isFishing and base.localAvatar.getDoId() != self.ownerId:
            base.localAvatar.guiMgr.createWarning(PLocalizer.OnlyCaptainCanUseWarning, PiratesGuiGlobals.TextFG6)
            available = False
        elif wheel.getUserId() and base.localAvatar.getDoId() != self.ownerId:
            base.localAvatar.guiMgr.createWarning(PLocalizer.AlreadyInUseWarning, PiratesGuiGlobals.TextFG6)
            available = False

        return available


    def setRespawnLocation(self, parentId, zoneId):
        self._respawnLocation = (parentId, zoneId)


    def setLocation(self, parentId, zoneId):
        DistributedSimpleShip.setLocation(self, parentId, zoneId)
        if self._respawnLocation is not None and self._respawnLocation == (parentId, zoneId):
            self._respawnLocation = None
            if not self._respawnResponseDelayedCall:
                self._respawnResponseDelayedCall = FrameDelayedCall('PlayerShip-respawnLocation-gridInterestComplete', Functor(base.cr.setAllInterestsCompleteCallback, self._sendRespawnLocationResponse))




    def _sendRespawnLocationResponse(self):
        self.sendUpdate('clientReachedRespawnLocation')
        self._respawnResponseDelayedCall = None


    def recoverFromSunk(self):
        self.lastAttacked = None
        DistributedSimpleShip.recoverFromSunk(self)


    def attacked(self):
        self.lastAttacked = globalClock.getFrameTime()
        if self.getSiegeTeam() and not (self.checkAnchor):
            self.checkAbleDropAnchor()

    def attackTimerRemaining(self):
        timer = 0
        if self.lastAttacked:
            timer = int(30 - globalClock.getFrameTime() - self.lastAttacked)
        return timer


    def _DistributedPlayerSimpleShip__recheckAbleDropAnchor(self, task):
        self.checkAnchor = None
        self.checkAbleDropAnchor()

    def checkAbleDropAnchor(self):
        PiratesGuiGlobals = PiratesGuiGlobals
        import pirates.piratesgui
        if localAvatar.doId == self.steeringAvId:
            if self.shipStatusDisplay:
                if localAvatar.getPort():
                    remaining = self.attackTimerRemaining()
                    if self.getSiegeTeam() and remaining > 0:
                        self.shipStatusDisplay.disableAnchorButton()
                        localAvatar.guiMgr.createWarning(PLocalizer.CannotDockYet % remaining, PiratesGuiGlobals.TextFG6)
                        self.checkAnchor = taskMgr.doMethodLater(remaining, self._DistributedPlayerSimpleShip__recheckAbleDropAnchor, 'checkAnchor')
                    elif self.isAtOpenPort():
                        self.shipStatusDisplay.enableAnchorButton()
                    else:
                        self.shipStatusDisplay.disableAnchorButton()
                        self.shipStatusDisplay.tellWrongPort()
                else:
                    self.shipStatusDisplay.disableAnchorButton()
                    self.shipStatusDisplay.hideWrongPort()




    def _addRepairSpotModels(self):
        if not self._repairSpotWoodPile:
            self._repairSpotWoodPile = loader.loadModel('models/props/repair_spot_wood')
            collFloors = self._repairSpotWoodPile.find('**/collision_floor')
            if not collFloors.isEmpty():
                collideMask = collFloors.getCollideMask()
                collideMask ^= PiratesGlobals.FloorBitmask
                collideMask |= PiratesGlobals.ShipFloorBitmask
                collFloors.setCollideMask(collideMask)


        for locIndex in PVPGlobals.ShipClass2repairLocators[self.modelClass].getValue():
            locName = PVPGlobals.RepairSpotLocatorNames[locIndex]
            self._repairSpotWoodPiles[locName] = self.getModelRoot().attachNewNode('repairSpotWoodPile-%s' % locName)
            self._repairSpotWoodPile.instanceTo(self._repairSpotWoodPiles[locName])
            locator = self.getLocator(locName)
            self._repairSpotWoodPiles[locName].setPosHpr(locator.getPos(), locator.getHpr())



    def _removeRepairSpotModels(self):
        for woodPile in self._repairSpotWoodPiles.itervalues():
            woodPile.detachNode()

        self._repairSpotWoodPiles = { }


    def _placeRepairSpotModel(self, locIndex, model):
        locName = PVPGlobals.RepairSpotLocatorNames[locIndex]
        parentNode = self.getModelRoot().attachNewNode('repairSpotHole-%s' % locName)
        parentNode.setTransparency(1, 100)
        model.instanceTo(parentNode)
        locator = self.getLocator(locName)
        parentNode.setPosHpr(locator.getPos(), locator.getHpr())
        self._repairSpotHoles[locIndex] = parentNode


    def _removeRepairSpotModel(self, locIndex):
        if locIndex in self._repairSpotHoles:
            self._repairSpotHoles[locIndex].detachNode()
            del self._repairSpotHoles[locIndex]



    def _fadeOutRepairSpotModel(self, locIndex):
        if locIndex in self._repairSpotIvals:
            self._repairSpotIvals[locIndex].pause()

        self._repairSpotHoles[locIndex].setTransparency(1, 100)
        ival = IG.Sequence(IG.Wait(DistributedPlayerSimpleShip.RepairSpotFadeAfter), IG.LerpColorScaleInterval(self._repairSpotHoles[locIndex], DistributedPlayerSimpleShip.RepairSpotFadeDur, Vec4(1.0, 1.0, 1.0, 0.0), blendType = 'easeInOut'))
        ival.start()
        self._repairSpotIvals[locIndex] = ival


    def _addRepairSpotHoles(self):
        if not self._repairSpotHole:
            repairSpotHoleModels = loader.loadModel('models/props/repair_spot_hole')
            self._repairSpotHole = repairSpotHoleModels.find('**/floor_hole')
            self._repairSpotHoleFixed = repairSpotHoleModels.find('**/floor_hole_fixed')

        for locIndex in PVPGlobals.ShipClass2repairLocators[self.modelClass].getValue():
            self._removeRepairSpotModel(locIndex)
            self._placeRepairSpotModel(locIndex, self._repairSpotHole)



    def _removeRepairSpotHoles(self):
        for locIndex in PVPGlobals.ShipClass2repairLocators[self.modelClass].getValue():
            self._removeRepairSpotModel(locIndex)
            if self._repairSpotHoleFixed:
                self._placeRepairSpotModel(locIndex, self._repairSpotHoleFixed)
                self._fadeOutRepairSpotModel(locIndex)
                self._repairSpotIvals[locIndex] = IG.Sequence(self._repairSpotIvals[locIndex], IG.Func(self._removeRepairSpotModel, locIndex))

    def b_setAllowCrewState(self, state):
        self.d_setAllowCrewState(state)
        self.setAllowCrewState(state)


    def b_setAllowFriendState(self, state):
        self.d_setAllowFriendState(state)
        self.setAllowFriendState(state)


    def b_setAllowGuildState(self, state):
        self.d_setAllowGuildState(state)
        self.setAllowGuildState(state)


    def b_setAllowPublicState(self, state):
        self.d_setAllowPublicState(state)
        self.setAllowPublicState(state)


    def d_setAllowCrewState(self, state):
        self.sendUpdate('setAllowCrewState', [
            state])


    def d_setAllowFriendState(self, state):
        self.sendUpdate('setAllowFriendState', [
            state])


    def d_setAllowGuildState(self, state):
        self.sendUpdate('setAllowGuildState', [
            state])


    def d_setAllowPublicState(self, state):
        self.sendUpdate('setAllowPublicState', [
            state])


    def setAllowCrewState(self, state):
        self.allowCrewState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowCrew(state)



    def setAllowFriendState(self, state):
        self.allowFriendState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowFriends(state)



    def setAllowGuildState(self, state):
        self.allowGuildState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowGuild(state)



    def setAllowPublicState(self, state):
        self.allowPublicState = state
        if self.shipStatusDisplay:
            self.shipStatusDisplay.setAllowPublic(state)



    def getAllowCrewState(self):
        return self.allowCrewState


    def getAllowFriendState(self):
        return self.allowFriendState


    def getAllowGuildState(self):
        return self.allowGuildState


    def getAllowPublicState(self):
        return self.allowPublicState


    def hasSpace(self, avId = 0, bandMgrId = 0, bandId = 0, guildId = 0):
        if avId == self.ownerId:
            return True

        if self.isInCrew(avId):
            return True

        if self.isInCrew(self.ownerId) and len(self.crew) >= self.maxCrew:
            return False

        if len(self.crew) >= self.maxCrew - 1:
            return False

        return True


    def confirmSameCrewTeleport(self, toFrom, incomingAvId = 0, bandMgrId = 0, bandId = 0, guildId = 0):
        if toFrom == 'from':
            return True
        elif not self.isGenerated():
            self.notify.warning('confirmSameCrewTeleport(%s)' % localAvatar.getShipString())
            return False

        if incomingAvId == self.ownerId:
            return True

        if bandMgrId and bandId and self.getAllowCrewState() and (bandMgrId, bandId) == self.getBandId():
            return True

        if localAvatar.doId == self.ownerId and self.getAllowFriendState() and self.cr.identifyFriend(incomingAvId):
            return True

        if guildId and self.getAllowGuildState() and guildId == self.getGuildId():
            return True

        if self.getAllowPublicState():
            return True

        return False

    confirmSameCrewTeleport = report(types = [
        'frameCount',
        'deltaStamp',
        'args'], dConfigParam = 'shipboard')(confirmSameCrewTeleport)

    def getMinimapObject(self):
        if not (self.minimapObj) and not self.isDisabled():
            self.minimapObj = MinimapPlayerShip(self)

        return self.minimapObj


    def setTeam(self, team):
        if not self._verifyTeam(team):
            return None

        DistributedSimpleShip.setTeam(self, team)


    def _verifyTeam(self, team):
        if team == PiratesGlobals.INVALID_TEAM:
            doId = '<no doId>'
            if hasattr(self, 'doId'):
                doId = self.doId
            else:
                self.badInitTeam = team
            base.cr.centralLogger.writeClientEvent('bad ship team: %s' % doId)
            self.notify.warning('bad ship team: %s' % doId)
            return False

        return True


    def d_setLocation(self, parentId, zoneId):
        theStack = StackTrace(start = 1)
        if self.prevLocStack and len(theStack.trace) == len(self.prevLocStack.trace) and map(lambda x: x[1], theStack.trace) == map(lambda x: x[1], self.prevLocStack.trace):
            base.cr.centralLogger.writeClientEvent('bad ship team: %s setLoc' % self.doId)
        else:
            base.cr.centralLogger.writeClientEvent('bad ship team: %s' % self.doId + theStack.compact()[1:len(theStack.compact())])
            self.prevLocStack = theStack
        DistributedSimpleShip.d_setLocation(self, parentId, zoneId)
 def __init__(self, cr, shipId=None):
     ShipRepairSpotMgrBase.__init__(self)
     self.cr = cr
     self._state.add(needModels=StateVar(False), needHoles=StateVar(False))
     self.setShipId(shipId)
Example #34
0
class SCTerminal(SCElement):
    """ SCTerminal is the base class for all 'terminal' speedchat
    entities """
    def __init__(self, linkedEmote=None):
        SCElement.__init__(self)
        self.setLinkedEmote(linkedEmote)

        scGui = loader.loadModel(SCMenu.GuiModelName)
        self.emotionIcon = scGui.find('**/emotionIcon')
        self.setDisabled(False)
        self.__numCharges = -1

        # should we listen for whisper mode changes?
        self._handleWhisperModeSV = StateVar(False)
        # can't set this up until we're ready to have the handler func called
        self._handleWhisperModeFC = None

    def destroy(self):
        self._handleWhisperModeSV.set(False)
        if self._handleWhisperModeFC:
            self._handleWhisperModeFC.destroy()
        self._handleWhisperModeSV.destroy()
        SCElement.destroy(self)

    def privSetSettingsRef(self, settingsRef):
        SCElement.privSetSettingsRef(self, settingsRef)
        if self._handleWhisperModeFC is None:
            self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged,
                                                     self._handleWhisperModeSV)
            self._handleWhisperModeFC.pushCurrentState()
        # if this terminal is not whisperable, we need to listen for whisper mode changes
        self._handleWhisperModeSV.set((self.settingsRef is not None) and
                                      (not self.isWhisperable()))

    def _handleWhisperModeSVChanged(self, handleWhisperMode):
        if handleWhisperMode:
            # this terminal can't be whispered. we need to reconstruct
            # our GUI element when the whisper mode changes
            # listen for that mode change
            # create a DirectObject to avoid conflicts with other parts of this
            # object that are listening for this event
            self._wmcListener = DirectObject()
            self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent),
                                     self._handleWhisperModeChange)
        else:
            if hasattr(self, '_wmcListener'):
                # we no longer need to listen for whisper mode changes
                self._wmcListener.ignoreAll()
                del self._wmcListener
                # make sure our GUI element is appropriate
                self.invalidate()

    def _handleWhisperModeChange(self, whisperMode):
        # whisper mode changed, we need to change our GUI element
        self.invalidate()

    # the meat of SCTerminal; inheritors should override this
    # and perform the appropriate action
    def handleSelect(self):
        """ called when the user selects this node """
        # send the generic 'something was selected' event
        messenger.send(self.getEventName(SCTerminalSelectedEvent))

        # if we have a linked emote, and it isn't disabled, generate a msg
        if self.hasLinkedEmote() and self.linkedEmoteEnabled():
                messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent),
                               [self.linkedEmote])

    def isWhisperable(self):
        # can this terminal be sent as a whisper message?
        return True

    # Some terminal nodes have an emote associated with them, which
    # should be invoked when the node is selected.
    def getLinkedEmote(self):
        return self.linkedEmote
    def setLinkedEmote(self, linkedEmote):
        self.linkedEmote = linkedEmote
        # TODO: we should make sure we're listening for emote
        # enable state changes if this is set while we're visible
        self.invalidate()
    def hasLinkedEmote(self):
        return (self.linkedEmote is not None)
    def linkedEmoteEnabled(self):
        if Emote.globalEmote:
            return Emote.globalEmote.isEnabled(self.linkedEmote)

    def getCharges(self):
        return self.__numCharges
    
    def setCharges(self, nCharges):
        self.__numCharges = nCharges
        if (nCharges is 0):
            self.setDisabled(True)
    
    # support for disabled terminals
    def isDisabled(self):
        return self.__disabled or (self.isWhispering() and not self.isWhisperable())

    def setDisabled(self, bDisabled):
        # make the button 'unclickable'
        self.__disabled = bDisabled

    # from SCElement
    def onMouseClick(self, event):
        if not self.isDisabled():
            SCElement.onMouseClick(self, event)
            self.handleSelect()

    def getMinDimensions(self):
        width, height = SCElement.getMinDimensions(self)
        if self.hasLinkedEmote():
            # add space for the emotion icon
            width += 1.3
        return width, height

    def finalize(self, dbArgs={}):
        """ catch this call and influence the appearance of our button """
        if not self.isDirty():
            return

        args = {}

        if self.hasLinkedEmote():
            self.lastEmoteIconColor = self.getEmoteIconColor()
            self.emotionIcon.setColorScale(*self.lastEmoteIconColor)
            args.update({
                'image':         self.emotionIcon,
                'image_pos':     (self.width-.6,0,-self.height*.5),
                })

        if self.isDisabled():
            args.update({
                'rolloverColor': (0,0,0,0),
                'pressedColor': (0,0,0,0),
                'rolloverSound': None,
                'clickSound': None,
                'text_fg': self.getColorScheme().getTextDisabledColor()+(1,),
                })

        args.update(dbArgs)
        SCElement.finalize(self, dbArgs=args)

    def getEmoteIconColor(self):
        if self.linkedEmoteEnabled() and (not self.isWhispering()):
            r,g,b = self.getColorScheme().getEmoteIconColor()
        else:
            r,g,b = self.getColorScheme().getEmoteIconDisabledColor()
        return (r,g,b,1)

    def updateEmoteIcon(self):
        if hasattr(self, 'button'):
            self.lastEmoteIconColor = self.getEmoteIconColor()
            for i in range(self.button['numStates']):
                self.button['image%s_image' % i].setColorScale(
                    *self.lastEmoteIconColor)
        else:
            self.invalidate()

    # from SCObject
    def enterVisible(self):
        SCElement.enterVisible(self)

        # Check if the emote state has changed since the last time
        # we were finalized, and invalidate if it's different.
        if hasattr(self, 'lastEmoteIconColor'):
            if self.getEmoteIconColor() != self.lastEmoteIconColor:
                self.invalidate()

        # listen for whisper-mode changes
        def handleWhisperModeChange(whisperMode, self=self):
            if self.hasLinkedEmote():
                # we are leaving or entering whisper mode;
                # the appearance of our emote icon needs to change
                # (no linked emotes on whispers)
                if self.isVisible() and not self.isWhispering():
                    self.updateEmoteIcon()
        self.accept(self.getEventName(SCWhisperModeChangeEvent),
                    handleWhisperModeChange)

        # listen for emote-enable state changes
        def handleEmoteEnableStateChange(self=self):
            if self.hasLinkedEmote():
                # emotions have just become enabled/disabled
                # update our emote icon
                # (no emotes when whispering)
                if self.isVisible() and not self.isWhispering():
                    self.updateEmoteIcon()
        if self.hasLinkedEmote():
            if Emote.globalEmote:
                self.accept(Emote.globalEmote.EmoteEnableStateChanged,
                            handleEmoteEnableStateChange)

    def exitVisible(self):
        SCElement.exitVisible(self)
        self.ignore(self.getEventName(SCWhisperModeChangeEvent))
        if Emote.globalEmote:
            self.ignore(Emote.globalEmote.EmoteEnableStateChanged)

    def getDisplayText(self):
        if self.getCharges() is not -1:
            return self.text + " (%s)" % self.getCharges()
        else:
            return self.text
Example #35
0
 def finalInit(self):
     from direct.fsm.StatePush import StateVar
     self._profileTasks = StateVar(False)
     self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
     self._profileFrames = StateVar(False)
     self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
Example #36
0
 def __init__(self, name, value):
     StateVar.__init__(self, value)
     Setting.__init__(self, name, value)
Example #37
0
    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.toons = []
        self.activeIntervals = {}
        self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg')
        self.closeSfx = base.loadSfx(
            'phase_5/audio/sfx/elevator_door_close.ogg')
        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []
        self.distBldgDoId = None
        self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0)
        self.currentFloor = -1
        self.elevatorName = self.__uniqueName('elevator')
        self.floorModel = None
        self.elevatorOutOpen = 0
        self.BottomFloor_SuitPositions = [
            Point3(0, 15, 0),
            Point3(10, 20, 0),
            Point3(-7, 24, 0),
            Point3(-10, 0, 0)
        ]

        self.BottomFloor_SuitHs = [75, 170, -91, -44]

        self.Cubicle_SuitPositions = [
            Point3(0, 18, 0),
            Point3(10, 12, 0),
            Point3(-9, 11, 0),
            Point3(-3, 13, 0)
        ]

        self.Cubicle_SuitHs = [170, 56, -52, 10]

        self.BossOffice_SuitPositions = [
            Point3(0, 15, 0),
            Point3(10, 20, 0),
            Point3(-10, 6, 0),
            Point3(-17, 30, 0)
        ]

        self.BossOffice_SuitHs = [170, 120, 12, 38]

        self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 1)
        self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom()
        self.brResults = [[], []]
        self.barrelRoomIntroTrack = None
        self.penthouseOutroTrack = None
        self.penthouseOutroChatDoneTrack = None
        self.penthouseIntroTrack = None
        self.waitMusic = base.loadMusic(
            'phase_7/audio/bgm/encntr_toon_winning_indoor.ogg')
        self.elevatorMusic = base.loadMusic(
            'phase_7/audio/bgm/tt_elevator.ogg')
        self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInterior', [
            State.State('WaitForAllToonsInside',
                        self.enterWaitForAllToonsInside,
                        self.exitWaitForAllToonsInside, ['Elevator']),
            State.State('Elevator', self.enterElevator, self.exitElevator,
                        ['Game', 'BattleIntro', 'BarrelRoomIntro']),
            State.State('Game', self.enterGame, self.exitGame, [
                'Resting', 'Failed', 'BattleIntro', 'BarrelRoomIntro',
                'Elevator'
            ]),
            State.State('BarrelRoomIntro', self.enterBarrelRoomIntro,
                        self.exitBarrelRoomIntro, ['CollectBarrels', 'Off']),
            State.State('CollectBarrels', self.enterCollectBarrels,
                        self.exitCollectBarrels, ['BarrelRoomReward', 'Off']),
            State.State('BarrelRoomReward', self.enterBarrelRoomReward,
                        self.exitBarrelRoomReward,
                        ['Battle', 'ReservesJoining', 'BattleIntro', 'Off']),
            State.State('BattleIntro', self.enterBattleIntro,
                        self.exitBattleIntro,
                        ['Battle', 'ReservesJoining', 'Off']),
            State.State('Battle', self.enterBattle, self.exitBattle,
                        ['Resting', 'Reward', 'ReservesJoining']),
            State.State('ReservesJoining', self.enterReservesJoining,
                        self.exitReservesJoining, ['Battle']),
            State.State('Resting', self.enterResting, self.exitResting,
                        ['Elevator']),
            State.State('Reward', self.enterReward, self.exitReward, ['Off']),
            State.State('Failed', self.enterFailed, self.exitFailed, ['Off']),
            State.State('Off', self.enterOff, self.exitOff,
                        ['Elevator', 'WaitForAllToonsInside', 'Battle'])
        ], 'Off', 'Off')

        self.fsm.enterInitialState()
        self._haveEntranceElevator = StateVar(False)
        self._stashEntranceElevator = StateVar(False)
        self._stashEntranceElevatorFC = FunctionCall(
            self._doStashEntranceElevator, self._haveEntranceElevator,
            self._stashEntranceElevator)
        self._entranceElevCallbacks = []
        self._doEntranceElevCallbacksFC = FunctionCall(
            self._doEntranceElevCallbacks, self._haveEntranceElevator)
        self.cage = None
        self.shopOwnerNpcId = None
        self.shopOwnerNpc = None
        self._movie = None
        self.SOSToonName = None
        self.FOType = None
Example #38
0
 def setValue(self, value):
     StateVar.set(self, value)
class DistributedPartyTugOfWarActivity(DistributedPartyTeamActivity):
    notify = directNotify.newCategory('DistributedPartyTugOfWarActivity')

    def __init__(self, cr):
        DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTugOfWar, startDelay=PartyGlobals.TugOfWarStartDelay)
        self.buttons = [0, 1]
        self.arrowKeys = None
        self.keyTTL = []
        self.idealRate = 0.0
        self.keyRate = 0
        self.allOutMode = False
        self.rateMatchAward = 0.0
        self.toonIdsToStartPositions = {}
        self.toonIdsToIsPullingFlags = {}
        self.toonIdsToRightHands = {}
        self.fallenToons = []
        self.fallenPositions = []
        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.toonIdsToAnimIntervals = {}
        self.tugRopes = []
        return

    def generate(self):
        DistributedPartyTeamActivity.generate(self)
        self._hopOffFinishedSV = StateVar(True)
        self._rewardFinishedSV = StateVar(True)
        self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady, self._hopOffFinishedSV, self._rewardFinishedSV)

    def delete(self):
        self._isWalkStateReadyFC.destroy()
        self._hopOffFinishedSV.destroy()
        self._rewardFinishedSV.destroy()
        DistributedPartyTeamActivity.delete(self)

    def handleToonJoined(self, toonId):
        DistributedPartyTeamActivity.handleToonJoined(self, toonId)
        self.toonIdsToAnimIntervals[toonId] = None
        if toonId == base.localAvatar.doId:
            base.cr.playGame.getPlace().fsm.request('activity')
            camera.wrtReparentTo(self.root)
            self.cameraMoveIval = LerpPosHprInterval(camera, 1.5, PartyGlobals.TugOfWarCameraPos, PartyGlobals.TugOfWarCameraInitialHpr, other=self.root)
            self.cameraMoveIval.start()
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam)
            self.notify.debug('posIndex: %d' % self.localToonPosIndex)
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex]
            if toon.getZ(self.root) < PartyGlobals.TugOfWarToonPositionZ:
                toon.setZ(self.root, PartyGlobals.TugOfWarToonPositionZ)
            targetH = fitDestAngle2Src(toon.getH(self.root), PartyGlobals.TugOfWarHeadings[self.localToonTeam])
            travelVector = targetPos - toon.getPos(self.root)
            duration = travelVector.length() / 5.0
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            self.toonIdsToAnimIntervals[toonId] = Sequence(Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), LerpPosHprInterval(toon, duration, targetPos, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral'))
            self.toonIdsToAnimIntervals[toonId].start()
        return

    def handleToonExited(self, toonId):
        DistributedPartyTeamActivity.handleToonExited(self, toonId)
        if toonId == base.localAvatar.doId:
            self.cameraMoveIval.pause()
            if toonId not in self.fallenToons:
                if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None:
                    self.toonIdsToAnimIntervals[toonId].finish()
                toon = self.getAvatar(toonId)
                targetH = fitDestAngle2Src(toon.getH(self.root), 180.0)
                targetPos = self.hopOffPositions[self.getTeam(toonId)][self.getIndex(toonId, self.getTeam(toonId))]
                hopOffAnim = Sequence(Func(toon.startPosHprBroadcast, 0.1), toon.hprInterval(0.2, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, toon, targetPos, 5.0, self.root), Func(toon.stopPosHprBroadcast), Func(toon.sendCurrentPosition), Func(self.hopOffFinished, toonId))
                self.toonIdsToAnimIntervals[toonId] = hopOffAnim
                self._hopOffFinishedSV.set(False)
                self.toonIdsToAnimIntervals[toonId].start()
            else:
                self._hopOffFinishedSV.set(True)
                del self.toonIdsToAnimIntervals[toonId]
        return

    def handleRewardDone(self):
        self._rewardFinishedSV.set(True)

    def _testWalkStateReady(self, hoppedOff, rewardFinished):
        if hoppedOff and rewardFinished:
            DistributedPartyTeamActivity.handleRewardDone(self)

    def hopOffFinished(self, toonId):
        if hasattr(self, 'toonIdsToAnimIntervals') and toonId in self.toonIdsToAnimIntervals:
            del self.toonIdsToAnimIntervals[toonId]
        if toonId == base.localAvatar.doId:
            if hasattr(self._hopOffFinishedSV, '_value'):
                self._hopOffFinishedSV.set(True)

    def handleToonShifted(self, toonId):
        if toonId == base.localAvatar.doId:
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam)
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex]
            self.toonIdsToAnimIntervals[toonId] = Sequence(Wait(0.6), Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), toon.posInterval(0.5, targetPos, other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral'))
            self.toonIdsToAnimIntervals[toonId].start()
        return

    def handleToonDisabled(self, toonId):
        if self.toonIdsToAnimIntervals.has_key(toonId):
            if self.toonIdsToAnimIntervals[toonId]:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            else:
                self.notify.debug('self.toonIdsToAnimIntervals[%d] is none' % toonId)

    def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds):
        DistributedPartyTeamActivity.setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds)
        self.toonIdsToRightHands.clear()
        for toonId in self.getToonIdsAsList():
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToRightHands[toonId] = toon.getRightHands()[0]

    def load(self):
        DistributedPartyTeamActivity.load(self)
        self.loadModels()
        self.loadGuiElements()
        self.loadSounds()
        self.loadIntervals()
        self.arrowKeys = ArrowKeys()

    def loadModels(self):
        self.playArea = loader.loadModel('phase_13/models/parties/partyTugOfWar')
        self.playArea.reparentTo(self.root)
        self.sign.reparentTo(self.playArea.find('**/TugOfWar_sign_locator'))
        self.dockPositions = [[], []]
        for i in xrange(4):
            self.dockPositions[0].append(Point3(-PartyGlobals.TugOfWarInitialToonPositionsXOffset - PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ))

        for i in xrange(4):
            self.dockPositions[1].append(Point3(PartyGlobals.TugOfWarInitialToonPositionsXOffset + PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ))

        self.hopOffPositions = [[], []]
        for i in xrange(1, 5):
            self.hopOffPositions[PartyGlobals.TeamActivityTeams.LeftTeam].append(self.playArea.find('**/leftTeamHopOff%d_locator' % i).getPos())
            self.hopOffPositions[PartyGlobals.TeamActivityTeams.RightTeam].append(self.playArea.find('**/rightTeamHopOff%d_locator' % i).getPos())

        for i in xrange(1, 5):
            pos = self.playArea.find('**/fallenToon%d_locator' % i).getPos()
            self.fallenPositions.append(pos)

        self.joinCollision = []
        self.joinCollisionNodePaths = []
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            collShape = CollisionTube(PartyGlobals.TugOfWarJoinCollisionEndPoints[0], PartyGlobals.TugOfWarJoinCollisionEndPoints[1], PartyGlobals.TugOfWarJoinCollisionRadius)
            collShape.setTangible(True)
            self.joinCollision.append(CollisionNode('TugOfWarJoinCollision%d' % i))
            self.joinCollision[i].addSolid(collShape)
            tubeNp = self.playArea.attachNewNode(self.joinCollision[i])
            tubeNp.node().setCollideMask(ToontownGlobals.WallBitmask)
            self.joinCollisionNodePaths.append(tubeNp)
            self.joinCollisionNodePaths[i].setPos(PartyGlobals.TugOfWarJoinCollisionPositions[i])

        self.__enableCollisions()
        ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope')
        self.ropeTexture = ropeModel.findTexture('*')
        ropeModel.removeNode()
        for i in xrange(PartyGlobals.TugOfWarMaximumPlayersPerTeam * 2 - 1):
            rope = Rope(self.uniqueName('TugRope%d' % i))
            if rope.showRope:
                rope.ropeNode.setRenderMode(RopeNode.RMBillboard)
                rope.ropeNode.setThickness(0.2)
                rope.setTexture(self.ropeTexture)
                rope.ropeNode.setUvMode(RopeNode.UVDistance)
                rope.ropeNode.setUvDirection(1)
                rope.setTransparency(1)
                rope.setColor(0.89, 0.89, 0.6, 1.0)
                rope.reparentTo(self.root)
                rope.stash()
            self.tugRopes.append(rope)

        self.splash = Splash.Splash(self.root)
        self.splash.setScale(2.0, 4.0, 1.0)
        pos = self.fallenPositions[0]
        self.splash.setPos(pos[0], pos[1], PartyGlobals.TugOfWarSplashZOffset)
        self.splash.hide()

    def loadGuiElements(self):
        self.powerMeter = MinigamePowerMeter(PartyGlobals.TugOfWarPowerMeterSize)
        self.powerMeter.reparentTo(aspect2d)
        self.powerMeter.setPos(0.0, 0.0, 0.6)
        self.powerMeter.hide()
        self.arrows = [None] * 2
        for x in xrange(len(self.arrows)):
            self.arrows[x] = loader.loadModel('phase_3/models/props/arrow')
            self.arrows[x].reparentTo(self.powerMeter)
            self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2)
            self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26)

        return

    def loadSounds(self):
        self.splashSound = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg')
        self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg')

    def loadIntervals(self):
        self.updateIdealRateInterval = Sequence()
        self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0]))
        for i in xrange(1, len(PartyGlobals.TugOfWarTargetRateList)):
            duration = PartyGlobals.TugOfWarTargetRateList[i][0]
            idealRate = PartyGlobals.TugOfWarTargetRateList[i][1]
            self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate))
            if i == len(PartyGlobals.TugOfWarTargetRateList) - 1:
                self.updateIdealRateInterval.append(Func(setattr, self, 'allOutMode', True))
            else:
                self.updateIdealRateInterval.append(Wait(duration))

        self.updateKeyPressRateInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate))
        self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer))
        self.setupInterval = Parallel()
        self.globalSetupInterval = Sequence(Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes))
        self.localSetupInterval = Sequence(Func(self.setStatus, TTLocalizer.PartyTugOfWarReady), Func(self.showStatus), Wait(PartyGlobals.TugOfWarReadyDuration), Func(base.playSfx, self.whistleSound), Func(self.setStatus, TTLocalizer.PartyTugOfWarGo), Wait(PartyGlobals.TugOfWarGoDuration), Func(self.enableKeys), Func(self.hideStatus), Func(self.updateIdealRateInterval.start), Func(self.updateKeyPressRateInterval.loop), Func(self.reportToServerInterval.loop))
        self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play))

    def unload(self):
        DistributedPartyTeamActivity.unload(self)
        self.arrowKeys.destroy()
        self.unloadIntervals()
        self.unloadModels()
        self.unloadGuiElements()
        self.unloadSounds()
        if hasattr(self, 'toonIds'):
            del self.toonIds
        del self.buttons
        del self.arrowKeys
        del self.keyTTL
        del self.idealRate
        del self.keyRate
        del self.allOutMode
        del self.rateMatchAward
        del self.toonIdsToStartPositions
        del self.toonIdsToIsPullingFlags
        del self.toonIdsToRightHands
        del self.fallenToons
        del self.fallenPositions
        del self.unusedFallenPositionsIndices
        self.toonIdsToAnimIntervals.clear()
        del self.toonIdsToAnimIntervals

    def unloadModels(self):
        self.playArea.removeNode()
        del self.playArea
        del self.dockPositions
        del self.hopOffPositions
        self.__disableCollisions()
        while len(self.joinCollision) > 0:
            collNode = self.joinCollision.pop()
            del collNode

        while len(self.joinCollisionNodePaths) > 0:
            collNodePath = self.joinCollisionNodePaths.pop()
            collNodePath.removeNode()
            del collNodePath

        while len(self.tugRopes) > 0:
            rope = self.tugRopes.pop()
            if rope is not None:
                rope.removeNode()
            del rope

        del self.tugRopes
        self.splash.destroy()
        del self.splash
        return

    def unloadGuiElements(self):
        for arrow in self.arrows:
            if arrow is not None:
                arrow.removeNode()
                del arrow

        del self.arrows
        if self.powerMeter is not None:
            self.powerMeter.cleanup()
            del self.powerMeter
        return

    def unloadSounds(self):
        del self.splashSound
        del self.whistleSound

    def unloadIntervals(self):
        self.updateIdealRateInterval.pause()
        del self.updateIdealRateInterval
        self.updateKeyPressRateInterval.pause()
        del self.updateKeyPressRateInterval
        self.reportToServerInterval.pause()
        del self.reportToServerInterval
        self.setupInterval.pause()
        del self.setupInterval
        self.globalSetupInterval.pause()
        del self.globalSetupInterval
        self.localSetupInterval.pause()
        del self.localSetupInterval
        self.splashInterval.pause()
        del self.splashInterval

    def __enableCollisions(self):
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            self.accept('enterTugOfWarJoinCollision%d' % i, getattr(self, '_join%s' % PartyGlobals.TeamActivityTeams.getString(i)))

    def __disableCollisions(self):
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            self.ignore('enterTugOfWarJoinCollision%d' % i)

    def startWaitForEnough(self):
        DistributedPartyTeamActivity.startWaitForEnough(self)
        self.__enableCollisions()

    def finishWaitForEnough(self):
        DistributedPartyTeamActivity.finishWaitForEnough(self)
        self.__disableCollisions()

    def startWaitToStart(self, waitStartTimestamp):
        DistributedPartyTeamActivity.startWaitToStart(self, waitStartTimestamp)
        self.__enableCollisions()

    def finishWaitToStart(self):
        DistributedPartyTeamActivity.finishWaitToStart(self)
        self.__disableCollisions()

    def startRules(self):
        DistributedPartyTeamActivity.startRules(self)
        self.setUpRopes()
        if self.isLocalToonPlaying:
            self.showControls()

    def finishRules(self):
        DistributedPartyTeamActivity.finishRules(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            self.hideRopes()
            self.hideControls()

    def finishWaitForServer(self):
        DistributedPartyTeamActivity.finishWaitForServer(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            self.hideRopes()
            self.hideControls()

    def startActive(self):
        DistributedPartyTeamActivity.startActive(self)
        self.toonIdsToStartPositions.clear()
        self.toonIdsToIsPullingFlags.clear()
        for toonId in self.getToonIdsAsList():
            self.toonIdsToIsPullingFlags[toonId] = False
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToStartPositions[toonId] = toon.getPos(self.root)
            else:
                self.notify.warning("couldn't find toon %d assigning 0,0,0 to startPos" % toonId)
                self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0)

        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.setupInterval = Parallel(self.globalSetupInterval)
        if self.isLocalToonPlaying:
            self.keyTTL = []
            self.idealForce = 0.0
            self.keyRate = 0
            self.rateMatchAward = 0.0
            self.allOutMode = False
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
            self.setupInterval.append(self.localSetupInterval)
        self.setupInterval.start()

    def finishActive(self):
        DistributedPartyTeamActivity.finishActive(self)
        self.hideControls()
        self.disableKeys()
        self.setupInterval.pause()
        self.reportToServerInterval.pause()
        self.updateKeyPressRateInterval.pause()
        self.updateIdealRateInterval.pause()
        self.hideRopes()

    def startConclusion(self, losingTeam):
        DistributedPartyTeamActivity.startConclusion(self, losingTeam)
        if self.isLocalToonPlaying:
            self._rewardFinishedSV.set(False)
            if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
                self.setStatus(TTLocalizer.PartyTeamActivityGameTie)
            else:
                self.setStatus(TTLocalizer.PartyTugOfWarGameEnd)
            self.showStatus()
        if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
            for toonId in self.getToonIdsAsList():
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('neutral')

        else:
            for toonId in self.toonIds[losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('neutral')

            for toonId in self.toonIds[1 - losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('victory')

        for ival in self.toonIdsToAnimIntervals.values():
            if ival is not None:
                ival.finish()

        return

    def finishConclusion(self):
        DistributedPartyTeamActivity.finishConclusion(self)
        self.fallenToons = []

    def getTitle(self):
        return TTLocalizer.PartyTugOfWarTitle

    def getInstructions(self):
        return TTLocalizer.TugOfWarInstructions

    def showControls(self):
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarDisabledArrowColor)

        self.powerMeter.setTarget(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setPower(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))
        self.powerMeter.clearTooSlowTooFast()
        self.powerMeter.show()

    def hideControls(self):
        self.powerMeter.hide()

    def setUpRopes(self):
        self.notify.debug('setUpRopes')
        ropeIndex = 0
        leftToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]:
            leftToonId = self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]
        rightToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]:
            rightToonId = self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]
        if leftToonId in self.toonIdsToRightHands and rightToonId in self.toonIdsToRightHands:
            self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[leftToonId], (0, 0, 0)), (self.root, (0.0, 0.0, 2.5)), (self.toonIdsToRightHands[rightToonId], (0, 0, 0))), [0,
             0,
             0,
             1,
             1,
             1])
            self.tugRopes[ropeIndex].unstash()
            ropeIndex += 1
        teams = [PartyGlobals.TeamActivityTeams.LeftTeam, PartyGlobals.TeamActivityTeams.RightTeam]
        for currTeam in teams:
            numToons = len(self.toonIds[currTeam])
            if numToons > 1:
                for i in xrange(numToons - 1, 0, -1):
                    toon1 = self.toonIds[currTeam][i]
                    toon2 = self.toonIds[currTeam][i - 1]
                    if not self.toonIdsToRightHands.has_key(toon1):
                        self.notify.warning('Toon in tug of war activity but not properly setup:  %s' % toon1)
                    elif not self.toonIdsToRightHands.has_key(toon2):
                        self.notify.warning('Toon in tug of war activity but not properly setup:  %s' % toon2)
                    else:
                        self.notify.debug('Connecting rope between toon %d and toon %d of team %d.' % (i, i - 1, currTeam))
                        self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon2], (0, 0, 0))), [0,
                         0,
                         0,
                         1,
                         1,
                         1])
                        self.tugRopes[ropeIndex].unstash()
                        ropeIndex += 1

    def tightenRopes(self):
        self.notify.debug('tightenRopes')
        self.tugRopes[0].setup(3, ((self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]], (0, 0, 0))), [0,
         0,
         0,
         1,
         1,
         1])

    def hideRopes(self):
        self.notify.debug('hideRopes')
        for rope in self.tugRopes:
            rope.stash()

    def handleGameTimerExpired(self):
        self.disableKeys()

    def setIdealRate(self, idealRate):
        self.notify.debug('setIdealRate( %d )' % idealRate)
        self.idealRate = idealRate
        self.idealForce = self.advantage * (4 + 0.4 * self.idealRate)

    def updateKeyPressRate(self):
        for i in xrange(len(self.keyTTL)):
            self.keyTTL[i] -= PartyGlobals.TugOfWarKeyPressUpdateRate

        for i in xrange(len(self.keyTTL)):
            if self.keyTTL[i] <= 0.0:
                a = self.keyTTL[0:i]
                del self.keyTTL
                self.keyTTL = a
                break

        self.keyRate = len(self.keyTTL)
        if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1:
            self.rateMatchAward += 0.3
        else:
            self.rateMatchAward = 0.0

    def reportToServer(self):
        self.currentForce = self.computeForce(self.keyRate)
        self.sendUpdate('reportKeyRateForce', [self.keyRate, self.currentForce])
        self.setSpeedGauge()
        self.setAnimState(base.localAvatar.doId, self.keyRate)

    def computeForce(self, keyRate):
        F = 0
        if self.allOutMode:
            F = 0.75 * keyRate
        else:
            stdDev = 0.25 * self.idealRate
            F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2)))
        return F

    def setSpeedGauge(self):
        self.powerMeter.setPower(self.keyRate)
        self.powerMeter.setTarget(self.idealRate)
        if not self.allOutMode:
            self.powerMeter.updateTooSlowTooFast()
            index = float(self.currentForce) / self.idealForce
            bonus = 0.0
            if index > 1.0:
                bonus = max(1.0, index - 1.0)
                index = 1.0
            color = (0,
             0.75 * index + 0.25 * bonus,
             0.75 * (1 - index),
             0.5)
            self.powerMeter.setBarColor(color)
        else:
            self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))

    def updateToonKeyRate(self, toonId, keyRate):
        if toonId != base.localAvatar.doId:
            self.setAnimState(toonId, keyRate)

    def setAnimState(self, toonId, keyRate):
        if self.activityFSM.state != 'Active':
            return
        toon = self.getAvatar(toonId)
        if not self.toonIdsToIsPullingFlags.has_key(toonId):
            if self.getTeam(toonId) == None:
                self.notify.warning("setAnimState called with toonId (%d) that wasn't in self.toonIds" % toonId)
                return
            else:
                self.notify.warning('setAnimState called with toonId (%d) that was in self.toonIds but not in self.toonIdsToIsPullingFlags. Adding it.' % toonId)
                self.toonIdsToIsPullingFlags[toonId] = False
        if keyRate > 0 and not self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.loop('tug-o-war')
            else:
                self.notify.warning('toon %d is None, skipping toon.loop(tugowar)' % toonId)
            self.toonIdsToIsPullingFlags[toonId] = True
        if keyRate <= 0 and self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.pose('tug-o-war', 3)
                toon.startLookAround()
            else:
                self.notify.warning('toon %d is None, skipping toon.startLookAround' % toonId)
            self.toonIdsToIsPullingFlags[toonId] = False
        return

    def enableKeys(self):
        self.notify.debug('enableKeys')
        self.arrowKeys.setPressHandlers([lambda : self.__pressHandler(2),
         lambda : self.__pressHandler(3),
         lambda : self.__pressHandler(1),
         lambda : self.__pressHandler(0)])
        self.arrowKeys.setReleaseHandlers([lambda : self.__releaseHandler(2),
         lambda : self.__releaseHandler(3),
         lambda : self.__releaseHandler(1),
         lambda : self.__releaseHandler(0)])
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def disableKeys(self):
        self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
        self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)

    def __pressHandler(self, index):
        if index == self.buttons[0]:
            self.arrows[index].setColor(PartyGlobals.TugOfWarHilightedArrowColor)
            self.keyTTL.insert(0, PartyGlobals.TugOfWarKeyPressTimeToLive)
            self.buttons.reverse()

    def __releaseHandler(self, index):
        if index in self.buttons:
            self.arrows[index].setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def updateToonPositions(self, offset):
        if self.activityFSM.state != 'Active':
            return
        if self.isLocalToonPlaying:
            camera.lookAt(self.root, offset, 0.0, PartyGlobals.TugOfWarCameraLookAtHeightOffset)
        for toonId in self.getToonIdsAsList():
            if hasattr(self, 'fallenToons') and toonId not in self.fallenToons:
                toon = self.getAvatar(toonId)
                if toon is not None:
                    origPos = self.toonIdsToStartPositions[toonId]
                    curPos = toon.getPos(self.root)
                    newPos = Point3(origPos[0] + offset, curPos[1], curPos[2])
                    if self.toonIdsToAnimIntervals[toonId] != None:
                        if self.toonIdsToAnimIntervals[toonId].isPlaying():
                            self.toonIdsToAnimIntervals[toonId].finish()
                            self.checkIfFallen(toonId)
                    if toonId not in self.fallenToons:
                        self.toonIdsToAnimIntervals[toonId] = Sequence(LerpPosInterval(toon, duration=PartyGlobals.TugOfWarKeyPressReportRate, pos=newPos, other=self.root), Func(self.checkIfFallen, toonId))
                        self.toonIdsToAnimIntervals[toonId].start()

        return

    def checkIfFallen(self, toonId):
        if hasattr(self, 'fallenToons') and toonId not in self.fallenToons:
            toon = self.getAvatar(toonId)
            if toon:
                curPos = toon.getPos(self.root)
                team = self.getTeam(toonId)
                if team == PartyGlobals.TeamActivityTeams.LeftTeam and curPos[0] > -2.0 or team == PartyGlobals.TeamActivityTeams.RightTeam and curPos[0] < 2.0:
                    losingTeam = self.getTeam(toonId)
                    self.throwTeamInWater(losingTeam)
                    self.sendUpdate('reportFallIn', [losingTeam])

    def throwTeamInWater(self, losingTeam):
        self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam))
        splashSet = False
        for toonId in self.toonIds[losingTeam]:
            self.fallenToons.append(toonId)
            toon = self.getAvatar(toonId)
            fallenPosIndex = self.toonIds[losingTeam].index(toonId)
            if fallenPosIndex < 0 or fallenPosIndex >= 4:
                fallenPosIndex = 0
            newPos = self.fallenPositions[fallenPosIndex]
            if self.toonIdsToAnimIntervals.has_key(toonId) and self.toonIdsToAnimIntervals[toonId] is not None:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            if toon:
                parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root))
            else:
                self.notify.warning('toon %d is none, skipping slip-forward' % toonId)
                parallel = Parallel()
            if not splashSet:
                splashSet = True
                parallel.append(self.splashInterval)
            if toon:
                self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral'))
            else:
                self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId)
                self.toonIdsToAnimIntervals[toonId] = parallel
            self.toonIdsToAnimIntervals[toonId].start()

        return

    def setAdvantage(self, advantage):
        DistributedPartyTeamActivity.setAdvantage(self, advantage)
        if self.isLocalToonPlaying:
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
Example #40
0
 def getValue(self):
     return StateVar.get(self)
Example #41
0
class DistributedPartyTugOfWarActivity(DistributedPartyTeamActivity):
    notify = directNotify.newCategory('DistributedPartyTugOfWarActivity')

    def __init__(self, cr):
        DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTugOfWar, startDelay=PartyGlobals.TugOfWarStartDelay)
        self.buttons = [0, 1]
        self.arrowKeys = None
        self.keyTTL = []
        self.idealRate = 0.0
        self.keyRate = 0
        self.allOutMode = False
        self.rateMatchAward = 0.0
        self.toonIdsToStartPositions = {}
        self.toonIdsToIsPullingFlags = {}
        self.toonIdsToRightHands = {}
        self.fallenToons = []
        self.fallenPositions = []
        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.toonIdsToAnimIntervals = {}
        self.tugRopes = []
        return

    def generate(self):
        DistributedPartyTeamActivity.generate(self)
        self._hopOffFinishedSV = StateVar(True)
        self._rewardFinishedSV = StateVar(True)
        self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady, self._hopOffFinishedSV, self._rewardFinishedSV)

    def delete(self):
        self._isWalkStateReadyFC.destroy()
        self._hopOffFinishedSV.destroy()
        self._rewardFinishedSV.destroy()
        DistributedPartyTeamActivity.delete(self)

    def handleToonJoined(self, toonId):
        DistributedPartyTeamActivity.handleToonJoined(self, toonId)
        self.toonIdsToAnimIntervals[toonId] = None
        if toonId == base.localAvatar.doId:
            base.cr.playGame.getPlace().fsm.request('activity')
            camera.wrtReparentTo(self.root)
            self.cameraMoveIval = LerpPosHprInterval(camera, 1.5, PartyGlobals.TugOfWarCameraPos, PartyGlobals.TugOfWarCameraInitialHpr, other=self.root)
            self.cameraMoveIval.start()
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam)
            self.notify.debug('posIndex: %d' % self.localToonPosIndex)
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex]
            if toon.getZ(self.root) < PartyGlobals.TugOfWarToonPositionZ:
                toon.setZ(self.root, PartyGlobals.TugOfWarToonPositionZ)
            targetH = fitDestAngle2Src(toon.getH(self.root), PartyGlobals.TugOfWarHeadings[self.localToonTeam])
            travelVector = targetPos - toon.getPos(self.root)
            duration = travelVector.length() / 5.0
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            self.toonIdsToAnimIntervals[toonId] = Sequence(Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), LerpPosHprInterval(toon, duration, targetPos, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral'))
            self.toonIdsToAnimIntervals[toonId].start()
        return

    def handleToonExited(self, toonId):
        DistributedPartyTeamActivity.handleToonExited(self, toonId)
        if toonId == base.localAvatar.doId:
            self.cameraMoveIval.pause()
            if toonId not in self.fallenToons:
                if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None:
                    self.toonIdsToAnimIntervals[toonId].finish()
                toon = self.getAvatar(toonId)
                targetH = fitDestAngle2Src(toon.getH(self.root), 180.0)
                targetPos = self.hopOffPositions[self.getTeam(toonId)][self.getIndex(toonId, self.getTeam(toonId))]
                hopOffAnim = Sequence(Func(toon.startPosHprBroadcast, 0.1), toon.hprInterval(0.2, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, toon, targetPos, 5.0, self.root), Func(toon.stopPosHprBroadcast), Func(toon.sendCurrentPosition), Func(self.hopOffFinished, toonId))
                self.toonIdsToAnimIntervals[toonId] = hopOffAnim
                self._hopOffFinishedSV.set(False)
                self.toonIdsToAnimIntervals[toonId].start()
            else:
                self._hopOffFinishedSV.set(True)
                del self.toonIdsToAnimIntervals[toonId]
        return

    def handleRewardDone(self):
        self._rewardFinishedSV.set(True)

    def _testWalkStateReady(self, hoppedOff, rewardFinished):
        if hoppedOff and rewardFinished:
            DistributedPartyTeamActivity.handleRewardDone(self)

    def hopOffFinished(self, toonId):
        if hasattr(self, 'toonIdsToAnimIntervals') and toonId in self.toonIdsToAnimIntervals:
            del self.toonIdsToAnimIntervals[toonId]
        if toonId == base.localAvatar.doId:
            if hasattr(self._hopOffFinishedSV, '_value'):
                self._hopOffFinishedSV.set(True)

    def handleToonShifted(self, toonId):
        if toonId == base.localAvatar.doId:
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam)
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex]
            self.toonIdsToAnimIntervals[toonId] = Sequence(Wait(0.6), Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), toon.posInterval(0.5, targetPos, other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral'))
            self.toonIdsToAnimIntervals[toonId].start()
        return

    def handleToonDisabled(self, toonId):
        if toonId in self.toonIdsToAnimIntervals:
            if self.toonIdsToAnimIntervals[toonId]:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            else:
                self.notify.debug('self.toonIdsToAnimIntervals[%d] is none' % toonId)

    def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds):
        DistributedPartyTeamActivity.setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds)
        self.toonIdsToRightHands.clear()
        for toonId in self.getToonIdsAsList():
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToRightHands[toonId] = toon.getRightHands()[0]

    def load(self):
        DistributedPartyTeamActivity.load(self)
        self.loadModels()
        self.loadGuiElements()
        self.loadSounds()
        self.loadIntervals()
        self.arrowKeys = ArrowKeys()

    def loadModels(self):
        self.playArea = loader.loadModel('phase_13/models/parties/partyTugOfWar')
        self.playArea.reparentTo(self.root)
        self.sign.reparentTo(self.playArea.find('**/TugOfWar_sign_locator'))
        self.dockPositions = [[], []]
        for i in range(4):
            self.dockPositions[0].append(Point3(-PartyGlobals.TugOfWarInitialToonPositionsXOffset - PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ))

        for i in range(4):
            self.dockPositions[1].append(Point3(PartyGlobals.TugOfWarInitialToonPositionsXOffset + PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ))

        self.hopOffPositions = [[], []]
        for i in range(1, 5):
            self.hopOffPositions[PartyGlobals.TeamActivityTeams.LeftTeam].append(self.playArea.find('**/leftTeamHopOff%d_locator' % i).getPos())
            self.hopOffPositions[PartyGlobals.TeamActivityTeams.RightTeam].append(self.playArea.find('**/rightTeamHopOff%d_locator' % i).getPos())

        for i in range(1, 5):
            pos = self.playArea.find('**/fallenToon%d_locator' % i).getPos()
            self.fallenPositions.append(pos)

        self.joinCollision = []
        self.joinCollisionNodePaths = []
        for i in range(len(PartyGlobals.TeamActivityTeams)):
            collShape = CollisionTube(PartyGlobals.TugOfWarJoinCollisionEndPoints[0], PartyGlobals.TugOfWarJoinCollisionEndPoints[1], PartyGlobals.TugOfWarJoinCollisionRadius)
            collShape.setTangible(True)
            self.joinCollision.append(CollisionNode('TugOfWarJoinCollision%d' % i))
            self.joinCollision[i].addSolid(collShape)
            tubeNp = self.playArea.attachNewNode(self.joinCollision[i])
            tubeNp.node().setCollideMask(ToontownGlobals.WallBitmask)
            self.joinCollisionNodePaths.append(tubeNp)
            self.joinCollisionNodePaths[i].setPos(PartyGlobals.TugOfWarJoinCollisionPositions[i])

        self.__enableCollisions()
        ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope')
        self.ropeTexture = ropeModel.findTexture('*')
        ropeModel.removeNode()
        for i in range(PartyGlobals.TugOfWarMaximumPlayersPerTeam * 2 - 1):
            rope = Rope(self.uniqueName('TugRope%d' % i))
            if rope.showRope:
                rope.ropeNode.setRenderMode(RopeNode.RMBillboard)
                rope.ropeNode.setThickness(0.2)
                rope.setTexture(self.ropeTexture)
                rope.ropeNode.setUvMode(RopeNode.UVDistance)
                rope.ropeNode.setUvDirection(1)
                rope.setTransparency(1)
                rope.setColor(0.89, 0.89, 0.6, 1.0)
                rope.reparentTo(self.root)
                rope.stash()
            self.tugRopes.append(rope)

        self.splash = Splash.Splash(self.root)
        self.splash.setScale(2.0, 4.0, 1.0)
        pos = self.fallenPositions[0]
        self.splash.setPos(pos[0], pos[1], PartyGlobals.TugOfWarSplashZOffset)
        self.splash.hide()

    def loadGuiElements(self):
        self.powerMeter = MinigamePowerMeter(PartyGlobals.TugOfWarPowerMeterSize)
        self.powerMeter.reparentTo(aspect2d)
        self.powerMeter.setPos(0.0, 0.0, 0.6)
        self.powerMeter.hide()
        self.arrows = [None] * 2
        for x in range(len(self.arrows)):
            self.arrows[x] = loader.loadModel('phase_3/models/props/arrow')
            self.arrows[x].reparentTo(self.powerMeter)
            self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2)
            self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26)

        return

    def loadSounds(self):
        self.splashSound = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg')
        self.whistleSound = base.loader.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg')

    def loadIntervals(self):
        self.updateIdealRateInterval = Sequence()
        self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0]))
        for i in range(1, len(PartyGlobals.TugOfWarTargetRateList)):
            duration = PartyGlobals.TugOfWarTargetRateList[i][0]
            idealRate = PartyGlobals.TugOfWarTargetRateList[i][1]
            self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate))
            if i == len(PartyGlobals.TugOfWarTargetRateList) - 1:
                self.updateIdealRateInterval.append(Func(setattr, self, 'allOutMode', True))
            else:
                self.updateIdealRateInterval.append(Wait(duration))

        self.updateKeyPressRateInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate))
        self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer))
        self.setupInterval = Parallel()
        self.globalSetupInterval = Sequence(Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes))
        self.localSetupInterval = Sequence(Func(self.setStatus, TTLocalizer.PartyTugOfWarReady), Func(self.showStatus), Wait(PartyGlobals.TugOfWarReadyDuration), Func(base.playSfx, self.whistleSound), Func(self.setStatus, TTLocalizer.PartyTugOfWarGo), Wait(PartyGlobals.TugOfWarGoDuration), Func(self.enableKeys), Func(self.hideStatus), Func(self.updateIdealRateInterval.start), Func(self.updateKeyPressRateInterval.loop), Func(self.reportToServerInterval.loop))
        self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play))

    def unload(self):
        DistributedPartyTeamActivity.unload(self)
        self.arrowKeys.destroy()
        self.unloadIntervals()
        self.unloadModels()
        self.unloadGuiElements()
        self.unloadSounds()
        if hasattr(self, 'toonIds'):
            del self.toonIds
        del self.buttons
        del self.arrowKeys
        del self.keyTTL
        del self.idealRate
        del self.keyRate
        del self.allOutMode
        del self.rateMatchAward
        del self.toonIdsToStartPositions
        del self.toonIdsToIsPullingFlags
        del self.toonIdsToRightHands
        del self.fallenToons
        del self.fallenPositions
        del self.unusedFallenPositionsIndices
        self.toonIdsToAnimIntervals.clear()
        del self.toonIdsToAnimIntervals

    def unloadModels(self):
        self.playArea.removeNode()
        del self.playArea
        del self.dockPositions
        del self.hopOffPositions
        self.__disableCollisions()
        while len(self.joinCollision) > 0:
            collNode = self.joinCollision.pop()
            del collNode

        while len(self.joinCollisionNodePaths) > 0:
            collNodePath = self.joinCollisionNodePaths.pop()
            collNodePath.removeNode()
            del collNodePath

        while len(self.tugRopes) > 0:
            rope = self.tugRopes.pop()
            if rope is not None:
                rope.removeNode()
            del rope

        del self.tugRopes
        self.splash.destroy()
        del self.splash
        return

    def unloadGuiElements(self):
        for arrow in self.arrows:
            if arrow is not None:
                arrow.removeNode()
                del arrow

        del self.arrows
        if self.powerMeter is not None:
            self.powerMeter.cleanup()
            del self.powerMeter
        return

    def unloadSounds(self):
        del self.splashSound
        del self.whistleSound

    def unloadIntervals(self):
        self.updateIdealRateInterval.pause()
        del self.updateIdealRateInterval
        self.updateKeyPressRateInterval.pause()
        del self.updateKeyPressRateInterval
        self.reportToServerInterval.pause()
        del self.reportToServerInterval
        self.setupInterval.pause()
        del self.setupInterval
        self.globalSetupInterval.pause()
        del self.globalSetupInterval
        self.localSetupInterval.pause()
        del self.localSetupInterval
        self.splashInterval.pause()
        del self.splashInterval

    def __enableCollisions(self):
        for i in range(len(PartyGlobals.TeamActivityTeams)):
            self.accept('enterTugOfWarJoinCollision%d' % i, getattr(self, '_join%s' % PartyGlobals.TeamActivityTeams.getString(i)))

    def __disableCollisions(self):
        for i in range(len(PartyGlobals.TeamActivityTeams)):
            self.ignore('enterTugOfWarJoinCollision%d' % i)

    def startWaitForEnough(self):
        DistributedPartyTeamActivity.startWaitForEnough(self)
        self.__enableCollisions()

    def finishWaitForEnough(self):
        DistributedPartyTeamActivity.finishWaitForEnough(self)
        self.__disableCollisions()

    def startWaitToStart(self, waitStartTimestamp):
        DistributedPartyTeamActivity.startWaitToStart(self, waitStartTimestamp)
        self.__enableCollisions()

    def finishWaitToStart(self):
        DistributedPartyTeamActivity.finishWaitToStart(self)
        self.__disableCollisions()

    def startRules(self):
        DistributedPartyTeamActivity.startRules(self)
        self.setUpRopes()
        if self.isLocalToonPlaying:
            self.showControls()

    def finishRules(self):
        DistributedPartyTeamActivity.finishRules(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            self.hideRopes()
            self.hideControls()

    def finishWaitForServer(self):
        DistributedPartyTeamActivity.finishWaitForServer(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            self.hideRopes()
            self.hideControls()

    def startActive(self):
        DistributedPartyTeamActivity.startActive(self)
        self.toonIdsToStartPositions.clear()
        self.toonIdsToIsPullingFlags.clear()
        for toonId in self.getToonIdsAsList():
            self.toonIdsToIsPullingFlags[toonId] = False
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToStartPositions[toonId] = toon.getPos(self.root)
            else:
                self.notify.warning("couldn't find toon %d assigning 0,0,0 to startPos" % toonId)
                self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0)

        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.setupInterval = Parallel(self.globalSetupInterval)
        if self.isLocalToonPlaying:
            self.keyTTL = []
            self.idealForce = 0.0
            self.keyRate = 0
            self.rateMatchAward = 0.0
            self.allOutMode = False
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
            self.setupInterval.append(self.localSetupInterval)
        self.setupInterval.start()

    def finishActive(self):
        DistributedPartyTeamActivity.finishActive(self)
        self.hideControls()
        self.disableKeys()
        self.setupInterval.pause()
        self.reportToServerInterval.pause()
        self.updateKeyPressRateInterval.pause()
        self.updateIdealRateInterval.pause()
        self.hideRopes()

    def startConclusion(self, losingTeam):
        DistributedPartyTeamActivity.startConclusion(self, losingTeam)
        if self.isLocalToonPlaying:
            self._rewardFinishedSV.set(False)
            if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
                self.setStatus(TTLocalizer.PartyTeamActivityGameTie)
            else:
                self.setStatus(TTLocalizer.PartyTugOfWarGameEnd)
            self.showStatus()
        if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
            for toonId in self.getToonIdsAsList():
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('neutral')

        else:
            for toonId in self.toonIds[losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('neutral')

            for toonId in self.toonIds[1 - losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('victory')

        for ival in list(self.toonIdsToAnimIntervals.values()):
            if ival is not None:
                ival.finish()

        return

    def finishConclusion(self):
        DistributedPartyTeamActivity.finishConclusion(self)
        self.fallenToons = []

    def getTitle(self):
        return TTLocalizer.PartyTugOfWarTitle

    def getInstructions(self):
        return TTLocalizer.TugOfWarInstructions

    def showControls(self):
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarDisabledArrowColor)

        self.powerMeter.setTarget(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setPower(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))
        self.powerMeter.clearTooSlowTooFast()
        self.powerMeter.show()

    def hideControls(self):
        self.powerMeter.hide()

    def setUpRopes(self):
        self.notify.debug('setUpRopes')
        ropeIndex = 0
        leftToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]:
            leftToonId = self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]
        rightToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]:
            rightToonId = self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]
        if leftToonId in self.toonIdsToRightHands and rightToonId in self.toonIdsToRightHands:
            self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[leftToonId], (0, 0, 0)), (self.root, (0.0, 0.0, 2.5)), (self.toonIdsToRightHands[rightToonId], (0, 0, 0))), [0,
             0,
             0,
             1,
             1,
             1])
            self.tugRopes[ropeIndex].unstash()
            ropeIndex += 1
        teams = [PartyGlobals.TeamActivityTeams.LeftTeam, PartyGlobals.TeamActivityTeams.RightTeam]
        for currTeam in teams:
            numToons = len(self.toonIds[currTeam])
            if numToons > 1:
                for i in range(numToons - 1, 0, -1):
                    toon1 = self.toonIds[currTeam][i]
                    toon2 = self.toonIds[currTeam][i - 1]
                    if toon1 not in self.toonIdsToRightHands:
                        self.notify.warning('Toon in tug of war activity but not properly setup:  %s' % toon1)
                    elif toon2 not in self.toonIdsToRightHands:
                        self.notify.warning('Toon in tug of war activity but not properly setup:  %s' % toon2)
                    else:
                        self.notify.debug('Connecting rope between toon %d and toon %d of team %d.' % (i, i - 1, currTeam))
                        self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon2], (0, 0, 0))), [0,
                         0,
                         0,
                         1,
                         1,
                         1])
                        self.tugRopes[ropeIndex].unstash()
                        ropeIndex += 1

    def tightenRopes(self):
        self.notify.debug('tightenRopes')
        self.tugRopes[0].setup(3, ((self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]], (0, 0, 0))), [0,
         0,
         0,
         1,
         1,
         1])

    def hideRopes(self):
        self.notify.debug('hideRopes')
        for rope in self.tugRopes:
            rope.stash()

    def handleGameTimerExpired(self):
        self.disableKeys()

    def setIdealRate(self, idealRate):
        self.notify.debug('setIdealRate( %d )' % idealRate)
        self.idealRate = idealRate
        self.idealForce = self.advantage * (4 + 0.4 * self.idealRate)

    def updateKeyPressRate(self):
        for i in range(len(self.keyTTL)):
            self.keyTTL[i] -= PartyGlobals.TugOfWarKeyPressUpdateRate

        for i in range(len(self.keyTTL)):
            if self.keyTTL[i] <= 0.0:
                a = self.keyTTL[0:i]
                del self.keyTTL
                self.keyTTL = a
                break

        self.keyRate = len(self.keyTTL)
        if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1:
            self.rateMatchAward += 0.3
        else:
            self.rateMatchAward = 0.0

    def reportToServer(self):
        self.currentForce = self.computeForce(self.keyRate)
        self.sendUpdate('reportKeyRateForce', [self.keyRate, self.currentForce])
        self.setSpeedGauge()
        self.setAnimState(base.localAvatar.doId, self.keyRate)

    def computeForce(self, keyRate):
        F = 0
        if self.allOutMode:
            F = 0.75 * keyRate
        else:
            stdDev = 0.25 * self.idealRate
            F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2)))
        return F

    def setSpeedGauge(self):
        self.powerMeter.setPower(self.keyRate)
        self.powerMeter.setTarget(self.idealRate)
        if not self.allOutMode:
            self.powerMeter.updateTooSlowTooFast()
            index = float(self.currentForce) / self.idealForce
            bonus = 0.0
            if index > 1.0:
                bonus = max(1.0, index - 1.0)
                index = 1.0
            color = (0,
             0.75 * index + 0.25 * bonus,
             0.75 * (1 - index),
             0.5)
            self.powerMeter.setBarColor(color)
        else:
            self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))

    def updateToonKeyRate(self, toonId, keyRate):
        if toonId != base.localAvatar.doId:
            self.setAnimState(toonId, keyRate)

    def setAnimState(self, toonId, keyRate):
        if self.activityFSM.state != 'Active':
            return
        toon = self.getAvatar(toonId)
        if toonId not in self.toonIdsToIsPullingFlags:
            if self.getTeam(toonId) == None:
                self.notify.warning("setAnimState called with toonId (%d) that wasn't in self.toonIds" % toonId)
                return
            else:
                self.notify.warning('setAnimState called with toonId (%d) that was in self.toonIds but not in self.toonIdsToIsPullingFlags. Adding it.' % toonId)
                self.toonIdsToIsPullingFlags[toonId] = False
        if keyRate > 0 and not self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.loop('tug-o-war')
            else:
                self.notify.warning('toon %d is None, skipping toon.loop(tugowar)' % toonId)
            self.toonIdsToIsPullingFlags[toonId] = True
        if keyRate <= 0 and self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.pose('tug-o-war', 3)
                toon.startLookAround()
            else:
                self.notify.warning('toon %d is None, skipping toon.startLookAround' % toonId)
            self.toonIdsToIsPullingFlags[toonId] = False
        return

    def enableKeys(self):
        self.notify.debug('enableKeys')
        self.arrowKeys.setPressHandlers([lambda : self.__pressHandler(2),
         lambda : self.__pressHandler(3),
         lambda : self.__pressHandler(1),
         lambda : self.__pressHandler(0)])
        self.arrowKeys.setReleaseHandlers([lambda : self.__releaseHandler(2),
         lambda : self.__releaseHandler(3),
         lambda : self.__releaseHandler(1),
         lambda : self.__releaseHandler(0)])
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def disableKeys(self):
        self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
        self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)

    def __pressHandler(self, index):
        if index == self.buttons[0]:
            self.arrows[index].setColor(PartyGlobals.TugOfWarHilightedArrowColor)
            self.keyTTL.insert(0, PartyGlobals.TugOfWarKeyPressTimeToLive)
            self.buttons.reverse()

    def __releaseHandler(self, index):
        if index in self.buttons:
            self.arrows[index].setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def updateToonPositions(self, offset):
        if self.activityFSM.state != 'Active':
            return
        if self.isLocalToonPlaying:
            camera.lookAt(self.root, offset, 0.0, PartyGlobals.TugOfWarCameraLookAtHeightOffset)
        for toonId in self.getToonIdsAsList():
            if hasattr(self, 'fallenToons') and toonId not in self.fallenToons:
                toon = self.getAvatar(toonId)
                if toon is not None:
                    origPos = self.toonIdsToStartPositions[toonId]
                    curPos = toon.getPos(self.root)
                    newPos = Point3(origPos[0] + offset, curPos[1], curPos[2])
                    if self.toonIdsToAnimIntervals[toonId] != None:
                        if self.toonIdsToAnimIntervals[toonId].isPlaying():
                            self.toonIdsToAnimIntervals[toonId].finish()
                            self.checkIfFallen(toonId)
                    if toonId not in self.fallenToons:
                        self.toonIdsToAnimIntervals[toonId] = Sequence(LerpPosInterval(toon, duration=PartyGlobals.TugOfWarKeyPressReportRate, pos=newPos, other=self.root), Func(self.checkIfFallen, toonId))
                        self.toonIdsToAnimIntervals[toonId].start()

        return

    def checkIfFallen(self, toonId):
        if hasattr(self, 'fallenToons') and toonId not in self.fallenToons:
            toon = self.getAvatar(toonId)
            if toon:
                curPos = toon.getPos(self.root)
                team = self.getTeam(toonId)
                if team == PartyGlobals.TeamActivityTeams.LeftTeam and curPos[0] > -2.0 or team == PartyGlobals.TeamActivityTeams.RightTeam and curPos[0] < 2.0:
                    losingTeam = self.getTeam(toonId)
                    self.throwTeamInWater(losingTeam)
                    self.sendUpdate('reportFallIn', [losingTeam])

    def throwTeamInWater(self, losingTeam):
        self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam))
        splashSet = False
        for toonId in self.toonIds[losingTeam]:
            self.fallenToons.append(toonId)
            toon = self.getAvatar(toonId)
            fallenPosIndex = self.toonIds[losingTeam].index(toonId)
            if fallenPosIndex < 0 or fallenPosIndex >= 4:
                fallenPosIndex = 0
            newPos = self.fallenPositions[fallenPosIndex]
            if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            if toon:
                parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root))
            else:
                self.notify.warning('toon %d is none, skipping slip-forward' % toonId)
                parallel = Parallel()
            if not splashSet:
                splashSet = True
                parallel.append(self.splashInterval)
            if toon:
                self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral'))
            else:
                self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId)
                self.toonIdsToAnimIntervals[toonId] = parallel
            self.toonIdsToAnimIntervals[toonId].start()

        return

    def setAdvantage(self, advantage):
        DistributedPartyTeamActivity.setAdvantage(self, advantage)
        if self.isLocalToonPlaying:
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
class DistributedCogdoInterior(DistributedObject.DistributedObject):
    id = 0
    cageHeights = [11.36, 0.01]

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.toons = []
        self.activeIntervals = {}
        self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg')
        self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg')
        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []
        self.distBldgDoId = None
        self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0)
        self.currentFloor = -1
        self.elevatorName = self.__uniqueName('elevator')
        self.floorModel = None
        self.elevatorOutOpen = 0
        self.BottomFloor_SuitPositions = [Point3(0, 15, 0),
         Point3(10, 20, 0),
         Point3(-7, 24, 0),
         Point3(-10, 0, 0)]
        self.BottomFloor_SuitHs = [75,
         170,
         -91,
         -44]
        self.Cubicle_SuitPositions = [Point3(0, 18, 0),
         Point3(10, 12, 0),
         Point3(-9, 11, 0),
         Point3(-3, 13, 0)]
        self.Cubicle_SuitHs = [170,
         56,
         -52,
         10]
        self.BossOffice_SuitPositions = [Point3(0, 15, 0),
         Point3(10, 20, 0),
         Point3(-10, 6, 0),
         Point3(-17, 30, 0)]
        self.BossOffice_SuitHs = [170,
         120,
         12,
         38]
        self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 1)
        self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom()
        self.brResults = [[], []]
        self.barrelRoomIntroTrack = None
        self.penthouseOutroTrack = None
        self.penthouseOutroChatDoneTrack = None
        self.penthouseIntroTrack = None
        self.waitMusic = base.loadMusic('phase_7/audio/bgm/encntr_toon_winning_indoor.ogg')
        self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.ogg')
        self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInterior', [State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']),
         State.State('Elevator', self.enterElevator, self.exitElevator, ['Game', 'BattleIntro', 'BarrelRoomIntro']),
         State.State('Game', self.enterGame, self.exitGame, ['Resting', 'Failed', 'BattleIntro', 'BarrelRoomIntro', 'Elevator']),
         State.State('BarrelRoomIntro', self.enterBarrelRoomIntro, self.exitBarrelRoomIntro, ['CollectBarrels', 'Off']),
         State.State('CollectBarrels', self.enterCollectBarrels, self.exitCollectBarrels, ['BarrelRoomReward', 'Off']),
         State.State('BarrelRoomReward', self.enterBarrelRoomReward, self.exitBarrelRoomReward, ['Battle',
          'ReservesJoining',
          'BattleIntro',
          'Off']),
         State.State('BattleIntro', self.enterBattleIntro, self.exitBattleIntro, ['Battle', 'ReservesJoining', 'Off']),
         State.State('Battle', self.enterBattle, self.exitBattle, ['Resting', 'Reward', 'ReservesJoining']),
         State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']),
         State.State('Resting', self.enterResting, self.exitResting, ['Elevator']),
         State.State('Reward', self.enterReward, self.exitReward, ['Off']),
         State.State('Failed', self.enterFailed, self.exitFailed, ['Off']),
         State.State('Off', self.enterOff, self.exitOff, ['Elevator', 'WaitForAllToonsInside', 'Battle'])], 'Off', 'Off')
        self.fsm.enterInitialState()
        self._haveEntranceElevator = StateVar(False)
        self._stashEntranceElevator = StateVar(False)
        self._stashEntranceElevatorFC = FunctionCall(self._doStashEntranceElevator, self._haveEntranceElevator, self._stashEntranceElevator)
        self._entranceElevCallbacks = []
        self._doEntranceElevCallbacksFC = FunctionCall(self._doEntranceElevCallbacks, self._haveEntranceElevator)
        self.cage = None
        self.shopOwnerNpcId = None
        self.shopOwnerNpc = None
        self._movie = None
        self.SOSToonName = None
        self.FOType = None

    def setShopOwnerNpcId(self, npcId):
        self.shopOwnerNpcId = npcId

    def setSOSNpcId(self, npcId):
        self.SOSToonName = NPCToons.getNPCName(npcId)

    def setFOType(self, typeId):
        self.FOType = chr(typeId)

    def getFOType(self):
        return self.FOType

    def __uniqueName(self, name):
        DistributedCogdoInterior.id += 1
        return name + '%d' % DistributedCogdoInterior.id

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        self.announceGenerateName = self.uniqueName('generate')
        self.accept(self.announceGenerateName, self.handleAnnounceGenerate)
        self.elevatorModelIn = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevatorB')
        self.leftDoorIn = self.elevatorModelIn.find('**/left_door')
        self.rightDoorIn = self.elevatorModelIn.find('**/right_door')
        self.elevatorModelOut = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevator')
        self.leftDoorOut = self.elevatorModelOut.find('**/left_door')
        self.rightDoorOut = self.elevatorModelOut.find('**/right_door')

    def __makeShopOwnerNpc(self):
        if self.shopOwnerNpc:
            return
        self.shopOwnerNpc = NPCToons.createLocalNPC(self.shopOwnerNpcId)
        if not self.shopOwnerNpc:
            self.notify.warning('No shopkeeper in this cogdominium, using FunnyFarm Sellbot FO NPCToons')
            random.seed(self.doId)
            shopkeeper = random.randint(7001, 7009)
            self.shopOwnerNpc = NPCToons.createLocalNPC(shopkeeper)
        self.shopOwnerNpc.addActive()
        self.shopOwnerNpc.reparentTo(self.cage)
        self.shopOwnerNpc.setPosHpr(0, -2, 0, 180, 0, 0)
        self.shopOwnerNpc.loop('neutral')

    def setElevatorLights(self, elevatorModel):
        npc = elevatorModel.findAllMatches('**/floor_light_?;+s')
        for i in xrange(npc.getNumPaths()):
            np = npc.getPath(i)
            np.setDepthOffset(120)
            floor = int(np.getName()[-1:]) - 1
            if floor == self.currentFloor:
                np.setColor(LIGHT_ON_COLOR)
            elif floor < self.layout.getNumGameFloors() + (1 if self.FOType != "s" else 0):
                if self.isBossFloor(self.currentFloor):
                    np.setColor(LIGHT_ON_COLOR)
                else:
                    np.setColor(LIGHT_OFF_COLOR)
            else:
                np.hide()

    def startAlertElevatorLightIval(self, elevatorModel):
        light = elevatorModel.find('**/floor_light_%s' % (self.currentFloor + 1))
        track = Sequence(Func(light.setColor, Vec4(1.0, 0.6, 0.6, 1.0)), Wait(0.9), Func(light.setColor, LIGHT_ON_COLOR), Wait(0.9))
        self.activeIntervals['alertElevatorLight'] = track
        track.loop()

    def stopAlertElevatorLightIval(self, elevatorModel):
        self.__finishInterval('alertElevatorLight')
        self.setElevatorLights(elevatorModel)

    def handleAnnounceGenerate(self, obj):
        self.ignore(self.announceGenerateName)
        self.cageDoorSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_door.ogg')
        self.cageLowerSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_lower.ogg')
        self.sendUpdate('setAvatarJoined', [])

    def disable(self):
        self.fsm.requestFinalState()
        self.__cleanupIntervals()
        self.ignoreAll()
        self.__cleanup()
        self.__cleanupShopOwnerNpc()
        self.__cleanupPenthouseIntro()
        DistributedObject.DistributedObject.disable(self)

    def __cleanupShopOwnerNpc(self):
        if self.shopOwnerNpc:
            self.shopOwnerNpc.removeActive()
            self.shopOwnerNpc.delete()
            self.shopOwnerNpc = None

    def __cleanupPenthouseIntro(self):
        if hasattr(self, '_movie') and self._movie:
            self._movie.unload()
            self._movie = None

    def delete(self):
        self._stashEntranceElevatorFC.destroy()
        self._doEntranceElevCallbacksFC.destroy()
        self._haveEntranceElevator.destroy()
        self._stashEntranceElevator.destroy()
        self._entranceElevCallbacks = None
        del self.waitMusic
        del self.elevatorMusic
        del self.openSfx
        del self.closeSfx
        del self.fsm
        base.localAvatar.inventory.setBattleCreditMultiplier(1)
        DistributedObject.DistributedObject.delete(self)

    def isBossFloor(self, floorNum):
        return self.layout.hasBossBattle() and (self.layout.getBossBattleFloor() + 0) == floorNum

    def __cleanup(self):
        self.toons = []
        self.suits = []
        self.reserveSuits = []
        self.joiningReserves = []
        if self.elevatorModelIn != None:
            self.elevatorModelIn.removeNode()
        if self.elevatorModelOut != None:
            self.elevatorModelOut.removeNode()
        if self.floorModel != None:
            self.floorModel.removeNode()
        if self.cage != None:
            self.cage = None
        if self.barrelRoom != None:
            self.barrelRoom.destroy()
            self.barrelRoom = None
        self.leftDoorIn = None
        self.rightDoorIn = None
        self.leftDoorOut = None
        self.rightDoorOut = None

    def __addToon(self, toon):
        self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon])

    def __handleUnexpectedExit(self, toon):
        self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId)
        self.__removeToon(toon, unexpected=1)

    def __removeToon(self, toon, unexpected = 0):
        if self.toons.count(toon) == 1:
            self.toons.remove(toon)
        self.ignore(toon.uniqueName('disable'))

    def __finishInterval(self, name):
        if name in self.activeIntervals:
            interval = self.activeIntervals[name]
            if interval.isPlaying():
                interval.finish()

    def __cleanupIntervals(self):
        for interval in self.activeIntervals.values():
            interval.finish()

        self.activeIntervals = {}

    def __closeInElevator(self):
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)

    def getZoneId(self):
        return self.zoneId

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def getExtZoneId(self):
        return self.extZoneId

    def setExtZoneId(self, extZoneId):
        self.extZoneId = extZoneId

    def getDistBldgDoId(self):
        return self.distBldgDoId

    def setDistBldgDoId(self, distBldgDoId):
        self.distBldgDoId = distBldgDoId

    def setNumFloors(self, numFloors):
        self.layout = CogdoLayout(numFloors)

    def getToonIds(self):
        toonIds = []
        for toon in self.toons:
            toonIds.append(toon.doId)

        return toonIds

    def setToons(self, toonIds, hack):
        self.toonIds = toonIds
        oldtoons = self.toons
        self.toons = []
        for toonId in toonIds:
            if toonId != 0:
                if toonId in self.cr.doId2do:
                    toon = self.cr.doId2do[toonId]
                    toon.stopSmooth()
                    self.toons.append(toon)
                    if oldtoons.count(toon) == 0:
                        self.__addToon(toon)
                else:
                    self.notify.warning('setToons() - no toon: %d' % toonId)

        for toon in oldtoons:
            if self.toons.count(toon) == 0:
                self.__removeToon(toon)

    def setSuits(self, suitIds, reserveIds, values):
        oldsuits = self.suits
        self.suits = []
        self.joiningReserves = []
        for suitId in suitIds:
            if suitId in self.cr.doId2do:
                suit = self.cr.doId2do[suitId]
                self.suits.append(suit)
                suit.fsm.request('Battle')
                suit.buildingSuit = 1
                suit.reparentTo(render)
                if oldsuits.count(suit) == 0:
                    self.joiningReserves.append(suit)

                if 'Elevator' in repr(self.fsm):
                    # Fix the position.
                    pos, h = BattleBase.BattleBase.suitPoints[len(suitIds) - 1][suitIds.index(suitId)]
                    suit.setPos(pos)
                    suit.setH(h)
            else:
                self.notify.warning('setSuits() - no suit: %d' % suitId)

        self.reserveSuits = []
        for index in xrange(len(reserveIds)):
            suitId = reserveIds[index]
            if suitId in self.cr.doId2do:
                suit = self.cr.doId2do[suitId]
                self.reserveSuits.append((suit, values[index]))
            else:
                self.notify.warning('setSuits() - no suit: %d' % suitId)

        if len(self.joiningReserves) > 0:
            self.fsm.request('ReservesJoining')

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

    def stashElevatorIn(self, stash = True):
        self._stashEntranceElevator.set(stash)

    def getEntranceElevator(self, callback):
        if self._haveEntranceElevator.get():
            callback(self.elevIn)
        else:
            self._entranceElevCallbacks.append(callback)

    def _doEntranceElevCallbacks(self, haveElev):
        if haveElev:
            while len(self._entranceElevCallbacks):
                cbs = self._entranceElevCallbacks[:]
                self._entranceElevCallbacks = []
                for callback in cbs:
                    callback(self.elevIn)

    def _doStashEntranceElevator(self, haveElev, doStash):
        if haveElev:
            if doStash:
                self.elevIn.stash()
            else:
                self.elevIn.unstash()

    def d_elevatorDone(self):
        self.sendUpdate('elevatorDone', [])

    def d_reserveJoinDone(self):
        self.sendUpdate('reserveJoinDone', [])

    def enterOff(self, ts = 0):
        messenger.send('sellbotFieldOfficeChanged', [False])
        return None

    def exitOff(self):
        return None

    def enterWaitForAllToonsInside(self, ts = 0):
        base.transitions.fadeOut(0)

    def exitWaitForAllToonsInside(self):
        return None

    def enterGame(self, ts = 0):
        base.cr.forbidCheesyEffects(1)

    def exitGame(self):
        base.cr.forbidCheesyEffects(0)

    def __playElevator(self, ts, name, callback):
        SuitHs = []
        SuitPositions = []

        if self.floorModel:
            self.floorModel.removeNode()
            self.floorModel = None

        if self.cage:
            self.cage = None

        if self.currentFloor == 0:
            SuitHs = self.BottomFloor_SuitHs
            SuitPositions = self.BottomFloor_SuitPositions

        if self.isBossFloor(self.currentFloor):
            self.notify.info('__playElevator: currentFloor %s is boss' % self.currentFloor)
            self.barrelRoom.unload()
            if self.FOType:
                penthouseName = SUITE_DICT.get(self.FOType)
                for i in xrange(4):
                    self.floorModel = loader.loadModel('phase_5/models/cogdominium/%s' % penthouseName)

            self.cage = self.floorModel.find('**/cage')
            pos = self.cage.getPos()
            self.cagePos = []
            for height in self.cageHeights:
                self.cagePos.append(Point3(pos[0], pos[1], height))

            self.cageDoor = self.floorModel.find('**/cage_door')
            self.cageDoor.wrtReparentTo(self.cage)
            if self.FOType:
                paintingModelName = PAINTING_DICT.get(self.FOType)
                for i in xrange(4):
                    paintingModel = loader.loadModel('phase_5/models/cogdominium/%s' % paintingModelName)
                    loc = self.floorModel.find('**/loc_painting%d' % (i + 1))
                    paintingModel.reparentTo(loc)

            if not self.floorModel.find('**/trophyCase').isEmpty():
                for i in xrange(4):
                    goldEmblem = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy.bam')
                    loc = self.floorModel.find('**/gold_0%d' % (i + 1))
                    goldEmblem.reparentTo(loc)
                for i in xrange(20):
                    silverEmblem = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam')
                    loc = self.floorModel.find('**/silver_0%d' % (i + 1))
                    silverEmblem.reparentTo(loc)

            SuitHs = self.BossOffice_SuitHs
            SuitPositions = self.BossOffice_SuitPositions
            self.__makeShopOwnerNpc()
        else:
            if self._wantBarrelRoom:
                self.barrelRoom.load()
                self.barrelRoom.hide()
            SuitHs = self.Cubicle_SuitHs
            SuitPositions = self.Cubicle_SuitPositions

        if self.floorModel:
            self.floorModel.reparentTo(render)
            if self.isBossFloor(self.currentFloor):
                self.notify.info('Load boss_suit_office')
                elevIn = self.floorModel.find(CogdoGameConsts.PenthouseElevatorInPath).copyTo(render)
                elevOut = self.floorModel.find(CogdoGameConsts.PenthouseElevatorOutPath)
                frame = self.elevatorModelOut.find('**/frame')

                if not frame.isEmpty():
                    frame.hide()

                frame = self.elevatorModelIn.find('**/frame')

                if not frame.isEmpty():
                    frame.hide()

                self.elevatorModelOut.reparentTo(elevOut)
                self.elevatorModelOut.setY(0)
            else:
                elevIn = self.floorModel.find('**/elevator-in')
                elevOut = self.floorModel.find('**/elevator-out')
        elif self._wantBarrelRoom and self.barrelRoom.isLoaded() and self.currentFloor == 2 and self.FOType == 'l': #i know this is really ugly
            elevIn = self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorInPath)
            elevOut = self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorOutPath)
            y = elevOut.getY(render)
            elevOut = elevOut.copyTo(render)
            elevOut.setY(render, y - 0.75)
        else:
            floorModel = loader.loadModel('phase_7/models/modules/boss_suit_office')
            elevIn = floorModel.find('**/elevator-in').copyTo(render)
            elevOut = floorModel.find('**/elevator-out').copyTo(render)
            floorModel.removeNode()

        self.elevIn = elevIn
        self.elevOut = elevOut
        self._haveEntranceElevator.set(True)
        for index in xrange(len(self.suits)):
            if not self.suits[index].isEmpty():
                self.suits[index].setPos(SuitPositions[index])
                if len(self.suits) > 2:
                    self.suits[index].setH(SuitHs[index])
                else:
                    self.suits[index].setH(170)
                self.suits[index].loop('neutral')

        for toon in self.toons:
            toon.reparentTo(self.elevatorModelIn)
            index = self.toonIds.index(toon.doId)
            toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2])
            toon.setHpr(180, 0, 0)
            toon.loop('neutral')

        self.elevatorModelIn.reparentTo(elevIn)
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)
        camera.reparentTo(self.elevatorModelIn)
        camera.setH(180)
        camera.setP(0)
        camera.setPos(0, 14, 4)
        base.playMusic(self.elevatorMusic, looping=1, volume=0.8)
        track = Sequence(Func(base.transitions.noTransitions), ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL), ElevatorUtils.getOpenInterval(self, self.leftDoorIn, self.rightDoorIn, self.openSfx, None, type=ELEVATOR_NORMAL), Func(camera.wrtReparentTo, render))
        for toon in self.toons:
            track.append(Func(toon.wrtReparentTo, render))

        track.append(Func(callback))
        track.start(ts)
        self.activeIntervals[name] = track

    def enterElevator(self, ts = 0):
        if not self._CogdoGameRepeat:
            self.currentFloor += 1
        self.cr.playGame.getPlace().currentFloor = self.currentFloor
        self.setElevatorLights(self.elevatorModelIn)
        self.setElevatorLights(self.elevatorModelOut)
        if not self.isBossFloor(self.currentFloor):
            self.elevatorModelOut.detachNode()
            messenger.send('sellbotFieldOfficeChanged', [True])
        else:
            if self.FOType == 's':
                self._movie = CogdoElevatorMovie()
                self._movie.load()
                self._movie.play()
        self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone)
        mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor)
        base.localAvatar.inventory.setBattleCreditMultiplier(mult)

    def __handleElevatorDone(self):
        self.d_elevatorDone()

    def exitElevator(self):
        self.elevatorMusic.stop()
        if self._movie:
            self._movie.end()
            self.__cleanupPenthouseIntro()
        self.__finishInterval(self.elevatorName)

    def __setupBarrelRoom(self):
        self.currentFloor += 1
        base.transitions.irisOut(0.0)
        self.elevatorModelOut.setY(-12)
        self.elevatorModelIn.reparentTo(self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorInPath))
        self.leftDoorIn.setPos(3.5, 0, 0)
        self.rightDoorIn.setPos(-3.5, 0, 0)
        self._showExitElevator()
        self.barrelRoom.show()
        self.barrelRoom.placeToonsAtEntrance(self.toons)
        self.setElevatorLights(self.elevatorModelOut)

    def barrelRoomIntroDone(self):
        self.sendUpdate('toonBarrelRoomIntroDone', [])

    def enterBarrelRoomIntro(self, ts = 0):
        if not self.isBossFloor(self.currentFloor):
            if self._wantBarrelRoom:
                self.__setupBarrelRoom()
                self.barrelRoomIntroTrack, trackName = self.barrelRoom.getIntroInterval()
                self.barrelRoomIntroDoneEvent = trackName
                self.accept(self.barrelRoomIntroDoneEvent, self.barrelRoomIntroDone)
                self.activeIntervals[trackName] = self.barrelRoomIntroTrack
                self.barrelRoomIntroTrack.start(ts)
                self._movie = CogdoBarrelRoomIntro()
                self._movie.load()
                self._movie.play()
            else:
                self._showExitElevator()

    def exitBarrelRoomIntro(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            self.ignore(self.barrelRoomIntroDoneEvent)
            if self.barrelRoomIntroTrack:
                self.barrelRoomIntroTrack.finish()
                DelayDelete.cleanupDelayDeletes(self.barrelRoomIntroTrack)
                self.barrelRoomIntroTrack = None

    def __handleLocalToonLeftBarrelRoom(self):
        self.notify.info('Local toon teleported out of barrel room.')
        self.sendUpdate('toonLeftBarrelRoom', [])
        self.barrelRoom.deactivate()

    def enterCollectBarrels(self, ts = 0):
        if not self.isBossFloor(self.currentFloor):
            if self._wantBarrelRoom:
                self.acceptOnce('localToonLeft', self.__handleLocalToonLeftBarrelRoom)
                self.barrelRoom.activate()
                base.playMusic(self.waitMusic, looping=1, volume=0.7)
                base.localAvatar.questMap.stop()

    def exitCollectBarrels(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            self.ignore('localToonLeft')
            self.barrelRoom.deactivate()
            self.waitMusic.stop()

    def __brRewardDone(self, task = None):
        self.notify.info('Toon finished watching the barrel room reward.')
        self.sendUpdate('toonBarrelRoomRewardDone', [])
        self.fsm.request('Battle')

    def enterBarrelRoomReward(self, ts = 0):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            base.cr.playGame.getPlace().fsm.request('stopped')
            self.startAlertElevatorLightIval(self.elevatorModelOut)
            track, trackName = self.barrelRoom.showRewardUi(callback=self.__brRewardDone)
            self.activeIntervals[trackName] = track
            track.start()
            self.barrelRoom.placeToonsNearBattle(self.toons)

    def exitBarrelRoomReward(self):
        if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
            base.cr.playGame.getPlace().fsm.request('walk')
            self.stopAlertElevatorLightIval(self.elevatorModelOut)
            self.barrelRoom.hideRewardUi()

    def enterBattleIntro(self, ts = 0):
        self._movie = CogdoExecutiveSuiteIntro(self.shopOwnerNpc)
        self._movie.load()
        self._movie.play()

    def exitBattleIntro(self):
        self._movie.end()
        self.__cleanupPenthouseIntro()

    def __playCloseElevatorOut(self, name, delay = 0):
        track = Sequence(Wait(delay + SUIT_LEAVE_ELEVATOR_TIME), Parallel(SoundInterval(self.closeSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut')))
        track.start()
        self.activeIntervals[name] = track

    def enterBattle(self, ts = 0):
        if self._wantBarrelRoom and self.elevatorOutOpen == 1:
            self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'), delay=2)
            camera.setPos(0, -15, 6)
            camera.headsUp(self.elevatorModelOut)

    def _showExitElevator(self):
        self.elevatorModelOut.reparentTo(self.elevOut)
        self.leftDoorOut.setPos(3.5, 0, 0)
        self.rightDoorOut.setPos(-3.5, 0, 0)
        if not self._wantBarrelRoom and self.elevatorOutOpen == 1:
            self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'))
            camera.setPos(0, -15, 6)
            camera.headsUp(self.elevatorModelOut)

    def exitBattle(self):
        if self.elevatorOutOpen == 1:
            self.__finishInterval(self.uniqueName('close-out-elevator'))
            self.elevatorOutOpen = 0

    def __playReservesJoining(self, ts, name, callback):
        index = 0
        for suit in self.joiningReserves:
            suit.reparentTo(render)
            suit.setPos(self.elevatorModelOut, Point3(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2]))
            index += 1
            suit.setH(180)
            suit.loop('neutral')

        track = Sequence(Func(camera.wrtReparentTo, self.elevatorModelOut), Func(camera.setPos, Point3(0, -8, 2)), Func(camera.setHpr, Vec3(0, 10, 0)), Parallel(SoundInterval(self.openSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), blendType='easeOut')), Wait(SUIT_HOLD_ELEVATOR_TIME), Func(camera.wrtReparentTo, render), Func(callback))
        track.start(ts)
        self.activeIntervals[name] = track

    def enterReservesJoining(self, ts = 0):
        self.__playReservesJoining(ts, self.uniqueName('reserves-joining'), self.__handleReserveJoinDone)

    def __handleReserveJoinDone(self):
        self.joiningReserves = []
        self.elevatorOutOpen = 1
        self.d_reserveJoinDone()

    def exitReservesJoining(self):
        self.__finishInterval(self.uniqueName('reserves-joining'))

    def enterResting(self, ts = 0):
        self._showExitElevator()
        self._setAvPosFDC = FrameDelayedCall('setAvPos', self._setAvPosToExit)
        if self._wantBarrelRoom:
            self.barrelRoom.showBattleAreaLight(True)
        base.playMusic(self.waitMusic, looping=1, volume=0.7)
        self.__closeInElevator()
        self._haveEntranceElevator.set(False)
        self._stashEntranceElevator.set(False)

    def _setAvPosToExit(self):
        base.localAvatar.setPos(self.elevOut, 0, -22, 0)
        base.localAvatar.setHpr(self.elevOut, 0, 0, 0)
        base.cr.playGame.getPlace().fsm.request('walk')

    def exitResting(self):
        self._setAvPosFDC.destroy()
        self.waitMusic.stop()

    def enterReward(self, ts = 0):
        if self.isBossFloor(self.currentFloor):
            self.penthouseOutroTrack = self.__outroPenthouse()
            self.penthouseOutroTrack.start(ts)
        else:
            self.exitCogdoBuilding()

    def exitReward(self):
        self.notify.debug('exitReward')
        if self.penthouseOutroTrack:
            self.penthouseOutroTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.penthouseOutroTrack)
            self.penthouseOutroTrack = None
            if not self.penthouseOutroChatDoneTrack:
                self.notify.debug('exitReward: instanting outroPenthouseChatDone track')
                self.__outroPenthouseChatDone()
            self.penthouseOutroChatDoneTrack.finish()
            self.penthouseOutroChatDoneTrack = None

    def enterFailed(self, ts = 0):
        self.exitCogdoBuilding()

    def exitFailed(self):
        self.notify.debug('exitFailed()')
        self.exitCogdoBuilding()

    def exitCogdoBuilding(self):
        if base.localAvatar.hp < 0:
            return
        base.localAvatar.b_setParent(ToontownGlobals.SPHidden)
        request = {'loader': ZoneUtil.getBranchLoaderName(self.extZoneId),
         'where': ZoneUtil.getToonWhereName(self.extZoneId),
         'how': 'elevatorIn',
         'hoodId': ZoneUtil.getHoodId(self.extZoneId),
         'zoneId': self.extZoneId,
         'shardId': None,
         'avId': -1,
         'bldgDoId': self.distBldgDoId}
        messenger.send('DSIDoneEvent', [request])

    def displayBadges(self):
        numFloors = self.layout.getNumGameFloors()
        if numFloors > 5 or numFloors < 3:
            pass
        else:
            self.notify.warning('Invalid floor number for display badges.')
        for player in xrange(len(self.toons)):
            goldBadge = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy')
            goldBadge.setScale(1.2)
            goldNode = render.find('**/gold_0' + str(player + 1))
            goldBadge.reparentTo(goldNode)
            for floor in xrange(numFloors):
                silverBadge = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam')
                silverBadge.setScale(1.2)
                silverNode = render.find('**/silver_0' + str(floor * 4 + (player + 1)))
                silverBadge.reparentTo(silverNode)

    def __outroPenthouse(self):
        avatar = base.localAvatar
        trackName = '__outroPenthouse-%d' % avatar.doId
        track = Parallel(name=trackName)
        base.cr.playGame.getPlace().fsm.request('stopped')

        if self.FOType == 'l':
            speech = TTLocalizer.CogdoExecutiveSuiteToonThankYouLawbot
        else:
            speech = TTLocalizer.CogdoExecutiveSuiteToonThankYou % self.SOSToonName

        track.append(Sequence(Func(camera.wrtReparentTo, localAvatar),
                              Func(camera.setPos, 0, -9, 9),
                              Func(camera.lookAt, Point3(5, 15, 0)),
                              Parallel(self.cage.posInterval(0.75, self.cagePos[1], blendType='easeOut'),
                                       SoundInterval(self.cageLowerSfx, duration=0.5)),
                              Parallel(self.cageDoor.hprInterval(0.5, VBase3(0, 90, 0), blendType='easeOut'),
                                       Sequence(SoundInterval(self.cageDoorSfx), duration=0)),
                              Wait(0.25),
                              Func(self.shopOwnerNpc.wrtReparentTo, render),
                              Func(self.shopOwnerNpc.setScale, 1),
                              Func(self.shopOwnerNpc.loop, 'walk'),
                              Func(self.shopOwnerNpc.headsUp, Point3(0, 10, 0)),
                              ParallelEndTogether(self.shopOwnerNpc.posInterval(1.5, Point3(0, 10, 0)), self.shopOwnerNpc.hprInterval(0.5, VBase3(180, 0, 0), blendType='easeInOut')),
                              Func(self.shopOwnerNpc.setChatAbsolute, TTLocalizer.CagedToonYippee, CFSpeech), ActorInterval(self.shopOwnerNpc, 'jump'),
                              Func(self.shopOwnerNpc.loop, 'neutral'), Func(self.shopOwnerNpc.headsUp, localAvatar),
                              Func(self.shopOwnerNpc.setLocalPageChat, speech, 0),
                              Func(camera.lookAt, self.shopOwnerNpc, Point3(0, 0, 2))))
        self.activeIntervals[trackName] = track
        self.accept('doneChatPage', self.__outroPenthouseChatDone)
        return track

    def __outroPenthouseChatDone(self, elapsed = None):
        self.shopOwnerNpc.setChatAbsolute(TTLocalizer.CogdoExecutiveSuiteToonBye, CFSpeech)
        self.ignore('doneChatPage')
        track = Parallel(Sequence(ActorInterval(self.shopOwnerNpc, 'wave'), Func(self.shopOwnerNpc.loop, 'neutral')), Sequence(Wait(2.0), Func(self.exitCogdoBuilding), Func(base.camLens.setFov, settings['fov'])))
        track.start()
        self.penthouseOutroChatDoneTrack = track
class SCTerminal(SCElement):

    def __init__(self, linkedEmote = None):
        SCElement.__init__(self)
        self.setLinkedEmote(linkedEmote)
        scGui = loader.loadModel(SCMenu.GuiModelName)
        self.emotionIcon = scGui.find('**/emotionIcon')
        self.setDisabled(False)
        self.__numCharges = -1
        self._handleWhisperModeSV = StateVar(False)
        self._handleWhisperModeFC = None
        return

    def destroy(self):
        self._handleWhisperModeSV.set(False)
        if self._handleWhisperModeFC:
            self._handleWhisperModeFC.destroy()
        self._handleWhisperModeSV.destroy()
        SCElement.destroy(self)

    def privSetSettingsRef(self, settingsRef):
        SCElement.privSetSettingsRef(self, settingsRef)
        if self._handleWhisperModeFC is None:
            self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged, self._handleWhisperModeSV)
            self._handleWhisperModeFC.pushCurrentState()
        self._handleWhisperModeSV.set(self.settingsRef is not None and not self.isWhisperable())
        return

    def _handleWhisperModeSVChanged(self, handleWhisperMode):
        if handleWhisperMode:
            self._wmcListener = DirectObject()
            self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange)
        elif hasattr(self, '_wmcListener'):
            self._wmcListener.ignoreAll()
            del self._wmcListener
            self.invalidate()

    def _handleWhisperModeChange(self, whisperMode):
        self.invalidate()

    def handleSelect(self):
        messenger.send(self.getEventName(SCTerminalSelectedEvent))
        if self.hasLinkedEmote() and self.linkedEmoteEnabled():
            messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote])

    def isWhisperable(self):
        return True

    def getLinkedEmote(self):
        return self.linkedEmote

    def setLinkedEmote(self, linkedEmote):
        self.linkedEmote = linkedEmote
        self.invalidate()

    def hasLinkedEmote(self):
        return self.linkedEmote is not None

    def linkedEmoteEnabled(self):
        if Emote.globalEmote:
            return Emote.globalEmote.isEnabled(self.linkedEmote)

    def getCharges(self):
        return self.__numCharges

    def setCharges(self, nCharges):
        self.__numCharges = nCharges
        if nCharges is 0:
            self.setDisabled(True)

    def isDisabled(self):
        return self.__disabled or self.isWhispering() and not self.isWhisperable()

    def setDisabled(self, bDisabled):
        self.__disabled = bDisabled

    def onMouseClick(self, event):
        if not self.isDisabled():
            SCElement.onMouseClick(self, event)
            self.handleSelect()

    def getMinDimensions(self):
        width, height = SCElement.getMinDimensions(self)
        if self.hasLinkedEmote():
            width += 1.3
        return (width, height)

    def finalize(self, dbArgs = {}):
        if not self.isDirty():
            return
        args = {}
        if self.hasLinkedEmote():
            self.lastEmoteIconColor = self.getEmoteIconColor()
            self.emotionIcon.setColorScale(*self.lastEmoteIconColor)
            args.update({'image': self.emotionIcon,
             'image_pos': (self.width - 0.6, 0, -self.height * 0.5)})
        if self.isDisabled():
            args.update({'rolloverColor': (0, 0, 0, 0),
             'pressedColor': (0, 0, 0, 0),
             'rolloverSound': None,
             'clickSound': None,
             'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)})
        args.update(dbArgs)
        SCElement.finalize(self, dbArgs=args)
        return

    def getEmoteIconColor(self):
        if self.linkedEmoteEnabled() and not self.isWhispering():
            r, g, b = self.getColorScheme().getEmoteIconColor()
        else:
            r, g, b = self.getColorScheme().getEmoteIconDisabledColor()
        return (r,
         g,
         b,
         1)

    def updateEmoteIcon(self):
        if hasattr(self, 'button'):
            self.lastEmoteIconColor = self.getEmoteIconColor()
            for i in range(self.button['numStates']):
                self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor)

        else:
            self.invalidate()

    def enterVisible(self):
        SCElement.enterVisible(self)
        if hasattr(self, 'lastEmoteIconColor'):
            if self.getEmoteIconColor() != self.lastEmoteIconColor:
                self.invalidate()

        def handleWhisperModeChange(whisperMode, self = self):
            if self.hasLinkedEmote():
                if self.isVisible() and not self.isWhispering():
                    self.updateEmoteIcon()

        self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange)

        def handleEmoteEnableStateChange(self = self):
            if self.hasLinkedEmote():
                if self.isVisible() and not self.isWhispering():
                    self.updateEmoteIcon()

        if self.hasLinkedEmote():
            if Emote.globalEmote:
                self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange)

    def exitVisible(self):
        SCElement.exitVisible(self)
        self.ignore(self.getEventName(SCWhisperModeChangeEvent))
        if Emote.globalEmote:
            self.ignore(Emote.globalEmote.EmoteEnableStateChanged)

    def getDisplayText(self):
        if self.getCharges() is not -1:
            return self.text + ' (%s)' % self.getCharges()
        else:
            return self.text
 def __init__(self, name, value):
     StateVar.__init__(self, value)
     Setting.__init__(self, name, value)