text:
from pandac.PandaModules import *
from toontown.toonbase.ToonBaseGlobal import *
from direct.interval.IntervalGlobal import *
from BattleBase import *
from direct.distributed.ClockDelta import *
from toontown.toonbase import ToontownBattleGlobals
from direct.distributed import DistributedNode
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from direct.task.Task import Task
from direct.directnotify import DirectNotifyGlobal
import Movie
import MovieUtil
from toontown.suit import Suit
from direct.actor import Actor
import BattleProps
from direct.particles import ParticleEffect
import BattleParticles
from toontown.hood import ZoneUtil
from toontown.distributed import DelayDelete
from toontown.toon import TTEmote
from otp.avatar import Emote

class DistributedBattleBase(DistributedNode.DistributedNode, BattleBase):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBase')
    id = 0
    camPos = ToontownBattleGlobals.BattleCamDefaultPos
    camHpr = ToontownBattleGlobals.BattleCamDefaultHpr
    camFov = ToontownBattleGlobals.BattleCamDefaultFov
    camMenuFov = ToontownBattleGlobals.BattleCamMenuFov
    camJoinPos = ToontownBattleGlobals.BattleCamJoinPos
    camJoinHpr = ToontownBattleGlobals.BattleCamJoinHpr

    def __init__(self, cr, townBattle):
        DistributedNode.DistributedNode.__init__(self, cr)
        NodePath.__init__(self)
        self.assign(render.attachNewNode(self.uniqueBattleName('distributed-battle')))
        BattleBase.__init__(self)
        self.bossBattle = 0
        self.townBattle = townBattle
        self.__battleCleanedUp = 0
        self.activeIntervals = {}
        self.localToonJustJoined = 0
        self.choseAttackAlready = 0
        self.toons = []
        self.exitedToons = []
        self.suitTraps = ''
        self.membersKeep = None
        self.faceOffName = self.uniqueBattleName('faceoff')
        self.localToonBattleEvent = self.uniqueBattleName('localtoon-battle-event')
        self.adjustName = self.uniqueBattleName('adjust')
        self.timerCountdownTaskName = self.uniqueBattleName('timer-countdown')
        self.movie = Movie.Movie(self)
        self.timer = Timer()
        self.needAdjustTownBattle = 0
        self.streetBattle = 1
        self.levelBattle = 0
        self.localToonFsm = ClassicFSM.ClassicFSM('LocalToon', [State.State('HasLocalToon', self.enterHasLocalToon, self.exitHasLocalToon, ['NoLocalToon', 'WaitForServer']), State.State('NoLocalToon', self.enterNoLocalToon, self.exitNoLocalToon, ['HasLocalToon', 'WaitForServer']), State.State('WaitForServer', self.enterWaitForServer, self.exitWaitForServer, ['HasLocalToon', 'NoLocalToon'])], 'WaitForServer', 'WaitForServer')
        self.localToonFsm.enterInitialState()
        self.fsm = ClassicFSM.ClassicFSM('DistributedBattle', [State.State('Off', self.enterOff, self.exitOff, ['FaceOff',
          'WaitForInput',
          'WaitForJoin',
          'MakeMovie',
          'PlayMovie',
          'Reward',
          'Resume']),
         State.State('FaceOff', self.enterFaceOff, self.exitFaceOff, ['WaitForInput']),
         State.State('WaitForJoin', self.enterWaitForJoin, self.exitWaitForJoin, ['WaitForInput', 'Resume']),
         State.State('WaitForInput', self.enterWaitForInput, self.exitWaitForInput, ['WaitForInput', 'PlayMovie', 'Resume']),
         State.State('MakeMovie', self.enterMakeMovie, self.exitMakeMovie, ['PlayMovie', 'Resume']),
         State.State('PlayMovie', self.enterPlayMovie, self.exitPlayMovie, ['WaitForInput',
          'WaitForJoin',
          'Reward',
          'Resume']),
         State.State('Reward', self.enterReward, self.exitReward, ['Resume']),
         State.State('Resume', self.enterResume, self.exitResume, [])], 'Off', 'Off')
        self.fsm.enterInitialState()
        self.adjustFsm = ClassicFSM.ClassicFSM('Adjust', [State.State('Adjusting', self.enterAdjusting, self.exitAdjusting, ['NotAdjusting']), State.State('NotAdjusting', self.enterNotAdjusting, self.exitNotAdjusting, ['Adjusting'])], 'NotAdjusting', 'NotAdjusting')
        self.adjustFsm.enterInitialState()
        self.interactiveProp = None
        return

    def uniqueBattleName(self, name):
        DistributedBattleBase.id += 1
        return name + '-%d' % DistributedBattleBase.id

    def generate(self):
        self.notify.debug('generate(%s)' % self.doId)
        DistributedNode.DistributedNode.generate(self)
        self.__battleCleanedUp = 0
        self.reparentTo(render)
        self._skippingRewardMovie = False

    def storeInterval(self, interval, name):
        if name in self.activeIntervals:
            ival = self.activeIntervals[name]
            if hasattr(ival, 'delayDelete') or hasattr(ival, 'delayDeletes'):
                self.clearInterval(name, finish=1)
        self.activeIntervals[name] = interval

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

        self.activeIntervals = {}

    def clearInterval(self, name, finish = 0):
        if self.activeIntervals.has_key(name):
            ival = self.activeIntervals[name]
            if finish:
                ival.finish()
            else:
                ival.pause()
            if self.activeIntervals.has_key(name):
                DelayDelete.cleanupDelayDeletes(ival)
                if self.activeIntervals.has_key(name):
                    del self.activeIntervals[name]
        else:
            self.notify.debug('interval: %s already cleared' % name)

    def finishInterval(self, name):
        if self.activeIntervals.has_key(name):
            interval = self.activeIntervals[name]
            interval.finish()

    def disable(self):
        self.notify.debug('disable(%s)' % self.doId)
        self.cleanupBattle()
        DistributedNode.DistributedNode.disable(self)

    def battleCleanedUp(self):
        return self.__battleCleanedUp

    def cleanupBattle(self):
        if self.__battleCleanedUp:
            return
        self.notify.debug('cleanupBattle(%s)' % self.doId)
        self.__battleCleanedUp = 1
        self.__cleanupIntervals()
        self.fsm.requestFinalState()
        if self.hasLocalToon():
            self.removeLocalToon()
            base.camLens.setFov(ToontownGlobals.DefaultCameraFov)
        self.localToonFsm.request('WaitForServer')
        self.ignoreAll()
        for suit in self.suits:
            if suit.battleTrap != NO_TRAP:
                self.notify.debug('250 calling self.removeTrap, suit=%d' % suit.doId)
                self.removeTrap(suit)
            suit.battleTrap = NO_TRAP
            suit.battleTrapProp = None
            self.notify.debug('253 suit.battleTrapProp = None')
            suit.battleTrapIsFresh = 0

        self.suits = []
        self.pendingSuits = []
        self.joiningSuits = []
        self.activeSuits = []
        self.suitTraps = ''
        self.toons = []
        self.joiningToons = []
        self.pendingToons = []
        self.activeToons = []
        self.runningToons = []
        self.__stopTimer()
        self.__cleanupIntervals()
        self._removeMembersKeep()
        return

    def delete(self):
        self.notify.debug('delete(%s)' % self.doId)
        self.__cleanupIntervals()
        self._removeMembersKeep()
        self.movie.cleanup()
        del self.townBattle
        self.removeNode()
        self.fsm = None
        self.localToonFsm = None
        self.adjustFsm = None
        self.__stopTimer()
        self.timer = None
        DistributedNode.DistributedNode.delete(self)
        return

    def loadTrap(self, suit, trapid):
        self.notify.debug('loadTrap() trap: %d suit: %d' % (trapid, suit.doId))
        trapName = AvProps[TRAP][trapid]
        trap = BattleProps.globalPropPool.getProp(trapName)
        suit.battleTrap = trapid
        suit.battleTrapIsFresh = 0
        suit.battleTrapProp = trap
        self.notify.debug('suit.battleTrapProp = trap %s' % trap)
        if trap.getName() == 'traintrack':
            pass
        else:
            trap.wrtReparentTo(suit)
        distance = MovieUtil.SUIT_TRAP_DISTANCE
        if trapName == 'rake':
            distance = MovieUtil.SUIT_TRAP_RAKE_DISTANCE
            distance += MovieUtil.getSuitRakeOffset(suit)
            trap.setH(180)
            trap.setScale(0.7)
        elif trapName == 'trapdoor' or trapName == 'quicksand':
            trap.setScale(1.7)
        elif trapName == 'marbles':
            distance = MovieUtil.SUIT_TRAP_MARBLES_DISTANCE
            trap.setH(94)
        elif trapName == 'tnt':
            trap.setP(90)
            tip = trap.find('**/joint_attachEmitter')
            sparks = BattleParticles.createParticleEffect(file='tnt')
            trap.sparksEffect = sparks
            sparks.start(tip)
        trap.setPos(0, distance, 0)
        if isinstance(trap, Actor.Actor):
            frame = trap.getNumFrames(trapName) - 1
            trap.pose(trapName, frame)

    def removeTrap(self, suit, removeTrainTrack = False):
        self.notify.debug('removeTrap() from suit: %d, removeTrainTrack=%s' % (suit.doId, removeTrainTrack))
        if suit.battleTrapProp == None:
            self.notify.debug('suit.battleTrapProp == None, suit.battleTrap=%s setting to NO_TRAP, returning' % suit.battleTrap)
            suit.battleTrap = NO_TRAP
            return
        if suit.battleTrap == UBER_GAG_LEVEL_INDEX:
            if removeTrainTrack:
                self.notify.debug('doing removeProp on traintrack')
                MovieUtil.removeProp(suit.battleTrapProp)
                for otherSuit in self.suits:
                    if not otherSuit == suit:
                        otherSuit.battleTrapProp = None
                        self.notify.debug('351 otherSuit=%d otherSuit.battleTrapProp = None' % otherSuit.doId)
                        otherSuit.battleTrap = NO_TRAP
                        otherSuit.battleTrapIsFresh = 0

            else:
                self.notify.debug('deliberately not doing removeProp on traintrack')
        else:
            self.notify.debug('suit.battleTrap != UBER_GAG_LEVEL_INDEX')
            MovieUtil.removeProp(suit.battleTrapProp)
        suit.battleTrapProp = None
        self.notify.debug('360 suit.battleTrapProp = None')
        suit.battleTrap = NO_TRAP
        suit.battleTrapIsFresh = 0
        return

    def pause(self):
        self.timer.stop()

    def unpause(self):
        self.timer.resume()

    def findSuit(self, id):
        for s in self.suits:
            if s.doId == id:
                return s

        return None

    def findToon(self, id):
        toon = self.getToon(id)
        if toon == None:
            return
        for t in self.toons:
            if t == toon:
                return t

        return

    def isSuitLured(self, suit):
        if self.luredSuits.count(suit) != 0:
            return 1
        return 0

    def unlureSuit(self, suit):
        self.notify.debug('movie unluring suit %s' % suit.doId)
        if self.luredSuits.count(suit) != 0:
            self.luredSuits.remove(suit)
            self.needAdjustTownBattle = 1
        return None

    def lureSuit(self, suit):
        self.notify.debug('movie luring suit %s' % suit.doId)
        if self.luredSuits.count(suit) == 0:
            self.luredSuits.append(suit)
            self.needAdjustTownBattle = 1
        return None

    def getActorPosHpr(self, actor, actorList = []):
        if isinstance(actor, Suit.Suit):
            if actorList == []:
                actorList = self.activeSuits
            if actorList.count(actor) != 0:
                numSuits = len(actorList) - 1
                index = actorList.index(actor)
                point = self.suitPoints[numSuits][index]
                return (Point3(point[0]), VBase3(point[1], 0.0, 0.0))
            else:
                self.notify.warning('getActorPosHpr() - suit not active')
        else:
            if actorList == []:
                actorList = self.activeToons
            if actorList.count(actor) != 0:
                numToons = len(actorList) - 1
                index = actorList.index(actor)
                point = self.toonPoints[numToons][index]
                return (Point3(point[0]), VBase3(point[1], 0.0, 0.0))
            else:
                self.notify.warning('getActorPosHpr() - toon not active')

    def setLevelDoId(self, levelDoId):
        pass

    def setBattleCellId(self, battleCellId):
        pass

    def setInteractivePropTrackBonus(self, trackBonus):
        self.interactivePropTrackBonus = trackBonus

    def getInteractivePropTrackBonus(self):
        return self.interactivePropTrackBonus

    def setPosition(self, x, y, z):
        self.notify.debug('setPosition() - %d %d %d' % (x, y, z))
        pos = Point3(x, y, z)
        self.setPos(pos)

    def setInitialSuitPos(self, x, y, z):
        self.initialSuitPos = Point3(x, y, z)
        self.headsUp(self.initialSuitPos)

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

    def setBossBattle(self, value):
        self.bossBattle = value

    def setState(self, state, timestamp):
        if self.__battleCleanedUp:
            return
        self.notify.debug('setState(%s)' % state)
        self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])

    def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp):
        if self.__battleCleanedUp:
            return
        self.notify.debug('setMembers() - suits: %s suitsJoining: %s suitsPending: %s suitsActive: %s suitsLured: %s suitTraps: %s toons: %s toonsJoining: %s toonsPending: %s toonsActive: %s toonsRunning: %s' % (suits,
         suitsJoining,
         suitsPending,
         suitsActive,
         suitsLured,
         suitTraps,
         toons,
         toonsJoining,
         toonsPending,
         toonsActive,
         toonsRunning))
        ts = globalClockDelta.localElapsedTime(timestamp)
        oldsuits = self.suits
        self.suits = []
        suitGone = 0
        for s in suits:
            if self.cr.doId2do.has_key(s):
                suit = self.cr.doId2do[s]
                suit.setState('Battle')
                self.suits.append(suit)
                suit.interactivePropTrackBonus = self.interactivePropTrackBonus
                try:
                    suit.battleTrap
                except:
                    suit.battleTrap = NO_TRAP
                    suit.battleTrapProp = None
                    self.notify.debug('496 suit.battleTrapProp = None')
                    suit.battleTrapIsFresh = 0

            else:
                self.notify.warning('setMembers() - no suit in repository: %d' % s)
                self.suits.append(None)
                suitGone = 1

        numSuitsThatDied = 0
        for s in oldsuits:
            if self.suits.count(s) == 0:
                self.__removeSuit(s)
                numSuitsThatDied += 1
                self.notify.debug('suit %d dies, numSuitsThatDied=%d' % (s.doId, numSuitsThatDied))

        if numSuitsThatDied == 4:
            trainTrap = self.find('**/traintrack')
            if not trainTrap.isEmpty():
                self.notify.debug('removing old train trap when 4 suits died')
                trainTrap.removeNode()
        for s in suitsJoining:
            suit = self.suits[int(s)]
            if suit != None and self.joiningSuits.count(suit) == 0:
                self.makeSuitJoin(suit, ts)

        for s in suitsPending:
            suit = self.suits[int(s)]
            if suit != None and self.pendingSuits.count(suit) == 0:
                self.__makeSuitPending(suit)

        activeSuits = []
        for s in suitsActive:
            suit = self.suits[int(s)]
            if suit != None and self.activeSuits.count(suit) == 0:
                activeSuits.append(suit)

        oldLuredSuits = self.luredSuits
        self.luredSuits = []
        for s in suitsLured:
            suit = self.suits[int(s)]
            if suit != None:
                self.luredSuits.append(suit)
                if oldLuredSuits.count(suit) == 0:
                    self.needAdjustTownBattle = 1

        if self.needAdjustTownBattle == 0:
            for s in oldLuredSuits:
                if self.luredSuits.count(s) == 0:
                    self.needAdjustTownBattle = 1

        index = 0
        oldSuitTraps = self.suitTraps
        self.suitTraps = suitTraps
        for s in suitTraps:
            trapid = int(s)
            if trapid == 9:
                trapid = -1
            suit = self.suits[index]
            index += 1
            if suit != None:
                if (trapid == NO_TRAP or trapid != suit.battleTrap) and suit.battleTrapProp != None:
                    self.notify.debug('569 calling self.removeTrap, suit=%d' % suit.doId)
                    self.removeTrap(suit)
                if trapid != NO_TRAP and suit.battleTrapProp == None:
                    if self.fsm.getCurrentState().getName() != 'PlayMovie':
                        self.loadTrap(suit, trapid)

        if len(oldSuitTraps) != len(self.suitTraps):
            self.needAdjustTownBattle = 1
        else:
            for i in range(len(oldSuitTraps)):
                if oldSuitTraps[i] == '9' and self.suitTraps[i] != '9' or oldSuitTraps[i] != '9' and self.suitTraps[i] == '9':
                    self.needAdjustTownBattle = 1
                    break

        if suitGone:
            validSuits = []
            for s in self.suits:
                if s != None:
                    validSuits.append(s)

            self.suits = validSuits
            self.needAdjustTownBattle = 1
        oldtoons = self.toons
        self.toons = []
        toonGone = 0
        for t in toons:
            toon = self.getToon(t)
            if toon == None:
                self.notify.warning('setMembers() - toon not in cr!')
                self.toons.append(None)
                toonGone = 1
                continue
            self.toons.append(toon)
            if oldtoons.count(toon) == 0:
                self.notify.debug('setMembers() - add toon: %d' % toon.doId)
                self.__listenForUnexpectedExit(toon)
                toon.stopLookAround()
                toon.stopSmooth()

        for t in oldtoons:
            if self.toons.count(t) == 0:
                if self.__removeToon(t) == 1:
                    self.notify.debug('setMembers() - local toon left battle')
                    return []

        for t in toonsJoining:
            if int(t) < len(self.toons):
                toon = self.toons[int(t)]
                if toon != None and self.joiningToons.count(toon) == 0:
                    self.__makeToonJoin(toon, toonsPending, ts)
            else:
                self.notify.warning('setMembers toonsJoining t=%s not in self.toons %s' % (t, self.toons))

        for t in toonsPending:
            if int(t) < len(self.toons):
                toon = self.toons[int(t)]
                if toon != None and self.pendingToons.count(toon) == 0:
                    self.__makeToonPending(toon, ts)
            else:
                self.notify.warning('setMembers toonsPending t=%s not in self.toons %s' % (t, self.toons))

        for t in toonsRunning:
            toon = self.toons[int(t)]
            if toon != None and self.runningToons.count(toon) == 0:
                self.__makeToonRun(toon, ts)

        activeToons = []
        for t in toonsActive:
            toon = self.toons[int(t)]
            if toon != None and self.activeToons.count(toon) == 0:
                activeToons.append(toon)

        if len(activeSuits) > 0 or len(activeToons) > 0:
            self.__makeAvsActive(activeSuits, activeToons)
        if toonGone == 1:
            validToons = []
            for toon in self.toons:
                if toon != None:
                    validToons.append(toon)

            self.toons = validToons
        if len(self.activeToons) > 0:
            self.__requestAdjustTownBattle()
        currStateName = self.localToonFsm.getCurrentState().getName()
        if self.toons.count(base.localAvatar):
            if oldtoons.count(base.localAvatar) == 0:
                self.notify.debug('setMembers() - local toon just joined')
                if self.streetBattle == 1:
                    base.cr.playGame.getPlace().enterZone(self.zoneId)
                self.localToonJustJoined = 1
            if currStateName != 'HasLocalToon':
                self.localToonFsm.request('HasLocalToon')
        else:
            if oldtoons.count(base.localAvatar):
                self.notify.debug('setMembers() - local toon just ran')
                if self.levelBattle:
                    self.unlockLevelViz()
            if currStateName != 'NoLocalToon':
                self.localToonFsm.request('NoLocalToon')
        return oldtoons

    def adjust(self, timestamp):
        if self.__battleCleanedUp:
            return
        self.notify.debug('adjust(%f) from server' % globalClockDelta.localElapsedTime(timestamp))
        self.adjustFsm.request('Adjusting', [globalClockDelta.localElapsedTime(timestamp)])

    def setMovie(self, active, toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3):
        if self.__battleCleanedUp:
            return
        self.notify.debug('setMovie()')
        if int(active) == 1:
            self.notify.debug('setMovie() - movie is active')
            self.movie.genAttackDicts(toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3)

    def setChosenToonAttacks(self, ids, tracks, levels, targets):
        if self.__battleCleanedUp:
            return
        self.notify.debug('setChosenToonAttacks() - (%s), (%s), (%s), (%s)' % (ids,
         tracks,
         levels,
         targets))
        toonIndices = []
        targetIndices = []
        unAttack = 0
        localToonInList = 0
        for i in range(len(ids)):
            track = tracks[i]
            level = levels[i]
            toon = self.findToon(ids[i])
            if toon == None or self.activeToons.count(toon) == 0:
                self.notify.warning('setChosenToonAttacks() - toon gone or not in battle: %d!' % ids[i])
                toonIndices.append(-1)
                tracks.append(-1)
                levels.append(-1)
                targetIndices.append(-1)
                continue
            if toon == base.localAvatar:
                localToonInList = 1
            toonIndices.append(self.activeToons.index(toon))
            if track == SOS:
                targetIndex = -1
            elif track == NPCSOS:
                targetIndex = -1
            elif track == PETSOS:
                targetIndex = -1
            elif track == PASS:
                targetIndex = -1
                tracks[i] = PASS_ATTACK
            elif attackAffectsGroup(track, level):
                targetIndex = -1
            elif track == HEAL:
                target = self.findToon(targets[i])
                if target != None and self.activeToons.count(target) != 0:
                    targetIndex = self.activeToons.index(target)
                else:
                    targetIndex = -1
            elif track == UN_ATTACK:
                targetIndex = -1
                tracks[i] = NO_ATTACK
                if toon == base.localAvatar:
                    unAttack = 1
                    self.choseAttackAlready = 0
            elif track == NO_ATTACK:
                targetIndex = -1
            else:
                target = self.findSuit(targets[i])
                if target != None and self.activeSuits.count(target) != 0:
                    targetIndex = self.activeSuits.index(target)
                else:
                    targetIndex = -1
            targetIndices.append(targetIndex)

        for i in range(4 - len(ids)):
            toonIndices.append(-1)
            tracks.append(-1)
            levels.append(-1)
            targetIndices.append(-1)

        self.townBattleAttacks = (toonIndices,
         tracks,
         levels,
         targetIndices)
        if self.localToonActive() and localToonInList == 1:
            if unAttack == 1 and self.fsm.getCurrentState().getName() == 'WaitForInput':
                if self.townBattle.fsm.getCurrentState().getName() != 'Attack':
                    self.townBattle.setState('Attack')
            self.townBattle.updateChosenAttacks(self.townBattleAttacks[0], self.townBattleAttacks[1], self.townBattleAttacks[2], self.townBattleAttacks[3])
        return

    def setBattleExperience(self, id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList):
        if self.__battleCleanedUp:
            return
        self.movie.genRewardDicts(id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList)

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

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

    def __handleDied(self, toon):
        self.notify.warning('handleDied() - toon: %d' % toon.doId)
        if toon == base.localAvatar:
            self.d_toonDied(toon.doId)
            self.cleanupBattle()

    def delayDeleteMembers(self):
        membersKeep = []
        for t in self.toons:
            membersKeep.append(DelayDelete.DelayDelete(t, 'delayDeleteMembers'))

        for s in self.suits:
            membersKeep.append(DelayDelete.DelayDelete(s, 'delayDeleteMembers'))

        self._removeMembersKeep()
        self.membersKeep = membersKeep

    def _removeMembersKeep(self):
        if self.membersKeep:
            for delayDelete in self.membersKeep:
                delayDelete.destroy()

        self.membersKeep = None
        return

    def __removeSuit(self, suit):
        self.notify.debug('__removeSuit(%d)' % suit.doId)
        if self.suits.count(suit) != 0:
            self.suits.remove(suit)
        if self.joiningSuits.count(suit) != 0:
            self.joiningSuits.remove(suit)
        if self.pendingSuits.count(suit) != 0:
            self.pendingSuits.remove(suit)
        if self.activeSuits.count(suit) != 0:
            self.activeSuits.remove(suit)
        self.suitGone = 1
        if suit.battleTrap != NO_TRAP:
            self.notify.debug('882 calling self.removeTrap, suit=%d' % suit.doId)
            self.removeTrap(suit)
        suit.battleTrap = NO_TRAP
        suit.battleTrapProp = None
        self.notify.debug('883 suit.battleTrapProp = None')
        suit.battleTrapIsFresh = 0
        return

    def __removeToon(self, toon, unexpected = 0):
        self.notify.debug('__removeToon(%d)' % toon.doId)
        self.exitedToons.append(toon)
        if self.toons.count(toon) != 0:
            self.toons.remove(toon)
        if self.joiningToons.count(toon) != 0:
            self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId))
            if toon in self.joiningToons:
                self.joiningToons.remove(toon)
        if self.pendingToons.count(toon) != 0:
            self.pendingToons.remove(toon)
        if self.activeToons.count(toon) != 0:
            self.activeToons.remove(toon)
        if self.runningToons.count(toon) != 0:
            self.clearInterval(self.taskName('running-%d' % toon.doId), finish=1)
            if toon in self.runningToons:
                self.runningToons.remove(toon)
        self.ignore(toon.uniqueName('disable'))
        self.ignore(toon.uniqueName('died'))
        self.toonGone = 1
        if toon == base.localAvatar:
            self.removeLocalToon()
            self.__teleportToSafeZone(toon)
            return 1
        return 0

    def removeLocalToon(self):
        if self._skippingRewardMovie:
            return
        if base.cr.playGame.getPlace() != None:
            base.cr.playGame.getPlace().setState('walk')
        base.localAvatar.earnedExperience = None
        self.localToonFsm.request('NoLocalToon')
        return

    def removeInactiveLocalToon(self, toon):
        self.notify.debug('removeInactiveLocalToon(%d)' % toon.doId)
        self.exitedToons.append(toon)
        if self.toons.count(toon) != 0:
            self.toons.remove(toon)
        if self.joiningToons.count(toon) != 0:
            self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId), finish=1)
            if toon in self.joiningToons:
                self.joiningToons.remove(toon)
        if self.pendingToons.count(toon) != 0:
            self.pendingToons.remove(toon)
        self.ignore(toon.uniqueName('disable'))
        self.ignore(toon.uniqueName('died'))
        base.cr.playGame.getPlace().setState('walk')
        self.localToonFsm.request('WaitForServer')

    def __createJoinInterval(self, av, destPos, destHpr, name, ts, callback, toon = 0):
        joinTrack = Sequence()
        joinTrack.append(Func(Emote.globalEmote.disableAll, av, 'dbattlebase, createJoinInterval'))
        avPos = av.getPos(self)
        avPos = Point3(avPos[0], avPos[1], 0.0)
        av.setShadowHeight(0)
        plist = self.buildJoinPointList(avPos, destPos, toon)
        if len(plist) == 0:
            joinTrack.append(Func(av.headsUp, self, destPos))
            if toon == 0:
                timeToDest = self.calcSuitMoveTime(avPos, destPos)
                joinTrack.append(Func(av.loop, 'walk'))
            else:
                timeToDest = self.calcToonMoveTime(avPos, destPos)
                joinTrack.append(Func(av.loop, 'run'))
            if timeToDest > BATTLE_SMALL_VALUE:
                joinTrack.append(LerpPosInterval(av, timeToDest, destPos, other=self))
                totalTime = timeToDest
            else:
                totalTime = 0
        else:
            timeToPerimeter = 0
            if toon == 0:
                timeToPerimeter = self.calcSuitMoveTime(plist[0], avPos)
                timePerSegment = 10.0 / BattleBase.suitSpeed
                timeToDest = self.calcSuitMoveTime(BattleBase.posA, destPos)
            else:
                timeToPerimeter = self.calcToonMoveTime(plist[0], avPos)
                timePerSegment = 10.0 / BattleBase.toonSpeed
                timeToDest = self.calcToonMoveTime(BattleBase.posE, destPos)
            totalTime = timeToPerimeter + (len(plist) - 1) * timePerSegment + timeToDest
            if totalTime > MAX_JOIN_T:
                self.notify.warning('__createJoinInterval() - time: %f' % totalTime)
            joinTrack.append(Func(av.headsUp, self, plist[0]))
            if toon == 0:
                joinTrack.append(Func(av.loop, 'walk'))
            else:
                joinTrack.append(Func(av.loop, 'run'))
            joinTrack.append(LerpPosInterval(av, timeToPerimeter, plist[0], other=self))
            for p in plist[1:]:
                joinTrack.append(Func(av.headsUp, self, p))
                joinTrack.append(LerpPosInterval(av, timePerSegment, p, other=self))

            joinTrack.append(Func(av.headsUp, self, destPos))
            joinTrack.append(LerpPosInterval(av, timeToDest, destPos, other=self))
        joinTrack.append(Func(av.loop, 'neutral'))
        joinTrack.append(Func(av.headsUp, self, Point3(0, 0, 0)))
        tval = totalTime - ts
        if tval < 0:
            tval = totalTime
        joinTrack.append(Func(Emote.globalEmote.releaseAll, av, 'dbattlebase, createJoinInterval'))
        joinTrack.append(Func(callback, av, tval))
        if av == base.localAvatar:
            camTrack = Sequence()

            def setCamFov(fov):
                base.camLens.setFov(fov)

            camTrack.append(Func(setCamFov, self.camFov))
            camTrack.append(Func(camera.wrtReparentTo, self))
            camTrack.append(Func(camera.setPos, self.camJoinPos))
            camTrack.append(Func(camera.setHpr, self.camJoinHpr))
            return Parallel(joinTrack, camTrack, name=name)
        else:
            return Sequence(joinTrack, name=name)

    def makeSuitJoin(self, suit, ts):
        self.notify.debug('makeSuitJoin(%d)' % suit.doId)
        spotIndex = len(self.pendingSuits) + len(self.joiningSuits)
        self.joiningSuits.append(suit)
        suit.setState('Battle')
        openSpot = self.suitPendingPoints[spotIndex]
        pos = openSpot[0]
        hpr = VBase3(openSpot[1], 0.0, 0.0)
        trackName = self.taskName('to-pending-suit-%d' % suit.doId)
        track = self.__createJoinInterval(suit, pos, hpr, trackName, ts, self.__handleSuitJoinDone)
        track.start(ts)
        track.delayDelete = DelayDelete.DelayDelete(suit, 'makeSuitJoin')
        self.storeInterval(track, trackName)
        if ToontownBattleGlobals.SkipMovie:
            track.finish()

    def __handleSuitJoinDone(self, suit, ts):
        self.notify.debug('suit: %d is now pending' % suit.doId)
        if self.hasLocalToon():
            self.d_joinDone(base.localAvatar.doId, suit.doId)

    def __makeSuitPending(self, suit):
        self.notify.debug('__makeSuitPending(%d)' % suit.doId)
        self.clearInterval(self.taskName('to-pending-suit-%d' % suit.doId), finish=1)
        if self.joiningSuits.count(suit):
            self.joiningSuits.remove(suit)
        self.pendingSuits.append(suit)

    def __teleportToSafeZone(self, toon):
        self.notify.debug('teleportToSafeZone(%d)' % toon.doId)
        hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId)
        if hoodId in base.localAvatar.hoodsVisited:
            target_sz = ZoneUtil.getSafeZoneId(self.zoneId)
        else:
            target_sz = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone)
        base.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(target_sz),
          'where': ZoneUtil.getWhereName(target_sz, 1),
          'how': 'teleportIn',
          'hoodId': target_sz,
          'zoneId': target_sz,
          'shardId': None,
          'avId': -1,
          'battle': 1}])
        return

    def __makeToonJoin(self, toon, pendingToons, ts):
        self.notify.debug('__makeToonJoin(%d)' % toon.doId)
        spotIndex = len(pendingToons) + len(self.joiningToons)
        self.joiningToons.append(toon)
        openSpot = self.toonPendingPoints[spotIndex]
        pos = openSpot[0]
        hpr = VBase3(openSpot[1], 0.0, 0.0)
        trackName = self.taskName('to-pending-toon-%d' % toon.doId)
        track = self.__createJoinInterval(toon, pos, hpr, trackName, ts, self.__handleToonJoinDone, toon=1)
        if toon != base.localAvatar:
            toon.animFSM.request('off')
        track.start(ts)
        track.delayDelete = DelayDelete.DelayDelete(toon, '__makeToonJoin')
        self.storeInterval(track, trackName)

    def __handleToonJoinDone(self, toon, ts):
        self.notify.debug('__handleToonJoinDone() - pending: %d' % toon.doId)
        if self.hasLocalToon():
            self.d_joinDone(base.localAvatar.doId, toon.doId)

    def __makeToonPending(self, toon, ts):
        self.notify.debug('__makeToonPending(%d)' % toon.doId)
        self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId), finish=1)
        if self.joiningToons.count(toon):
            self.joiningToons.remove(toon)
        spotIndex = len(self.pendingToons)
        self.pendingToons.append(toon)
        openSpot = self.toonPendingPoints[spotIndex]
        pos = openSpot[0]
        hpr = VBase3(openSpot[1], 0.0, 0.0)
        toon.loop('neutral')
        toon.setPosHpr(self, pos, hpr)
        if base.localAvatar == toon:
            currStateName = self.fsm.getCurrentState().getName()

    def __makeAvsActive(self, suits, toons):
        self.notify.debug('__makeAvsActive()')
        self.__stopAdjusting()
        for s in suits:
            if self.joiningSuits.count(s):
                self.notify.warning('suit: %d was in joining list!' % s.doId)
                self.joiningSuits.remove(s)
            if self.pendingSuits.count(s):
                self.pendingSuits.remove(s)
            self.notify.debug('__makeAvsActive() - suit: %d' % s.doId)
            self.activeSuits.append(s)

        if len(self.activeSuits) >= 1:
            for suit in self.activeSuits:
                suitPos, suitHpr = self.getActorPosHpr(suit)
                if self.isSuitLured(suit) == 0:
                    suit.setPosHpr(self, suitPos, suitHpr)
                else:
                    spos = Point3(suitPos[0], suitPos[1] - MovieUtil.SUIT_LURE_DISTANCE, suitPos[2])
                    suit.setPosHpr(self, spos, suitHpr)
                suit.loop('neutral')

        for toon in toons:
            if self.joiningToons.count(toon):
                self.notify.warning('toon: %d was in joining list!' % toon.doId)
                self.joiningToons.remove(toon)
            if self.pendingToons.count(toon):
                self.pendingToons.remove(toon)
            self.notify.debug('__makeAvsActive() - toon: %d' % toon.doId)
            if self.activeToons.count(toon) == 0:
                self.activeToons.append(toon)
            else:
                self.notify.warning('makeAvsActive() - toon: %d is active!' % toon.doId)

        if len(self.activeToons) >= 1:
            for toon in self.activeToons:
                toonPos, toonHpr = self.getActorPosHpr(toon)
                toon.setPosHpr(self, toonPos, toonHpr)
                toon.loop('neutral')

        if self.fsm.getCurrentState().getName() == 'WaitForInput' and self.localToonActive() and self.localToonJustJoined == 1:
            self.notify.debug('makeAvsActive() - local toon just joined')
            self.__enterLocalToonWaitForInput()
            self.localToonJustJoined = 0
            self.startTimer()

    def __makeToonRun(self, toon, ts):
        self.notify.debug('__makeToonRun(%d)' % toon.doId)
        if self.activeToons.count(toon):
            self.activeToons.remove(toon)
        self.runningToons.append(toon)
        self.toonGone = 1
        self.__stopTimer()
        if self.localToonRunning():
            self.townBattle.setState('Off')
        runMTrack = MovieUtil.getToonTeleportOutInterval(toon)
        runName = self.taskName('running-%d' % toon.doId)
        self.notify.debug('duration: %f' % runMTrack.getDuration())
        runMTrack.start(ts)
        runMTrack.delayDelete = DelayDelete.DelayDelete(toon, '__makeToonRun')
        self.storeInterval(runMTrack, runName)

    def getToon(self, toonId):
        if self.cr.doId2do.has_key(toonId):
            return self.cr.doId2do[toonId]
        else:
            self.notify.warning('getToon() - toon: %d not in repository!' % toonId)
            return None
        return None

    def d_toonRequestJoin(self, toonId, pos):
        self.notify.debug('network:toonRequestJoin()')
        self.sendUpdate('toonRequestJoin', [pos[0], pos[1], pos[2]])

    def d_toonRequestRun(self, toonId):
        self.notify.debug('network:toonRequestRun()')
        self.sendUpdate('toonRequestRun', [])

    def d_toonDied(self, toonId):
        self.notify.debug('network:toonDied()')
        self.sendUpdate('toonDied', [])

    def d_faceOffDone(self, toonId):
        self.notify.debug('network:faceOffDone()')
        self.sendUpdate('faceOffDone', [])

    def d_adjustDone(self, toonId):
        self.notify.debug('network:adjustDone()')
        self.sendUpdate('adjustDone', [])

    def d_timeout(self, toonId):
        self.notify.debug('network:timeout()')
        self.sendUpdate('timeout', [])

    def d_movieDone(self, toonId):
        self.notify.debug('network:movieDone()')
        self.sendUpdate('movieDone', [])

    def d_rewardDone(self, toonId):
        self.notify.debug('network:rewardDone()')
        self.sendUpdate('rewardDone', [])

    def d_joinDone(self, toonId, avId):
        self.notify.debug('network:joinDone(%d)' % avId)
        self.sendUpdate('joinDone', [avId])

    def d_requestAttack(self, toonId, track, level, av):
        self.notify.debug('network:requestAttack(%d, %d, %d)' % (track, level, av))
        self.sendUpdate('requestAttack', [track, level, av])

    def d_requestPetProxy(self, toonId, av):
        self.notify.debug('network:requestPetProxy(%s)' % av)
        self.sendUpdate('requestPetProxy', [av])

    def enterOff(self, ts = 0):
        self.localToonFsm.requestFinalState()
        return None

    def exitOff(self):
        return None

    def enterFaceOff(self, ts = 0):
        return None

    def exitFaceOff(self):
        return None

    def enterWaitForJoin(self, ts = 0):
        self.notify.debug('enterWaitForJoin()')
        return None

    def exitWaitForJoin(self):
        return None

    def __enterLocalToonWaitForInput(self):
        self.notify.debug('enterLocalToonWaitForInput()')
        camera.setPosHpr(self.camPos, self.camHpr)
        base.camLens.setFov(self.camMenuFov)
        NametagGlobals.setMasterArrowsOn(0)
        self.townBattle.setState('Attack')
        self.accept(self.localToonBattleEvent, self.__handleLocalToonBattleEvent)

    def startTimer(self, ts = 0):
        self.notify.debug('startTimer()')
        if ts >= CLIENT_INPUT_TIMEOUT:
            self.notify.warning('startTimer() - ts: %f timeout: %f' % (ts, CLIENT_INPUT_TIMEOUT))
            self.__timedOut()
            return
        self.timer.startCallback(CLIENT_INPUT_TIMEOUT - ts, self.__timedOut)
        timeTask = Task.loop(Task(self.__countdown), Task.pause(0.2))
        taskMgr.add(timeTask, self.timerCountdownTaskName)

    def __stopTimer(self):
        self.notify.debug('__stopTimer()')
        self.timer.stop()
        taskMgr.remove(self.timerCountdownTaskName)

    def __countdown(self, task):
        if hasattr(self.townBattle, 'timer'):
            self.townBattle.updateTimer(int(self.timer.getT()))
        else:
            self.notify.warning('__countdown has tried to update a timer that has been deleted. Stopping timer')
            self.__stopTimer()
        return Task.done

    def enterWaitForInput(self, ts = 0):
        self.notify.debug('enterWaitForInput()')
        if self.interactiveProp:
            self.interactiveProp.gotoBattleCheer()
        self.choseAttackAlready = 0
        if self.localToonActive():
            self.__enterLocalToonWaitForInput()
            self.startTimer(ts)
        if self.needAdjustTownBattle == 1:
            self.__adjustTownBattle()
        return None

    def exitWaitForInput(self):
        self.notify.debug('exitWaitForInput()')
        if self.localToonActive():
            self.townBattle.setState('Off')
            base.camLens.setFov(self.camFov)
            self.ignore(self.localToonBattleEvent)
            self.__stopTimer()
        return None

    def __handleLocalToonBattleEvent(self, response):
        mode = response['mode']
        noAttack = 0
        if mode == 'Attack':
            self.notify.debug('got an attack')
            track = response['track']
            level = response['level']
            target = response['target']
            targetId = target
            if track == HEAL and not levelAffectsGroup(HEAL, level):
                if target >= 0 and target < len(self.activeToons):
                    targetId = self.activeToons[target].doId
                else:
                    self.notify.warning('invalid toon target: %d' % target)
                    track = -1
                    level = -1
                    targetId = -1
            elif track == HEAL and len(self.activeToons) == 1:
                self.notify.warning('invalid group target for heal')
                track = -1
                level = -1
            elif not attackAffectsGroup(track, level):
                if target >= 0 and target < len(self.activeSuits):
                    targetId = self.activeSuits[target].doId
                else:
                    target = -1
            if len(self.luredSuits) > 0:
                if track == TRAP or track == LURE and not levelAffectsGroup(LURE, level):
                    if target != -1:
                        suit = self.findSuit(targetId)
                        if self.luredSuits.count(suit) != 0:
                            self.notify.warning('Suit: %d was lured!' % targetId)
                            track = -1
                            level = -1
                            targetId = -1
                elif track == LURE:
                    if levelAffectsGroup(LURE, level) and len(self.activeSuits) == len(self.luredSuits):
                        self.notify.warning('All suits are lured!')
                        track = -1
                        level = -1
                        targetId = -1
            if track == TRAP:
                if target != -1:
                    if attackAffectsGroup(track, level):
                        pass
                    else:
                        suit = self.findSuit(targetId)
                        if suit.battleTrap != NO_TRAP:
                            self.notify.warning('Suit: %d was already trapped!' % targetId)
                            track = -1
                            level = -1
                            targetId = -1
            self.d_requestAttack(base.localAvatar.doId, track, level, targetId)
        elif mode == 'Run':
            self.notify.debug('got a run')
            self.d_toonRequestRun(base.localAvatar.doId)
        elif mode == 'SOS':
            targetId = response['id']
            self.notify.debug('got an SOS for friend: %d' % targetId)
            self.d_requestAttack(base.localAvatar.doId, SOS, -1, targetId)
        elif mode == 'NPCSOS':
            targetId = response['id']
            self.notify.debug('got an NPCSOS for friend: %d' % targetId)
            self.d_requestAttack(base.localAvatar.doId, NPCSOS, -1, targetId)
        elif mode == 'PETSOS':
            targetId = response['id']
            trickId = response['trickId']
            self.notify.debug('got an PETSOS for pet: %d' % targetId)
            self.d_requestAttack(base.localAvatar.doId, PETSOS, trickId, targetId)
        elif mode == 'PETSOSINFO':
            petProxyId = response['id']
            self.notify.debug('got a PETSOSINFO for pet: %d' % petProxyId)
            if base.cr.doId2do.has_key(petProxyId):
                self.notify.debug('pet: %d was already in the repository' % petProxyId)
                proxyGenerateMessage = 'petProxy-%d-generated' % petProxyId
                messenger.send(proxyGenerateMessage)
            else:
                self.d_requestPetProxy(base.localAvatar.doId, petProxyId)
            noAttack = 1
        elif mode == 'Pass':
            targetId = response['id']
            self.notify.debug('got a Pass')
            self.d_requestAttack(base.localAvatar.doId, PASS, -1, -1)
        elif mode == 'UnAttack':
            self.d_requestAttack(base.localAvatar.doId, UN_ATTACK, -1, -1)
            noAttack = 1
        elif mode == 'Fire':
            target = response['target']
            targetId = self.activeSuits[target].doId
            self.d_requestAttack(base.localAvatar.doId, FIRE, -1, targetId)
        else:
            self.notify.warning('unknown battle response')
            return
        if noAttack == 1:
            self.choseAttackAlready = 0
        else:
            self.choseAttackAlready = 1

    def __timedOut(self):
        if self.choseAttackAlready == 1:
            return
        self.notify.debug('WaitForInput timed out')
        if self.localToonActive():
            self.notify.debug('battle timed out')
            self.d_timeout(base.localAvatar.doId)

    def enterMakeMovie(self, ts = 0):
        self.notify.debug('enterMakeMovie()')
        return None

    def exitMakeMovie(self):
        return None

    def enterPlayMovie(self, ts):
        self.notify.debug('enterPlayMovie()')
        self.delayDeleteMembers()
        if self.hasLocalToon():
            NametagGlobals.setMasterArrowsOn(0)
        if ToontownBattleGlobals.SkipMovie:
            self.movie.play(ts, self.__handleMovieDone)
            self.movie.finish()
        else:
            self.movie.play(ts, self.__handleMovieDone)
        return None

    def __handleMovieDone(self):
        self.notify.debug('__handleMovieDone()')
        if self.hasLocalToon():
            self.d_movieDone(base.localAvatar.doId)
        self.movie.reset()

    def exitPlayMovie(self):
        self.notify.debug('exitPlayMovie()')
        self.movie.reset(finish=1)
        self._removeMembersKeep()
        self.townBattleAttacks = ([-1,
          -1,
          -1,
          -1],
         [-1,
          -1,
          -1,
          -1],
         [-1,
          -1,
          -1,
          -1],
         [0,
          0,
          0,
          0])
        return None

    def hasLocalToon(self):
        return self.toons.count(base.localAvatar) > 0

    def localToonPendingOrActive(self):
        return self.pendingToons.count(base.localAvatar) > 0 or self.activeToons.count(base.localAvatar) > 0

    def localToonActive(self):
        return self.activeToons.count(base.localAvatar) > 0

    def localToonActiveOrRunning(self):
        return self.activeToons.count(base.localAvatar) > 0 or self.runningToons.count(base.localAvatar) > 0

    def localToonRunning(self):
        return self.runningToons.count(base.localAvatar) > 0

    def enterHasLocalToon(self):
        self.notify.debug('enterHasLocalToon()')
        if base.cr.playGame.getPlace() != None:
            base.cr.playGame.getPlace().setState('battle', self.localToonBattleEvent)
            if localAvatar and hasattr(localAvatar, 'inventory') and localAvatar.inventory:
                localAvatar.inventory.setInteractivePropTrackBonus(self.interactivePropTrackBonus)
        camera.wrtReparentTo(self)
        base.camLens.setFov(self.camFov)
        return

    def exitHasLocalToon(self):
        self.ignore(self.localToonBattleEvent)
        self.__stopTimer()
        if localAvatar and hasattr(localAvatar, 'inventory') and localAvatar.inventory:
            localAvatar.inventory.setInteractivePropTrackBonus(-1)
        stateName = None
        place = base.cr.playGame.getPlace()
        if place:
            stateName = place.fsm.getCurrentState().getName()
        if stateName == 'died':
            self.movie.reset()
            camera.reparentTo(render)
            camera.setPosHpr(localAvatar, 5.2, 5.45, localAvatar.getHeight() * 0.66, 131.5, 3.6, 0)
        else:
            camera.wrtReparentTo(base.localAvatar)
            messenger.send('localToonLeftBattle')
        base.camLens.setFov(ToontownGlobals.DefaultCameraFov)
        return

    def enterNoLocalToon(self):
        self.notify.debug('enterNoLocalToon()')
        return None

    def exitNoLocalToon(self):
        return None

    def setSkippingRewardMovie(self):
        self._skippingRewardMovie = True

    def enterWaitForServer(self):
        self.notify.debug('enterWaitForServer()')
        return None

    def exitWaitForServer(self):
        return None

    def createAdjustInterval(self, av, destPos, destHpr, toon = 0, run = 0):
        if run == 1:
            adjustTime = self.calcToonMoveTime(destPos, av.getPos(self))
        else:
            adjustTime = self.calcSuitMoveTime(destPos, av.getPos(self))
        self.notify.debug('creating adjust interval for: %d' % av.doId)
        adjustTrack = Sequence()
        if run == 1:
            adjustTrack.append(Func(av.loop, 'run'))
        else:
            adjustTrack.append(Func(av.loop, 'walk'))
        adjustTrack.append(Func(av.headsUp, self, destPos))
        adjustTrack.append(LerpPosInterval(av, adjustTime, destPos, other=self))
        adjustTrack.append(Func(av.setHpr, self, destHpr))
        adjustTrack.append(Func(av.loop, 'neutral'))
        return adjustTrack

    def __adjust(self, ts, callback):
        self.notify.debug('__adjust(%f)' % ts)
        adjustTrack = Parallel()
        if len(self.pendingSuits) > 0 or self.suitGone == 1:
            self.suitGone = 0
            numSuits = len(self.pendingSuits) + len(self.activeSuits) - 1
            index = 0
            for suit in self.activeSuits:
                point = self.suitPoints[numSuits][index]
                pos = suit.getPos(self)
                destPos = point[0]
                if self.isSuitLured(suit) == 1:
                    destPos = Point3(destPos[0], destPos[1] - MovieUtil.SUIT_LURE_DISTANCE, destPos[2])
                if pos != destPos:
                    destHpr = VBase3(point[1], 0.0, 0.0)
                    adjustTrack.append(self.createAdjustInterval(suit, destPos, destHpr))
                index += 1

            for suit in self.pendingSuits:
                point = self.suitPoints[numSuits][index]
                destPos = point[0]
                destHpr = VBase3(point[1], 0.0, 0.0)
                adjustTrack.append(self.createAdjustInterval(suit, destPos, destHpr))
                index += 1

        if len(self.pendingToons) > 0 or self.toonGone == 1:
            self.toonGone = 0
            numToons = len(self.pendingToons) + len(self.activeToons) - 1
            index = 0
            for toon in self.activeToons:
                point = self.toonPoints[numToons][index]
                pos = toon.getPos(self)
                destPos = point[0]
                if pos != destPos:
                    destHpr = VBase3(point[1], 0.0, 0.0)
                    adjustTrack.append(self.createAdjustInterval(toon, destPos, destHpr))
                index += 1

            for toon in self.pendingToons:
                point = self.toonPoints[numToons][index]
                destPos = point[0]
                destHpr = VBase3(point[1], 0.0, 0.0)
                adjustTrack.append(self.createAdjustInterval(toon, destPos, destHpr))
                index += 1

        if len(adjustTrack) > 0:
            self.notify.debug('creating adjust multitrack')
            e = Func(self.__handleAdjustDone)
            track = Sequence(adjustTrack, e, name=self.adjustName)
            self.storeInterval(track, self.adjustName)
            track.start(ts)
            if ToontownBattleGlobals.SkipMovie:
                track.finish()
        else:
            self.notify.warning('adjust() - nobody needed adjusting')
            self.__adjustDone()

    def __handleAdjustDone(self):
        self.notify.debug('__handleAdjustDone() - client adjust finished')
        self.clearInterval(self.adjustName)
        self.__adjustDone()

    def __stopAdjusting(self):
        self.notify.debug('__stopAdjusting()')
        self.clearInterval(self.adjustName)
        if self.adjustFsm.getCurrentState().getName() == 'Adjusting':
            self.adjustFsm.request('NotAdjusting')

    def __requestAdjustTownBattle(self):
        self.notify.debug('__requestAdjustTownBattle() curstate = %s' % self.fsm.getCurrentState().getName())
        if self.fsm.getCurrentState().getName() == 'WaitForInput':
            self.__adjustTownBattle()
        else:
            self.needAdjustTownBattle = 1

    def __adjustTownBattle(self):
        self.notify.debug('__adjustTownBattle()')
        if self.localToonActive() and len(self.activeSuits) > 0:
            self.notify.debug('__adjustTownBattle() - adjusting town battle')
            luredSuits = []
            for suit in self.luredSuits:
                if suit not in self.activeSuits:
                    self.notify.error('lured suit not in self.activeSuits')
                luredSuits.append(self.activeSuits.index(suit))

            trappedSuits = []
            for suit in self.activeSuits:
                if suit.battleTrap != NO_TRAP:
                    trappedSuits.append(self.activeSuits.index(suit))

            self.townBattle.adjustCogsAndToons(self.activeSuits, luredSuits, trappedSuits, self.activeToons)
            if hasattr(self, 'townBattleAttacks'):
                self.townBattle.updateChosenAttacks(self.townBattleAttacks[0], self.townBattleAttacks[1], self.townBattleAttacks[2], self.townBattleAttacks[3])
        self.needAdjustTownBattle = 0

    def __adjustDone(self):
        self.notify.debug('__adjustDone()')
        if self.hasLocalToon():
            self.d_adjustDone(base.localAvatar.doId)
        self.adjustFsm.request('NotAdjusting')

    def enterAdjusting(self, ts):
        self.notify.debug('enterAdjusting()')
        if self.localToonActive():
            self.__stopTimer()
        self.delayDeleteMembers()
        self.__adjust(ts, self.__handleAdjustDone)
        return None

    def exitAdjusting(self):
        self.notify.debug('exitAdjusting()')
        self.finishInterval(self.adjustName)
        self._removeMembersKeep()
        currStateName = self.fsm.getCurrentState().getName()
        if currStateName == 'WaitForInput' and self.localToonActive():
            self.startTimer()
        return None

    def enterNotAdjusting(self):
        self.notify.debug('enterNotAdjusting()')
        return None

    def exitNotAdjusting(self):
        return None

    def visualize(self):
        try:
            self.isVisualized
        except:
            self.isVisualized = 0

        if self.isVisualized:
            self.vis.removeNode()
            del self.vis
            self.detachNode()
            self.isVisualized = 0
        else:
            lsegs = LineSegs()
            lsegs.setColor(0.5, 0.5, 1, 1)
            lsegs.moveTo(0, 0, 0)
            for p in BattleBase.allPoints:
                lsegs.drawTo(p[0], p[1], p[2])

            p = BattleBase.allPoints[0]
            lsegs.drawTo(p[0], p[1], p[2])
            self.vis = self.attachNewNode(lsegs.create())
            self.reparentTo(render)
            self.isVisualized = 1

    def setupCollisions(self, name):
        self.lockout = CollisionTube(0, 0, 0, 0, 0, 9, 9)
        lockoutNode = CollisionNode(name)
        lockoutNode.addSolid(self.lockout)
        lockoutNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.lockoutNodePath = self.attachNewNode(lockoutNode)
        self.lockoutNodePath.detachNode()

    def removeCollisionData(self):
        del self.lockout
        self.lockoutNodePath.removeNode()
        del self.lockoutNodePath

    def enableCollision(self):
        self.lockoutNodePath.reparentTo(self)
        if len(self.toons) < 4:
            self.accept(self.getCollisionName(), self.__handleLocalToonCollision)

    def __handleLocalToonCollision(self, collEntry):
        self.notify.debug('localToonCollision')
        if self.fsm.getCurrentState().getName() == 'Off':
            self.notify.debug('ignoring collision in Off state')
            return
        if not base.localAvatar.wantBattles:
            return
        if self._skippingRewardMovie:
            return
        base.cr.playGame.getPlace().setState('WaitForBattle')
        toon = base.localAvatar
        self.d_toonRequestJoin(toon.doId, toon.getPos(self))
        base.localAvatar.preBattleHpr = base.localAvatar.getHpr(render)
        self.localToonFsm.request('WaitForServer')
        self.onWaitingForJoin()

    def onWaitingForJoin(self):
        pass

    def denyLocalToonJoin(self):
        self.notify.debug('denyLocalToonJoin()')
        place = self.cr.playGame.getPlace()
        if place.fsm.getCurrentState().getName() == 'WaitForBattle':
            place.setState('walk')
        self.localToonFsm.request('NoLocalToon')

    def disableCollision(self):
        self.ignore(self.getCollisionName())
        self.lockoutNodePath.detachNode()

    def openBattleCollision(self):
        if not self.hasLocalToon():
            self.enableCollision()

    def closeBattleCollision(self):
        self.ignore(self.getCollisionName())

    def getCollisionName(self):
        return 'enter' + self.lockoutNodePath.getName()


