from panda3d.core import Camera, Lens, RopeNode, Texture, VBase3, headsUp
from toontown.toonbase.ToonBaseGlobal import *
from direct.interval.IntervalGlobal import *
from DistributedMinigame import *
from direct.gui.DirectGui import *
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from toontown.toonbase import ToontownTimer
from toontown.toon import ToonHead
from toontown.suit import SuitDNA
from toontown.suit import Suit
import ArrowKeys
import random
from toontown.toonbase import ToontownGlobals
from toontown.toonbase import TTLocalizer
import TugOfWarGameGlobals
from direct.showutil import Rope
from toontown.effects import Splash
from toontown.effects import Ripples
from toontown.toonbase import TTLocalizer
import MinigamePowerMeter
from direct.task.Task import Task

class DistributedTugOfWarGame(DistributedMinigame):
    bgm = 'phase_4/audio/bgm/MG_cannon_game_tug.ogg'
    toonAnimNames = ['neutral',
     'tug-o-war',
     'slip-forward',
     'slip-backward',
     'victory',
     'sad-neutral']
    suitAnimNames = ['neutral',
     'tug-o-war',
     'slip-forward',
     'slip-backward',
     'flail',
     'victory']
    UPDATE_TIMER_TASK = 'TugOfWarGameUpdateTimerTask'
    UPDATE_KEY_PRESS_RATE_TASK = 'TugOfWarGameUpdateKeyPressRateTask'
    UPDATE_ROPE_TASK = 'TugOfWarGameUpdateRopeTask'
    H_TO_L = 0
    L_TO_H = 1

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedTugOfWarGame', [State.State('off', self.enterOff, self.exitOff, ['waitForGoSignal']),
         State.State('waitForGoSignal', self.enterWaitForGoSignal, self.exitWaitForGoSignal, ['tug', 'cleanup']),
         State.State('tug', self.enterTug, self.exitTug, ['gameDone', 'cleanup']),
         State.State('gameDone', self.enterGameDone, self.exitGameDone, ['cleanup']),
         State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        self.gameType = TugOfWarGameGlobals.TOON_VS_TOON
        self.suit = None
        self.suitId = 666
        self.suitType = 'f'
        self.suitLevel = 1
        self.sides = {}
        self.avList = [[], []]
        self.buttons = [0, 1]
        self.mouseMode = 0
        self.mouseSide = 0
        self.fallenList = []
        self.handycap = 2.0
        self.advantage = 1.0
        self.tugRopes = []
        self.ropePts = []
        self.ropeTex = []
        self.rightHandDict = {}
        self.posDict = {}
        self.hprDict = {}
        self.offsetDict = {}
        self.pullingDict = {}
        self.dropShadowDict = {}
        self.arrowKeys = None
        self.keyTTL = []
        self.idealRate = 2.0
        self.idealForce = 0.0
        self.keyRate = 0
        self.allOutMode = 0
        self.rateMatchAward = 0
        self.targetRateList = [[8, 6],
         [5, 7],
         [6, 8],
         [6, 10],
         [7, 11],
         [8, 12]]
        self.nextRateIndex = 0
        self.drinkPositions = []
        for k in xrange(4):
            self.drinkPositions.append(VBase3(-0.2 + 0.2 * k, 16 + 2 * k, 0.0))

        self.rng = RandomNumGen.RandomNumGen(1000)
        self.introTrack = None
        self.showTrack = None
        self.setupTrack = None
        self.animTracks = {}
        self.randomNumGen = None
        return

    def getTitle(self):
        return TTLocalizer.TugOfWarGameTitle

    def getInstructions(self):
        return TTLocalizer.TugOfWarInstructions

    def getMaxDuration(self):
        return TugOfWarGameGlobals.GAME_DURATION

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.hide()
        self.room = loader.loadModel('phase_4/models/minigames/tug_of_war_dock')
        self.room.reparentTo(hidden)
        ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope')
        self.ropeTexture = ropeModel.findTexture('*')
        ropeModel.removeNode()
        self.sky = loader.loadModel('phase_3.5/models/props/TT_sky')
        self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow')
        self.correctSound = loader.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.ogg')
        self.sndHitWater = loader.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg')
        self.whistleSound = loader.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg')
        self.music = loader.loadMusic(self.bgm)
        self.roundText = DirectLabel(text='     ', text_fg=(0, 1, 0, 1), frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0.014, 0, -0.84), scale=0.2)
        self.powerMeter = MinigamePowerMeter.MinigamePowerMeter(17)
        self.powerMeter.reparentTo(aspect2d)
        self.powerMeter.setPos(0, 0, 0.4)
        self.powerMeter.setPower(8)
        self.powerMeter.setTarget(8)
        self.arrows = [None] * 2
        for x in xrange(len(self.arrows)):
            self.arrows[x] = loader.loadModel('phase_3/models/props/arrow')
            self.arrows[x].reparentTo(self.powerMeter)
            self.arrows[x].hide()
            self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2)
            self.arrows[x].setPos(0.12 - 0.24 * x, 0, -0.26)
            self.disableArrow(self.arrows[x])

        self.splash = Splash.Splash(render)
        self.suitSplash = Splash.Splash(render)
        self.ripples = Ripples.Ripples(render)
        self.suitRipples = Ripples.Ripples(render)
        return

    def toggleMouseMode(self, param):
        self.mouseMode = not self.mouseMode
        if self.mouseMode:
            mpos = param.getMouse()
            if mpos[0] < 0:
                self.hilightArrow(self.arrows[1])
            else:
                self.hilightArrow(self.arrows[0])
            self.__spawnMouseSpeedTask()
        else:
            self.__releaseHandler(0)
            self.__releaseHandler(1)
            self.__killMouseSpeedTask()

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        self.timer.destroy()
        del self.timer
        self.room.removeNode()
        del self.room
        self.sky.removeNode()
        del self.sky
        del self.dropShadowDict
        self.dropShadow.removeNode()
        del self.dropShadow
        del self.correctSound
        del self.sndHitWater
        del self.whistleSound
        del self.music
        self.roundText.destroy()
        del self.roundText
        if self.powerMeter:
            self.powerMeter.destroy()
            del self.powerMeter
        for x in self.arrows:
            if x:
                x.removeNode()
                del x

        del self.arrows
        self.splash.destroy()
        del self.splash
        self.suitSplash.destroy()
        del self.suitSplash
        if self.ripples != None:
            self.ripples.stop()
            self.ripples.detachNode()
            del self.ripples
        if self.suitRipples != None:
            self.suitRipples.stop()
            self.suitRipples.detachNode()
            del self.suitRipples
        for x in self.avList:
            del x

        del self.avList
        for x in self.tugRopes:
            if x != None:
                x.detachNode()
            del x

        del self.tugRopes
        for x in self.ropePts:
            if x:
                for t in x:
                    del t

                del x

        del self.ropePts
        for x in self.ropeTex:
            if x:
                for t in x:
                    t.destroy()
                    del t

                del x

        del self.ropeTex
        del self.posDict
        del self.hprDict
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM
        if self.suit:
            self.suit.delete()
            del self.suit
        del self.sides
        del self.buttons
        del self.pullingDict
        del self.rightHandDict
        for x in self.drinkPositions:
            del x

        del self.drinkPositions
        del self.offsetDict
        del self.keyTTL
        del self.rng
        return

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.onstage(self)
        self.arrowKeys = ArrowKeys.ArrowKeys()
        self.room.reparentTo(render)
        self.room.setPosHpr(0.0, 18.39, -ToontownGlobals.FloorOffset, 0.0, 0.0, 0.0)
        self.room.setScale(0.4)
        self.sky.setZ(-5)
        self.sky.reparentTo(render)
        self.dropShadow.setColor(0, 0, 0, 0.5)
        camera.reparentTo(render)
        camera.setPosHpr(-11.4427, 9.03559, 2.80094, -49.104, -0.89, 0)
        self.dropShadow.setBin('fixed', 0, 1)
        self.splash.reparentTo(render)
        self.suitSplash.reparentTo(render)
        base.playMusic(self.music, looping=1, volume=1)
        for x in xrange(len(self.arrows)):
            self.arrows[x].show()

        for avId in self.avIdList:
            self.pullingDict[avId] = 0

    def offstage(self):
        self.notify.debug('offstage')
        DistributedMinigame.offstage(self)
        self.music.stop()
        if self.introTrack:
            self.introTrack.finish()
            del self.introTrack
            self.introTrack = None
        for track in self.animTracks.values():
            if track:
                track.finish()
                del track
            self.animTracks = {}

        if self.showTrack:
            self.showTrack.finish()
            del self.showTrack
            self.showTrack = None
        if self.setupTrack:
            self.setupTrack.finish()
            del self.setupTrack
            self.setupTrack = None
        base.camLens.setMinFov(settings['fov'] / (4.0 / 3.0))
        base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar)
        if self.arrowKeys:
            self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
            self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)
            self.arrowKeys.destroy()
            del self.arrowKeys
            self.arrowKeys = None
        self.room.reparentTo(hidden)
        self.sky.reparentTo(hidden)
        self.splash.reparentTo(hidden)
        self.splash.stop()
        self.suitSplash.reparentTo(hidden)
        self.suitSplash.stop()
        self.ripples.reparentTo(hidden)
        self.ripples.stop()
        self.hideControls()
        self.roundText.hide()
        for avId in self.avIdList:
            av = self.getAvatar(avId)
            if av:
                av.loop('neutral')
                av.dropShadow.show()

        for x in self.tugRopes:
            if x != None:
                x.reparentTo(hidden)

        if self.suit:
            self.suit.reparentTo(hidden)
        for avId in self.avIdList:
            if avId in self.dropShadowDict:
                self.dropShadowDict[avId].reparentTo(hidden)

        if self.suitId in self.dropShadowDict:
            self.dropShadowDict[self.suitId].reparentTo(hidden)
        return

    def initCamera(self):
        birdseyePosHpr = [1.95461,
         18.4891,
         38.4646,
         1.18185,
         -87.5308,
         0]
        introPosHpr = [None] * 2
        introPosHpr[0] = [VBase3(-11.4427, 9.03559, 2.80094), VBase3(-49.104, -0.732374, 0)]
        introPosHpr[1] = [VBase3(16.9291, 13.9302, 2.64282), VBase3(66.9685, -6.195, 0)]
        gameCamHpr = VBase3(-1.13, 1.042, 0)
        gameCamPos = VBase3(0, 1.0838, 2.745)
        camera.reparentTo(render)
        camera.setPosHpr(introPosHpr[self.sides[self.localAvId]][0], introPosHpr[self.sides[self.localAvId]][1])
        lerpDur = 8
        self.introTrack = LerpPosHprInterval(camera, lerpDur, pos=gameCamPos, hpr=gameCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos'))
        self.introTrack.start()
        base.camLens.setMinFov((60 + 2 * self.numPlayers) / (4.0 / 3.0))
        base.camLens.setFar(450.0)
        return

    def sendGameType(self, index, suit):
        if not self.hasLocalToon:
            return
        self.gameType = index
        self.suitLevel = suit
        if suit == 1:
            self.suitType = 'pp'
        elif suit == 2:
            self.suitType = 'dt'
        elif suit == 3:
            self.suitType = 'gh'
        elif suit == 4:
            self.suitType = 'cr'

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        else:
            self.notify.debug('setGameReady')
            if DistributedMinigame.setGameReady(self):
                return
            self.initToons()
            self.createSuits()
            self.calculatePositions()
            self.initHandycaps()
            self.initRopes()
            self.initCamera()
            self.animTracks = {}
            for avId in self.avIdList:
                self.animTracks[avId] = None

            self.animTracks[self.suitId] = None
            self.showTrack = None
            self.setupTrack = None
            self.__initGameVars()
            return

    def hideControls(self):
        for x in xrange(len(self.arrows)):
            self.arrows[x].hide()

        for rope in self.tugRopes:
            if rope != None:
                rope.reparentTo(hidden)

        for tex in self.ropeTex:
            if tex != None:
                for texi in tex:
                    if texi:
                        texi.reparentTo(hidden)

        if self.powerMeter != None:
            self.powerMeter.unbind(DGG.B1PRESS)
            self.powerMeter.unbind(DGG.B1RELEASE)
            self.powerMeter.hide()
        return

    def setUpRopes(self, notTaut):
        if self.numPlayers == 1:
            suitRightHand = self.suit.getRightHand()
            toonRightHand = self.rightHandDict[self.avIdList[0]]
            if notTaut:
                self.tugRopes[0].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
            else:
                midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0
                self.tugRopes[0].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
            self.tugRopes[0].reparentTo(render)
        elif self.numPlayers == 2:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                suitRightHand = self.suit.getRightHand()
                toonRightHand = self.rightHandDict[self.avIdList[1]]
                if notTaut:
                    self.tugRopes[1].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                else:
                    midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0
                    self.tugRopes[1].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                self.tugRopes[0].reparentTo(render)
                self.tugRopes[1].reparentTo(render)
            else:
                if notTaut:
                    self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                else:
                    self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                self.tugRopes[0].reparentTo(render)
        elif self.numPlayers == 3:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                suitRightHand = self.suit.getRightHand()
                toonRightHand = self.rightHandDict[self.avIdList[2]]
                if notTaut:
                    self.tugRopes[2].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                else:
                    midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0
                    self.tugRopes[2].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                self.tugRopes[0].reparentTo(render)
                self.tugRopes[1].reparentTo(render)
                self.tugRopes[2].reparentTo(render)
            else:
                if notTaut:
                    self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                else:
                    self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                self.tugRopes[0].reparentTo(render)
                self.tugRopes[1].reparentTo(render)
        elif self.numPlayers == 4:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.tugRopes[2].setup(3, ((self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[3]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                suitRightHand = self.suit.getRightHand()
                toonRightHand = self.rightHandDict[self.avIdList[3]]
                if notTaut:
                    self.tugRopes[3].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                else:
                    midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0
                    self.tugRopes[3].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                self.tugRopes[0].reparentTo(render)
                self.tugRopes[1].reparentTo(render)
                self.tugRopes[2].reparentTo(render)
                self.tugRopes[3].reparentTo(render)
            else:
                self.tugRopes[2].setup(3, ((self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[3]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0,
                 0,
                 0,
                 1,
                 1,
                 1])
                if notTaut:
                    self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                else:
                    self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0,
                     0,
                     0,
                     1,
                     1,
                     1])
                self.tugRopes[0].reparentTo(render)
                self.tugRopes[1].reparentTo(render)
                self.tugRopes[2].reparentTo(render)

    def initToons(self):
        for avId in self.avIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                toon.startBlink()
                toon.startLookAround()
                for anim in self.toonAnimNames:
                    toon.pose(anim, 0)

                toon.pose('tug-o-war', 3)
                self.rightHandDict[avId] = toon.getRightHand()
                toon.loop('neutral')
                toon.dropShadow.hide()
                self.dropShadowDict[avId] = self.dropShadow.copyTo(hidden)
                self.dropShadowDict[avId].reparentTo(toon)
                self.dropShadowDict[avId].setScale(0.35)

    def calculatePositions(self):
        hprPositions = [VBase3(240, 0, 0), VBase3(120, 0, 0)]
        dockPositions = []
        for k in xrange(5):
            dockPositions.append(VBase3(-9.0 + 1.5 * k, 18, 0.1))

        for k in xrange(5):
            dockPositions.append(VBase3(3 + 1.5 * k, 18, 0.1))

        self.sendUpdate('sendNewAvIdList', [self.avIdList])
        if self.numPlayers == 1:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.posDict[self.suitId] = dockPositions[7]
                self.posDict[self.avIdList[0]] = dockPositions[2]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
            else:
                self.notify.warning("can't play toon vs. toon with one player")
        elif self.numPlayers == 2:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1)
                self.posDict[self.suitId] = dockPositions[7]
                self.posDict[self.avIdList[0]] = dockPositions[1]
                self.posDict[self.avIdList[1]] = dockPositions[2]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
                self.hprDict[self.avIdList[1]] = hprPositions[0]
            else:
                self.randomNumGen.shuffle(self.avIdList)
                self.posDict[self.avIdList[0]] = dockPositions[2]
                self.posDict[self.avIdList[1]] = dockPositions[7]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
                self.hprDict[self.avIdList[1]] = hprPositions[1]
        elif self.numPlayers == 3:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 2)
                self.posDict[self.suitId] = dockPositions[7]
                self.posDict[self.avIdList[0]] = dockPositions[0]
                self.posDict[self.avIdList[1]] = dockPositions[1]
                self.posDict[self.avIdList[2]] = dockPositions[2]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
                self.hprDict[self.avIdList[1]] = hprPositions[0]
                self.hprDict[self.avIdList[2]] = hprPositions[0]
            else:
                self.randomNumGen.shuffle(self.avIdList)
                self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1)
                self.posDict[self.avIdList[0]] = dockPositions[1]
                self.posDict[self.avIdList[1]] = dockPositions[2]
                self.posDict[self.avIdList[2]] = dockPositions[7]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
                self.hprDict[self.avIdList[1]] = hprPositions[0]
                self.hprDict[self.avIdList[2]] = hprPositions[1]
        elif self.numPlayers == 4:
            if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
                self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 3)
                self.posDict[self.suitId] = dockPositions[6]
                self.posDict[self.avIdList[0]] = dockPositions[0]
                self.posDict[self.avIdList[1]] = dockPositions[1]
                self.posDict[self.avIdList[2]] = dockPositions[2]
                self.posDict[self.avIdList[3]] = dockPositions[3]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
                self.hprDict[self.avIdList[1]] = hprPositions[0]
                self.hprDict[self.avIdList[2]] = hprPositions[0]
                self.hprDict[self.avIdList[3]] = hprPositions[0]
            else:
                self.randomNumGen.shuffle(self.avIdList)
                self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1)
                self.arrangeByHeight(self.avIdList, self.L_TO_H, 2, 3)
                self.posDict[self.avIdList[0]] = dockPositions[1]
                self.posDict[self.avIdList[1]] = dockPositions[2]
                self.posDict[self.avIdList[2]] = dockPositions[7]
                self.posDict[self.avIdList[3]] = dockPositions[8]
                self.hprDict[self.avIdList[0]] = hprPositions[0]
                self.hprDict[self.avIdList[1]] = hprPositions[0]
                self.hprDict[self.avIdList[2]] = hprPositions[1]
                self.hprDict[self.avIdList[3]] = hprPositions[1]
        for x in self.avIdList:
            self.offsetDict[x] = 0
            if self.posDict[x][0] < 0:
                self.sides[x] = 0
                self.avList[0].append(x)
            else:
                self.sides[x] = 1
                self.avList[1].append(x)

        for avId in self.avIdList:
            toon = self.getAvatar(avId)
            toon.setPos(self.posDict[avId])
            toon.setHpr(self.hprDict[avId])

    def arrangeByHeight(self, avIdList, order, iStart, iFin):
        for i in xrange(iStart, iFin + 1):
            for j in xrange(i + 1, iFin + 1):
                if order == self.H_TO_L and self.rightHandDict[avIdList[i]].getZ() < self.rightHandDict[avIdList[j]].getZ() or order == self.L_TO_H and self.rightHandDict[avIdList[i]].getZ() > self.rightHandDict[avIdList[j]].getZ():
                    temp = avIdList[i]
                    avIdList[i] = avIdList[j]
                    avIdList[j] = temp

    def disableArrow(self, a):
        a.setColor(1, 0, 0, 0.3)

    def enableArrow(self, a):
        a.setColor(1, 0, 0, 1)

    def hilightArrow(self, a):
        a.setColor(1, 0.7, 0, 1)

    def unhilightArrow(self, a):
        self.enableArrow(a)

    def handleDisabledAvatar(self, avId):
        self.notify.debug('handleDisabledAvatar')
        self.notify.debug('avatar ' + str(avId) + ' disabled')
        DistributedMinigame.handleDisabledAvatar(self, avId)

    def __playing(self):
        return self.gameFSM.getCurrentState() != self.gameFSM.getFinalState()

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameStart')
        if not self.__playing():
            return
        DistributedMinigame.setGameStart(self, timestamp)
        self.gameFSM.request('waitForGoSignal')

    def __initGameVars(self):
        pass

    def makeToonLookatCamera(self, toon):
        toon.headsUp(camera)

    def setText(self, t, newtext):
        t['text'] = newtext

    def setTextFG(self, t, fg):
        t['text_fg'] = fg

    def enterOff(self):
        self.notify.debug('enterOff')

    def exitOff(self):
        pass

    def enterWaitForGoSignal(self):
        self.notify.debug('enterWaitForGoSignal')
        self.powerMeter.show()
        self.sendUpdate('reportPlayerReady', [self.sides[self.localAvId]])
        self.roundText.show()
        taskMgr.doMethodLater(TugOfWarGameGlobals.WAIT_FOR_GO_TIMEOUT, self.waitForGoTimeoutTask, self.taskName('wait-for-go-timeout'))

    def exitWaitForGoSignal(self):
        taskMgr.remove(self.taskName('wait-for-go-timeout'))

    def enterTug(self):
        self.notify.debug('enterTug')
        self.__spawnUpdateIdealRateTask()
        self.__spawnUpdateTimerTask()
        self.__spawnUpdateKeyPressRateTask()
        taskMgr.doMethodLater(TugOfWarGameGlobals.TUG_TIMEOUT, self.tugTimeoutTask, self.taskName('tug-timeout'))
        if self.suit:
            self.suit.loop('tug-o-war')

    def exitTug(self):
        self.notify.debug('exitTug')
        if self.suit:
            self.suit.loop('neutral')
        self.timer.stop()
        self.timer.hide()
        taskMgr.remove(self.taskName('tug-timeout'))

    def enterGameDone(self):
        pass

    def exitGameDone(self):
        pass

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.__killUpdateIdealRateTask()
        self.__killUpdateTimerTask()
        self.__killUpdateKeyPressRateTask()
        self.__killUpdateRopeTask()

    def exitCleanup(self):
        pass

    def __gameTimerExpired(self):
        self.notify.debug('game timer expired')
        if self.arrowKeys:
            self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
            self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)

    def __pressHandler(self, index):
        self.notify.debug('pressHandler')
        if index == self.buttons[0]:
            self.hilightArrow(self.arrows[index])
            self.keyTTL.insert(0, 1.0)
            self.buttons.reverse()

    def __releaseHandler(self, index):
        self.notify.debug('releaseHandler')
        if index in self.buttons:
            self.unhilightArrow(self.arrows[index])

    def __updateKeyPressRateTask(self, task):
        if self.gameFSM.getCurrentState().getName() != 'tug':
            return Task.done
        for i in xrange(len(self.keyTTL)):
            self.keyTTL[i] -= 0.1

        for i in xrange(len(self.keyTTL)):
            if self.keyTTL[i] <= 0:
                a = self.keyTTL[0:i]
                del self.keyTTL
                self.keyTTL = a
                break

        self.keyRate = len(self.keyTTL)
        if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1:
            self.rateMatchAward += 0.3
        else:
            self.rateMatchAward = 0
        self.__spawnUpdateKeyPressRateTask()
        return Task.done

    def __updateTimerTask(self, task):
        if self.gameFSM.getCurrentState().getName() != 'tug':
            return Task.done
        self.currentForce = self.computeForce(self.keyRate)
        self.sendUpdate('reportCurrentKeyRate', [self.keyRate, self.currentForce])
        self.setSpeedGauge()
        self.setAnimState(self.localAvId, self.keyRate)
        self.__spawnUpdateTimerTask()
        return Task.done

    def __spawnUpdateTimerTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_TIMER_TASK))
        taskMgr.doMethodLater(TugOfWarGameGlobals.SEND_UPDATE, self.__updateTimerTask, self.taskName(self.UPDATE_TIMER_TASK))

    def __killUpdateTimerTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_TIMER_TASK))

    def __spawnUpdateKeyPressRateTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))
        taskMgr.doMethodLater(0.1, self.__updateKeyPressRateTask, self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))

    def __killUpdateKeyPressRateTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK))

    def __spawnUpdateIdealRateTask(self):
        self.idealRate = self.targetRateList[self.nextRateIndex][1]
        self.idealForce = self.advantage * (4 + 0.4 * self.idealRate)
        taskMgr.doMethodLater(self.targetRateList[self.nextRateIndex][0], self.__updateIdealRateTask, self.taskName('targetRateTimer'))

    def __updateIdealRateTask(self, task):
        self.nextRateIndex = self.nextRateIndex + 1
        if self.nextRateIndex < len(self.targetRateList):
            if self.nextRateIndex == len(self.targetRateList) - 1:
                self.allOutMode = 1
            self.idealRate = self.targetRateList[self.nextRateIndex][1]
            self.idealForce = self.advantage * (4 + 0.4 * self.idealRate)
            taskMgr.doMethodLater(self.targetRateList[self.nextRateIndex][0], self.__updateIdealRateTask, self.taskName('targetRateTimer'))
        return Task.done

    def __killUpdateIdealRateTask(self):
        taskMgr.remove(self.taskName('targetRateTimer'))

    def sendGoSignal(self, index):
        if not self.hasLocalToon:
            return
        else:
            self.notify.debug('sendGoSignal')
            self.buttons = index
            self.setupTrack = None
            self.showTrack = None

            def startTimer(self = self):
                self.currentStartTime = int(globalClock.getFrameTime() * 1000)
                time = 10
                self.timer.show()
                self.timer.setTime(TugOfWarGameGlobals.GAME_DURATION)
                self.timer.countdown(TugOfWarGameGlobals.GAME_DURATION, self.__gameTimerExpired)

            def enableKeys(self = self):

                def keyPress(self, index):
                    self.__pressHandler(index)

                def keyRelease(self, index):
                    self.__releaseHandler(index)

                self.arrowKeys.setPressHandlers([lambda self = self, keyPress = keyPress: keyPress(self, 2),
                 lambda self = self, keyPress = keyPress: keyPress(self, 3),
                 lambda self = self, keyPress = keyPress: keyPress(self, 1),
                 lambda self = self, keyPress = keyPress: keyPress(self, 0)])
                self.arrowKeys.setReleaseHandlers([lambda self = self, keyRelease = keyRelease: keyRelease(self, 2),
                 lambda self = self, keyRelease = keyRelease: keyRelease(self, 3),
                 lambda self = self, keyRelease = keyRelease: keyRelease(self, 1),
                 lambda self = self, keyRelease = keyRelease: keyRelease(self, 0)])
                for x in index:
                    self.enableArrow(self.arrows[x])

            if self.introTrack != None:
                self.introTrack.finish()
                self.introTrack = None
            self.setupTrack = Sequence(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameReady), Wait(1.5), Func(base.playSfx, self.whistleSound), Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameGo), Func(self.roundText.setScale, 0.3), Wait(1.5), Func(startTimer), Func(enableKeys), Func(self.gameFSM.request, 'tug'), Func(self.setText, self.roundText, ' '), Func(self.roundText.setScale, 0.2))
            self.setupTrack.start()
            return

    def sendStopSignal(self, winners, losers, tieers):
        if not self.hasLocalToon:
            return
        else:
            self.notify.debug('sendStopSignal')
            self.gameFSM.request('gameDone')
            self.hideControls()
            reactSeq = Sequence()
            exitSeq = Sequence()
            suitSlipTime = 0
            if self.gameFSM.getCurrentState().getName() == 'cleanup' or not self.randomNumGen:
                return
            if self.suit:
                if self.suitId in winners:
                    newPos = VBase3(2.65, 18, 0.1)
                    randInt = self.randomNumGen.randrange(0, 10)
                    oopsTrack = Wait(0)
                    if randInt < 3:
                        suitSlipTime = 2.2
                        waterPos = VBase3(0, 16, -5)
                        newPos -= VBase3(0.4, 0, 0)
                        self.suitSplash.stop()
                        self.suitSplash.setPos(waterPos[0], waterPos[1], -1.8)
                        self.suitSplash.setScale(3.5, 3.5, 1)
                        self.suitRipples.setPos(waterPos[0], waterPos[1], -1.7)
                        self.suitRipples.setScale(1, 1, 1)
                        startHpr = self.suit.getHpr()
                        destHpr = startHpr + VBase3(0, 0, -30)
                        oopsTrack = Sequence(Parallel(Func(self.suit.play, 'flail', None, 26, 38), LerpHprInterval(self.suit, 0.5, destHpr, startHpr=startHpr)), Parallel(Func(self.suit.play, 'slip-forward'), LerpPosInterval(self.suit, duration=1, pos=waterPos), Sequence(Wait(0.55), Func(base.playSfx, self.sndHitWater), Func(self.suitSplash.play), Func(self.ripples.play))))
                        reactSeq.append(Sequence(Func(self.suit.loop, 'victory'), Wait(2.6), LerpPosInterval(self.suit, duration=2, pos=newPos), oopsTrack, Func(self.suit.loop, 'neutral')))
            for avId in self.avIdList:
                toon = self.getAvatar(avId)
                toon.loop('neutral')
                if avId in winners:
                    reactSeq.append(Func(toon.loop, 'victory'))
                elif avId in losers:
                    reactSeq.append(Func(toon.loop, 'neutral'))
                else:
                    reactSeq.append(Func(toon.loop, 'neutral'))

            if self.localAvId in winners:
                exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameEnd))
                exitSeq.append(Wait(5.0))
            elif self.localAvId in losers:
                exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameEnd))
                exitSeq.append(Wait(4.8 + suitSlipTime))
            else:
                exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameTie))
                exitSeq.append(Wait(2.5))
            exitSeq.append(Func(self.gameOver))
            self.showTrack = Parallel(reactSeq, exitSeq)
            for x in self.animTracks.values():
                if x != None:
                    x.finish()

            self.showTrack.start()
            if self.arrowKeys:
                self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
                self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)
            return

    def remoteKeyRateUpdate(self, avId, keyRate):
        if not self.hasLocalToon:
            return
        if avId != self.localAvId:
            self.setAnimState(avId, keyRate)

    def sendSuitPosition(self, suitOffset):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() != 'tug':
            return
        self.suitOffset = suitOffset
        self.moveSuits()

    def sendCurrentPosition(self, avIdList, offsetList):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() != 'tug':
            return
        for i in xrange(len(avIdList)):
            self.offsetDict[avIdList[i]] = offsetList[i]

        self.moveToons()
        self.setUpRopes(0)

    def createSuits(self):
        if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
            self.suit = Suit.Suit()
            self.origSuitPosHpr = [VBase3(6.0, 18, 0.1), VBase3(120, 0, 0)]
            self.suitOffset = 0
            d = SuitDNA.SuitDNA()
            d.newSuit(self.suitType)
            self.suit.setDNA(d)
            self.suit.nametag3d.stash()
            self.suit.nametag.destroy()
            self.suit.reparentTo(render)
            self.suit.setPos(self.origSuitPosHpr[0])
            self.suit.setHpr(self.origSuitPosHpr[1])
            for anim in self.suitAnimNames:
                self.suit.pose(anim, 0)

            self.suit.pose('tug-o-war', 0)
            self.dropShadowDict[self.suitId] = self.dropShadow.copyTo(hidden)
            self.dropShadowDict[self.suitId].reparentTo(self.suit)
            self.dropShadowDict[self.suitId].setScale(0.45)

    def initHandycaps(self):
        if self.numPlayers == 3 and self.gameType == TugOfWarGameGlobals.TOON_VS_TOON:
            if len(self.avList[0]) == 1:
                toon = self.getAvatar(self.avList[0][0])
                if self.avList[0][0] == self.localAvId:
                    self.advantage = 2.0
                toon.applyCheesyEffect(ToontownGlobals.CEBigHead)
            elif len(self.avList[1]) == 1:
                toon = self.getAvatar(self.avList[1][0])
                if self.avList[1][0] == self.localAvId:
                    self.advantage = 2.0
                toon.applyCheesyEffect(ToontownGlobals.CEBigHead)

    def setSpeedGauge(self):
        self.powerMeter.setPower(self.keyRate)
        self.powerMeter.setTarget(self.idealRate)
        if not self.allOutMode:
            self.powerMeter.updateTooSlowTooFast()
        if not self.allOutMode:
            index = float(self.currentForce) / self.idealForce
            bonus = 0.0
            if index > 1:
                bonus = max(1, index - 1)
                index = 1
            color = (0,
             0.75 * index + 0.25 * bonus,
             0.75 * (1 - index),
             0.5)
            self.powerMeter.setBarColor(color)
        else:
            self.powerMeter.setBarColor((0, 1, 0, 0.5))

    def setAnimState(self, avId, keyRate):
        if self.gameFSM.getCurrentState().getName() != 'tug':
            return
        toon = self.getAvatar(avId)
        if keyRate > 0 and self.pullingDict[avId] == 0:
            toon.loop('tug-o-war')
            self.pullingDict[avId] = 1
        if keyRate <= 0 and self.pullingDict[avId] == 1:
            toon.pose('tug-o-war', 3)
            toon.startLookAround()
            self.pullingDict[avId] = 0

    def moveSuits(self):
        if self.gameType != TugOfWarGameGlobals.TOON_VS_COG:
            return
        else:
            origPos = self.origSuitPosHpr[0]
            curPos = self.suit.getPos()
            newPos = VBase3(origPos[0] + self.suitOffset, curPos[1], curPos[2])
            if self.animTracks[self.suitId] != None:
                if self.animTracks[self.suitId].isPlaying():
                    self.animTracks[self.suitId].finish()
                    self.checkIfFallen()
            if self.suitId not in self.fallenList:
                self.animTracks[self.suitId] = Sequence(LerpPosInterval(self.suit, duration=TugOfWarGameGlobals.SEND_UPDATE, pos=newPos), Func(self.checkIfFallen))
                self.animTracks[self.suitId].start()
            return

    def moveToons(self):
        for avId in self.avIdList:
            if avId not in self.fallenList:
                toon = self.getAvatar(avId)
                if toon:
                    origPos = self.posDict[avId]
                    curPos = toon.getPos()
                    newPos = VBase3(origPos[0] + self.offsetDict[avId] / self.handycap, curPos[1], curPos[2])
                    if self.animTracks[avId] != None:
                        if self.animTracks[avId].isPlaying():
                            self.animTracks[avId].finish()
                            self.checkIfFallen(avId)
                    if avId not in self.fallenList:
                        self.animTracks[avId] = Sequence(LerpPosInterval(toon, duration=TugOfWarGameGlobals.SEND_UPDATE, pos=newPos), Func(self.checkIfFallen, avId))
                        self.animTracks[avId].start()

        return

    def checkIfFallen(self, avId = None):
        if avId == None:
            if self.suitId not in self.fallenList:
                curPos = self.suit.getPos()
                if curPos[0] < 0 and curPos[0] > -2 or curPos[0] > 0 and curPos[0] < 2:
                    self.hideControls()
                    self.throwInWater()
                    losingSide = 1
                    self.sendUpdate('reportEndOfContest', [losingSide])
        elif avId not in self.fallenList:
            toon = self.getAvatar(avId)
            if toon:
                curPos = toon.getPos()
                if curPos[0] < 0 and curPos[0] > -2 or curPos[0] > 0 and curPos[0] < 2:
                    self.hideControls()
                    losingSide = self.sides[avId]
                    for avId in self.avIdList:
                        if self.sides[avId] == losingSide:
                            self.throwInWater(avId)

                    self.sendUpdate('reportEndOfContest', [losingSide])
        return

    def throwInWater(self, avId = None):
        if avId == None:
            self.fallenList.append(self.suitId)
            waterPos = self.drinkPositions.pop()
            newPos = VBase3(waterPos[0], waterPos[1], waterPos[2] - self.suit.getHeight() / 1.5)
            self.suit.loop('neutral')
            self.dropShadowDict[self.suitId].reparentTo(hidden)
            loser = self.suit
            animId = self.suitId
        else:
            self.fallenList.append(avId)
            toon = self.getAvatar(avId)
            waterPos = self.drinkPositions.pop()
            newPos = VBase3(waterPos[0], waterPos[1], waterPos[2] - toon.getHeight())
            toon.loop('neutral')
            self.dropShadowDict[avId].reparentTo(hidden)
            loser = toon
            animId = avId
        if self.animTracks[animId] != None:
            if self.animTracks[animId].isPlaying():
                self.animTracks[animId].finish()
        self.splash.setPos(newPos[0], newPos[1], -1.8)
        self.splash.setScale(2.5, 2.5, 1)
        self.ripples.setPos(newPos[0], newPos[1], -1.7)
        self.ripples.setScale(1, 1, 1)
        self.animTracks[animId] = Sequence(Parallel(ActorInterval(actor=loser, animName='slip-forward', duration=2.0), LerpPosInterval(loser, duration=2.0, pos=newPos), Sequence(Wait(1.0), Parallel(Func(base.playSfx, self.sndHitWater), Func(self.splash.play), Func(self.ripples.play)))), Func(loser.loop, 'neutral'))
        self.animTracks[animId].start()
        return

    def computeForce(self, keyRate):
        F = 0
        if self.allOutMode == 1:
            F = 0.75 * keyRate
        else:
            stdDev = 0.25 * self.idealRate
            F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2)))
        return F

    def initRopes(self):
        if self.gameType == TugOfWarGameGlobals.TOON_VS_COG:
            numRopes = self.numPlayers
        else:
            numRopes = self.numPlayers - 1
        for x in xrange(0, numRopes):
            rope = Rope.Rope(self.uniqueName('TugRope' + str(x)))
            if rope.showRope:
                rope.ropeNode.setRenderMode(RopeNode.RMBillboard)
                rope.ropeNode.setThickness(0.2)
                rope.setTexture(self.ropeTexture)
                rope.ropeNode.setUvMode(RopeNode.UVDistance)
                rope.ropeNode.setUvDirection(1)
                rope.setTransparency(1)
                rope.setColor(0.89, 0.89, 0.6, 1)
            self.tugRopes.append(rope)

        self.setUpRopes(1)

    def __spawnUpdateRopeTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_ROPE_TASK))
        taskMgr.add(self.__updateRopeTask, self.taskName(self.UPDATE_ROPE_TASK))

    def __updateRopeTask(self, task):
        if self.tugRopes != None:
            for i in xrange(len(self.tugRopes)):
                if self.tugRopes[i] != None:
                    self.ropePts[i] = self.tugRopes[i].getPoints(len(self.ropeTex[i]))
                    for j in xrange(len(self.ropePts[i])):
                        self.ropeTex[i][j].setPos(self.ropePts[i][j])

        return Task.cont

    def __killUpdateRopeTask(self):
        taskMgr.remove(self.taskName(self.UPDATE_ROPE_TASK))

    def tugTimeoutTask(self, task):
        self.gameOver()
        return Task.done

    def waitForGoTimeoutTask(self, task):
        self.gameOver()
        return Task.done

    def __spawnMouseSpeedTask(self):
        taskMgr.remove(self.taskName('mouseSpeed'))
        taskMgr.add(self.__mouseSpeedTask, self.taskName('mouseSpeed'))

    def __killMouseSpeedTask(self):
        taskMgr.remove(self.taskName('mouseSpeed'))

    def __mouseSpeedTask(self, task):
        dx = 0.1
        if self.mouseMode:
            mx = base.mouseWatcherNode.getMouseX()
            my = base.mouseWatcherNode.getMouseY()
            if self.mouseSide == 0:
                if mx > dx:
                    self.mouseSide = 1
                    self.__releaseHandler(1)
                    self.__pressHandler(0)
                elif mx > -dx:
                    self.__releaseHandler(1)
            elif self.mouseSide == 1:
                if mx < -dx:
                    self.mouseSide = 0
                    self.__releaseHandler(0)
                    self.__pressHandler(1)
                elif mx < dx:
                    self.__releaseHandler(0)
        return Task.cont