text:
Sentry is a simple, easy to use authorization and authentication package built for FuelPHP.
It also provides additional features such as user groups and additional security features.

###Features

* Authentication (via username or email)
* Authorization
* Groups / Roles
* Remember Me
* User Suspension / Login Attempt Limiter
* Password Reset
* User Activation
* User Metadata

###Docs

[http://sentry.cartalyst.com/manual/v1.1.html](http://sentry.cartalyst.com/manual/v1.1.html)

redpajama_set_name: RedPajamaGithub


text:
package com.github.rosmith.nlp.service.parser;

import java.util.ArrayList;
import java.util.List;

import com.github.rosmith.nlp.query.PrefixStatement;
import com.github.rosmith.nlp.query.QueryStatement;
import com.github.rosmith.nlp.query.SparQLQuery;
import com.github.rosmith.nlp.query.Variable;
import com.github.rosmith.nlp.query.filter.QueryFilter;
import com.github.rosmith.nlp.service.expression.BinaryExpression;
import com.github.rosmith.nlp.service.expression.BracketExpression;
import com.github.rosmith.nlp.service.expression.Expression;
import com.github.rosmith.nlp.service.expression.Literal;
import com.github.rosmith.nlp.service.expression.Operator;
import com.github.rosmith.nlp.service.expression.QuotedLiteral;
import com.github.rosmith.nlp.service.expression.RegexExpression;
import com.github.rosmith.nlp.service.parser.Symbol.Id;

import static com.github.rosmith.nlp.service.parser.Symbol.Id.*;
import static com.github.rosmith.nlp.service.util.ServiceUtil.*;

public class Syntax extends Lexer {

	private static final List<String> regexOptions = new ArrayList<String>();

	static {
		regexOptions.add("s");
		regexOptions.add("m");
		regexOptions.add("i");
		regexOptions.add("x");
	}

	private SparQLQuery query;

	private Syntax(String code, boolean printSymbols) throws Exception {
		super(code, printSymbols);

		query = new SparQLQuery();
		query.addPrefixStatement(new PrefixStatement(NAMESPACE_VARIABLE(),
				NAMESPACE()));
		query.addPrefixStatement(new PrefixStatement(XSD_NAMESPACE_VARIABLE(),
				XSD_NAMESPACE()));
		nextSymbol();
	}

	public static SparQLQuery parse(String code, boolean printSymbols)
			throws Exception {
		String toParse = code.trim().replace("\n", " ");
		Syntax syn = new Syntax(toParse, printSymbols);
		syn.select();
		return syn.query;
	}

	private void select() throws Exception {
		expectSymbol(SELECT);
		if (getSymbol().getId() == DISTINCT) {
			query.setDistinct(true);
			nextSymbol();
		}
		variables();
	}

	private void variables() throws Exception {
		while (getSymbol().getId() == INTERROGATION) {
			nextSymbol();
			if (getSymbol().getId() != IDENT) {
				throw new Exception(
						"An identifier should always follow an interrogation.");
			}
			query.addVariable(new Variable(getSymbol().getIdent()));
			nextSymbol();
		}
		where();
	}

	private void where() throws Exception {
		expectSymbol(WHERE);
		body();
	}

	private void body() throws Exception {
		boolean bracketsArePresent = false;
		if (getSymbol().getId() == LBRACK) {
			bracketsArePresent = true;
			nextSymbol();
		}

		while (statement())
			;

		if (bracketsArePresent) {
			expectSymbol(RBRACK);
		} else {
			nextSymbol();
		}
	}

	private QueryFilter filter() throws Exception {
		return disjunction().toQuery();
	}

	private Expression disjunction() throws Exception {
		Expression result = conjunction();
		while (getSymbol().getId() == Id.OR) {
			nextSymbol();
			result = new BinaryExpression(result, Operator.OR, conjunction());
		}
		return result;
	}

	private Expression conjunction() throws Exception {
		Expression result = relation();
		while (getSymbol().getId() == Id.AND) {
			nextSymbol();
			result = new BinaryExpression(result, Operator.AND, relation());
		}
		return result;
	}

	private Expression relation() throws Exception {
		Expression result = expression();
		if (getSymbol().getId() == Id.EQ) {
			if (!(result instanceof Literal)) {
				throw new RuntimeException(
						"The left hand side of an equal sign should be a literal");
			}
			nextSymbol();
			boolean isVar = getSymbol().getId() == INTERROGATION;
			if (isVar) {
				nextSymbol();
			}
			Expression r = expression();
			if (!(r instanceof Literal)) {
				throw new RuntimeException(
						"The right hand side of an equal sign should be a literal");
			}
			Literal lit = (Literal) r;
			lit.setVar(isVar);
			result = new BinaryExpression(result, Operator.EQUALS, r);
		}
		return result;
	}

	private Expression expression() throws Exception {

		if (getSymbol().getId() == LPAREN) {
			nextSymbol();
			Expression result = new BracketExpression(disjunction());
			expectSymbol(RPAREN);
			return result;
		}

		if (getSymbol().getId() == Id.REGEX) {
			nextSymbol();
			return regex();
		}

		if (getSymbol().getId() == Id.INTERROGATION) {
			nextSymbol();
		}

		return literal();
	}

	private Expression regex() throws Exception {
		expectSymbol(LPAREN);
		expectSymbol(INTERROGATION);
		Literal l = (Literal) literal();
		expectSymbol(COMMA);
		expectQuoteSymbol(QUOTE);
		String regex = getSymbol().getIdent();
		if (regex != null && !regex.equals("QUOTE")) {
			nextSymbol();
			expectSymbol(QUOTE);
		} else {
			regex = "";
			nextSymbol();
		}
		Expression result = null;
		if (getSymbol().getId() == COMMA) {
			nextSymbol();
			expectQuoteSymbol(QUOTE);
			String flag = getSymbol().getIdent();
			if (flag == null || flag.isEmpty() || !regexOptions.contains(flag)) {
				throw new RuntimeException(
						"Please use the flags \"s, m, i, x\" as option for the regex function.");
			}
			nextSymbol();
			expectSymbol(QUOTE);
			result = new RegexExpression(l, regex, flag);
		} else {
			result = new RegexExpression(l, regex);
		}
		expectSymbol(RPAREN);
		return result;
	}

	private Expression literal() throws Exception {
		String value = null;
		Id id = getSymbol().getId();
		if (id == NUMBER) {
			value = String.valueOf(getSymbol().getNumber());
		}
		if (id == IDENT) {
			value = getSymbol().getIdent();
		}
		if (id == QUOTE) {
			nextQuoteSymbol();
			value = getSymbol().getIdent();
			if (value != null && !value.equals("QUOTE")) {
				nextSymbol();
				expectSymbol(QUOTE);
			} else {
				value = "";
				nextSymbol();
			}
			return new QuotedLiteral(value);
		}
		nextSymbol();
		return new Literal(value);
	}

	private boolean statement() throws Exception {
		if (getSymbol().getId() == FILTER) {
			nextSymbol();
			query.addFilter(filter());
			return true;
		}

		String subject = null, relation = null, object = null;
		boolean isVar = false;
		if (getSymbol().getId() != INTERROGATION) {
			return false;
		}
		nextSymbol();
		subject = getSymbol().getIdent();

		nextSymbol();
		relation = getSymbol().getIdent();

		nextSymbol();

		if (getSymbol().getId() == QUOTE) {
			nextSymbol();
			isVar = false;
			object = getSymbol().getIdent();
			nextSymbol();
			expectSymbol(QUOTE);
		} else if (getSymbol().getId() == INTERROGATION) {
			nextSymbol();
			isVar = true;
			object = getSymbol().getIdent();
			nextSymbol();
		}

		expectSymbol(PERIOD);

		query.addStatement(new QueryStatement(subject, namespace(relation),
				object, isVar));

		return true;
	}

	void expectSymbol(Symbol.Id id) throws Exception {
		if (id != getSymbol().getId()) {
			unexpectedSymbol();
		}
		nextSymbol();
	}

	void expectQuoteSymbol(Symbol.Id id) throws Exception {
		if (id != getSymbol().getId()) {
			unexpectedSymbol();
		}
		nextQuoteSymbol();
	}

	private void unexpectedSymbol() throws Exception {
		throw new Exception("unexpected symbol "
				+ getSymbol().getId().toString());
	}

}


text:
package io.reactivex.subjects;

import static org.junit.Assert.*;

import java.util.*;

import org.junit.Test;

import io.reactivex.Observable;
import io.reactivex.TestHelper;
import io.reactivex.disposables.*;
import io.reactivex.exceptions.TestException;
import io.reactivex.observers.TestObserver;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;

public class SerializedSubjectTest {

    @Test
    public void testBasic() {
        SerializedSubject<String> subject = new SerializedSubject<String>(PublishSubject.<String> create());
        TestObserver<String> ts = new TestObserver<String>();
        subject.subscribe(ts);
        subject.onNext("hello");
        subject.onComplete();
        ts.awaitTerminalEvent();
        ts.assertValue("hello");
    }

    @Test
    public void testAsyncSubjectValueRelay() {
        AsyncSubject<Integer> async = AsyncSubject.create();
        async.onNext(1);
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertEquals((Integer)1, async.getValue());
        assertTrue(async.hasValue());
        assertArrayEquals(new Object[] { 1 }, async.getValues());
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { 1, null }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testAsyncSubjectValueEmpty() {
        AsyncSubject<Integer> async = AsyncSubject.create();
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testAsyncSubjectValueError() {
        AsyncSubject<Integer> async = AsyncSubject.create();
        TestException te = new TestException();
        async.onError(te);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertTrue(serial.hasThrowable());
        assertSame(te, serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testPublishSubjectValueRelay() {
        PublishSubject<Integer> async = PublishSubject.create();
        async.onNext(1);
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
    }

    @Test
    public void testPublishSubjectValueEmpty() {
        PublishSubject<Integer> async = PublishSubject.create();
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
    }
    @Test
    public void testPublishSubjectValueError() {
        PublishSubject<Integer> async = PublishSubject.create();
        TestException te = new TestException();
        async.onError(te);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertTrue(serial.hasThrowable());
        assertSame(te, serial.getThrowable());
    }

    @Test
    public void testBehaviorSubjectValueRelay() {
        BehaviorSubject<Integer> async = BehaviorSubject.create();
        async.onNext(1);
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testBehaviorSubjectValueRelayIncomplete() {
        BehaviorSubject<Integer> async = BehaviorSubject.create();
        async.onNext(1);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertEquals((Integer)1, async.getValue());
        assertTrue(async.hasValue());
        assertArrayEquals(new Object[] { 1 }, async.getValues());
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { 1, null }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testBehaviorSubjectIncompleteEmpty() {
        BehaviorSubject<Integer> async = BehaviorSubject.create();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testBehaviorSubjectEmpty() {
        BehaviorSubject<Integer> async = BehaviorSubject.create();
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testBehaviorSubjectError() {
        BehaviorSubject<Integer> async = BehaviorSubject.create();
        TestException te = new TestException();
        async.onError(te);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertTrue(serial.hasThrowable());
        assertSame(te, serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }

    @Test
    public void testReplaySubjectValueRelay() {
        ReplaySubject<Integer> async = ReplaySubject.create();
        async.onNext(1);
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertEquals((Integer)1, async.getValue());
        assertTrue(async.hasValue());
        assertArrayEquals(new Object[] { 1 }, async.getValues());
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { 1, null }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectValueRelayIncomplete() {
        ReplaySubject<Integer> async = ReplaySubject.create();
        async.onNext(1);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertEquals((Integer)1, async.getValue());
        assertTrue(async.hasValue());
        assertArrayEquals(new Object[] { 1 }, async.getValues());
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { 1, null }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectValueRelayBounded() {
        ReplaySubject<Integer> async = ReplaySubject.createWithSize(1);
        async.onNext(0);
        async.onNext(1);
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertEquals((Integer)1, async.getValue());
        assertTrue(async.hasValue());
        assertArrayEquals(new Object[] { 1 }, async.getValues());
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { 1, null }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectValueRelayBoundedIncomplete() {
        ReplaySubject<Integer> async = ReplaySubject.createWithSize(1);
        async.onNext(0);
        async.onNext(1);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertEquals((Integer)1, async.getValue());
        assertTrue(async.hasValue());
        assertArrayEquals(new Object[] { 1 }, async.getValues());
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { 1 }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { 1, null }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectValueRelayBoundedEmptyIncomplete() {
        ReplaySubject<Integer> async = ReplaySubject.createWithSize(1);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectValueRelayEmptyIncomplete() {
        ReplaySubject<Integer> async = ReplaySubject.create();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }

    @Test
    public void testReplaySubjectEmpty() {
        ReplaySubject<Integer> async = ReplaySubject.create();
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectError() {
        ReplaySubject<Integer> async = ReplaySubject.create();
        TestException te = new TestException();
        async.onError(te);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertTrue(serial.hasThrowable());
        assertSame(te, serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }

    @Test
    public void testReplaySubjectBoundedEmpty() {
        ReplaySubject<Integer> async = ReplaySubject.createWithSize(1);
        async.onComplete();
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertTrue(serial.hasComplete());
        assertFalse(serial.hasThrowable());
        assertNull(serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }
    @Test
    public void testReplaySubjectBoundedError() {
        ReplaySubject<Integer> async = ReplaySubject.createWithSize(1);
        TestException te = new TestException();
        async.onError(te);
        Subject<Integer> serial = async.toSerialized();

        assertFalse(serial.hasObservers());
        assertFalse(serial.hasComplete());
        assertTrue(serial.hasThrowable());
        assertSame(te, serial.getThrowable());
        assertNull(async.getValue());
        assertFalse(async.hasValue());
        assertArrayEquals(new Object[] { }, async.getValues());
        assertArrayEquals(new Integer[] { }, async.getValues(new Integer[0]));
        assertArrayEquals(new Integer[] { null }, async.getValues(new Integer[] { 0 }));
        assertArrayEquals(new Integer[] { null, 0 }, async.getValues(new Integer[] { 0, 0 }));
    }

    @Test
    public void testDontWrapSerializedSubjectAgain() {
        PublishSubject<Object> s = PublishSubject.create();
        Subject<Object> s1 = s.toSerialized();
        Subject<Object> s2 = s1.toSerialized();
        assertSame(s1, s2);
    }

    @Test
    public void normal() {
        Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

        TestObserver<Integer> ts = s.test();

        Observable.range(1, 10).subscribe(s);

        ts.assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        assertFalse(s.hasObservers());

        s.onNext(11);

        List<Throwable> errors = TestHelper.trackPluginErrors();
        try {
            s.onError(new TestException());

            TestHelper.assertUndeliverable(errors, 0, TestException.class);
        } finally {
            RxJavaPlugins.reset();
        }
        s.onComplete();

        Disposable bs = Disposables.empty();
        s.onSubscribe(bs);
        assertTrue(bs.isDisposed());
    }

    @Test
    public void onNextOnNextRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onNext(1);
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onNext(2);
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertSubscribed().assertNoErrors().assertNotComplete()
            .assertValueSet(Arrays.asList(1, 2));
        }
    }

    @Test
    public void onNextOnErrorRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            final TestException ex = new TestException();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onNext(1);
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onError(ex);
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertError(ex).assertNotComplete();

            if (ts.valueCount() != 0) {
                ts.assertValue(1);
            }
        }
    }

    @Test
    public void onNextOnCompleteRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onNext(1);
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onComplete();
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertComplete().assertNoErrors();

            if (ts.valueCount() != 0) {
                ts.assertValue(1);
            }
        }
    }

    @Test
    public void onNextOnSubscribeRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            final Disposable bs = Disposables.empty();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onNext(1);
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onSubscribe(bs);
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertValue(1).assertNotComplete().assertNoErrors();
        }
    }

    @Test
    public void onCompleteOnSubscribeRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            final Disposable bs = Disposables.empty();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onComplete();
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onSubscribe(bs);
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertResult();
        }
    }

    @Test
    public void onCompleteOnCompleteRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onComplete();
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onComplete();
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertResult();
        }
    }

    @Test
    public void onErrorOnErrorRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            final TestException ex = new TestException();

            List<Throwable> errors = TestHelper.trackPluginErrors();
            try {
                Runnable r1 = new Runnable() {
                    @Override
                    public void run() {
                        s.onError(ex);
                    }
                };

                Runnable r2 = new Runnable() {
                    @Override
                    public void run() {
                        s.onError(ex);
                    }
                };

                TestHelper.race(r1, r2, Schedulers.single());

                ts.assertFailure(TestException.class);

                TestHelper.assertUndeliverable(errors, 0, TestException.class);
            } finally {
                RxJavaPlugins.reset();
            }
        }
    }

    @Test
    public void onSubscribeOnSubscribeRace() {
        for (int i = 0; i < 500; i++) {
            final Subject<Integer> s = PublishSubject.<Integer>create().toSerialized();

            TestObserver<Integer> ts = s.test();

            final Disposable bs1 = Disposables.empty();
            final Disposable bs2 = Disposables.empty();

            Runnable r1 = new Runnable() {
                @Override
                public void run() {
                    s.onSubscribe(bs1);
                }
            };

            Runnable r2 = new Runnable() {
                @Override
                public void run() {
                    s.onSubscribe(bs2);
                }
            };

            TestHelper.race(r1, r2, Schedulers.single());

            ts.assertEmpty();
        }
    }
}


text:
cd $GOPATH/src/github.com/jimmy-go/mdb
go test -cover -coverprofile=coverage.out

if [ "$1" == "html" ]; then
    go tool cover -html=coverage.out
fi


text:
import base64
from copy import deepcopy
from django.test import TestCase
from corehq.dbaccessors.couchapps.all_docs import delete_all_docs_by_doc_type
from corehq.doctypemigrations.bulk_migrate import bulk_migrate
from corehq.doctypemigrations.changes import CouchChange
from corehq.doctypemigrations.continuous_migrate import filter_doc_ids_by_doc_type, \
    copy_docs, delete_docs, ContinuousReplicator
from corehq.doctypemigrations.migrator import Migrator
from corehq.doctypemigrations.stats import get_doc_counts_per_doc_type
from dimagi.utils.couch.bulk import get_docs
from django.conf import settings

class TestDocTypeMigrations(TestCase):

    def setUp(self):
        super(TestDocTypeMigrations, self).setUp()
        self.migration = Migrator(
            slug='user_db_migration',
            source_db_name=None,
            target_db_name=settings.NEW_USERS_GROUPS_DB,
            doc_types=(
                'Group',
                'CommCareUser',
            )
        )
        delete_all_docs_by_doc_type(self.migration.source_db, self.migration.doc_types)
        self.migration.target_db.delete_docs(_get_non_design_docs(self.migration.target_db))

        self.docs = [
            {'doc_type': 'CommCareUser', 'username': 'johnny@example.com'},
            {'doc_type': 'CommCareUser', 'username': 'fatima@example.com',
             '_attachments': {
                 "greeting.txt": {
                     "content_type": "text/plain", "data": base64.b64encode("hi"),
                     "digest": "md5-QTVOnBwGnrw6Tx9YG1ZRyA==", "revpos": 1,
                 }
             }},
            {'doc_type': 'Group', 'name': 'User Group'},
            {'doc_type': 'Group-Deleted', 'name': 'Deleted User Group'},
        ]
        results = self.migration.source_db.bulk_save(self.docs)
        for doc, result in zip(self.docs, results):
            doc['_id'] = result['id']
            doc['_rev'] = result['rev']
        _sort_by_doc_id(self.docs)

    def tearDown(self):
        # have to copy because deleted_docs modifies the docs param in place
        docs = deepcopy(self.docs)
        self.migration.source_db.delete_docs(docs)
        self.migration.target_db.delete_docs(
            _get_non_design_docs(self.migration.target_db))
        super(TestDocTypeMigrations, self).tearDown()

    def assert_in_sync(self):
        actual_docs = _get_non_design_docs(self.migration.target_db)
        self.assertEqual(actual_docs, self.docs)

    def assert_no_docs_in_target_db(self):
        actual_docs = _get_non_design_docs(self.migration.target_db)
        self.assertEqual(actual_docs, [])

    def test_bulk_migrate(self):
        bulk_migrate(
            self.migration.source_db, self.migration.target_db,
            self.migration.doc_types)
        self.assert_in_sync()

    def test_phase_1_bulk_migrate(self):
        self.migration.phase_1_bulk_migrate()
        self.assert_in_sync()
        self.assertTrue(self.migration._migration_model.original_seq)

    def test_filter_doc_ids_by_doc_type(self):
        expected_doc_ids = {doc['_id'] for doc in self.docs}
        input_doc_ids = expected_doc_ids ^ {'other', 'random', 'ids'}
        actual_doc_ids = set(filter_doc_ids_by_doc_type(
            self.migration.source_db, input_doc_ids, self.migration.doc_types))
        self.assertEqual(expected_doc_ids, actual_doc_ids)

    def test_copy_docs(self):
        doc_ids = [doc['_id'] for doc in self.docs]
        copy_docs(self.migration.source_db, self.migration.target_db, doc_ids)
        self.assert_in_sync()

    def test_delete_docs(self):
        doc_ids = [doc['_id'] for doc in self.docs]
        delete_docs(self.migration.target_db, doc_ids)
        self.assert_no_docs_in_target_db()

    def test_delete_docs_non_existent(self):
        doc_ids = [doc['_id'] for doc in self.docs] + ['unknown_id']
        delete_docs(self.migration.target_db, doc_ids)
        self.assert_no_docs_in_target_db()

    def test_continuous_replicator(self):
        replicator = ContinuousReplicator(
            self.migration.source_db, self.migration.target_db, self.migration.doc_types)
        for i, doc in enumerate(self.docs):
            change = CouchChange(seq=i, id=doc['_id'], rev=doc['_rev'], deleted=False)
            replicator.replicate_change(change)
        replicator.commit()
        self.assert_in_sync()

        for i, doc in enumerate(self.docs):
            # deleted=true changes contain a theoretical "next rev" of the deleted doc
            # that is completely useless, replicate_change has to work without it
            change = CouchChange(seq=i, id=doc['_id'], rev='go fuck yourself', deleted=True)
            replicator.replicate_change(change)
        replicator.commit()
        self.assert_no_docs_in_target_db()

    def test_get_doc_counts_per_doc_type(self):
        doc_types = self.migration.doc_types
        self.assertEqual(
            get_doc_counts_per_doc_type(self.migration.source_db, doc_types),
            {'CommCareUser': 2, 'CommCareUser-Deleted': 0, 'Group': 1, 'Group-Deleted': 1})
        self.assertEqual(
            get_doc_counts_per_doc_type(self.migration.target_db, doc_types),
            {'CommCareUser': 0, 'CommCareUser-Deleted': 0, 'Group': 0, 'Group-Deleted': 0})
        self.migration.phase_1_bulk_migrate()
        self.assertEqual(
            get_doc_counts_per_doc_type(self.migration.source_db, doc_types),
            {'CommCareUser': 2, 'CommCareUser-Deleted': 0, 'Group': 1, 'Group-Deleted': 1})
        self.assertEqual(
            get_doc_counts_per_doc_type(self.migration.target_db, doc_types),
            {'CommCareUser': 2, 'CommCareUser-Deleted': 0, 'Group': 1, 'Group-Deleted': 1})

def _get_non_design_docs(db):
    docs = get_docs(db, [result['id'] for result in db
                         if not result['id'].startswith('_design/')],
                    attachments=True)
    _sort_by_doc_id(docs)
    return docs

def _sort_by_doc_id(lst):
    lst.sort(key=lambda doc: doc['_id'])