class SquirtingFlower(SquirtGag):

    def __init__(self):
        SquirtGag.__init__(self, CIGlobals.SquirtFlower, GagGlobals.getProp(3.5, 'button'), 3, GagGlobals.FLOWER_HIT_SFX, GagGlobals.FLOWER_HIT_SFX, GagGlobals.NULL_SFX, None, 0, 0, 0)
        self.setImage('phase_3.5/maps/squirting-flower.png')
        self.flower = None
        self.flowerScale = 1.5
        self.track = Parallel()
        self.timeout = 4.0
        self.sprayRotation = Vec3(0, 20, 0)
        return

    def start(self):
        SquirtGag.start(self)
        self.buildFlower()
        self.build()
        self.equip()
        if self.isLocal():
            self.startTimeout()
        self.origin = self.getSprayStartPos()

        def attachFlower():
            flowerJoint = self.avatar.find('**/def_joint_attachFlower')
            if flowerJoint.isEmpty():
                flowerJoint = self.avatar.find('**/joint_attachFlower')
            self.flower.reparentTo(flowerJoint)
            self.flower.setY(self.flower.getY())

        totalAnimationTime = 2.5
        flowerAppear = 1.0
        flowerScaleTime = 0.5
        animTrack = ActorInterval(self.avatar, 'push-button')
        self.track.append(animTrack)
        flowerTrack = Sequence(Func(attachFlower), Wait(flowerAppear), LerpScaleInterval(self.flower, flowerScaleTime, 1.5, startScale=GagGlobals.PNT3NEAR0), Wait(totalAnimationTime - flowerScaleTime - flowerAppear))
        flowerTrack.append(Func(self.release))
        flowerTrack.append(LerpScaleInterval(self.flower, flowerScaleTime, GagGlobals.PNT3NEAR0))
        flowerTrack.append(LerpScaleInterval(self.gag, flowerScaleTime, GagGlobals.PNT3NEAR0))
        flowerTrack.append(Func(self.unEquip))
        self.track.append(flowerTrack)
        self.track.start()

    def getSprayStartPos(self):
        if not self.avatar.isEmpty() and not self.flower.isEmpty():
            self.avatar.update(0)
            return self.flower.getPos(render)

    def release(self):
        SquirtGag.release(self)
        if not self.avatar.isEmpty() and self.gag:
            self.sprayJoint = self.flower.find('**/joint_attachSpray')
            self.sprayRange = self.avatar.getPos(render) + Point3(0, GagGlobals.SELTZER_RANGE, 0)
            self.doSpray(0.2, 0.2, 0.1, horizScale=0.3, vertScale=0.3)
            if self.isLocal():
                base.localAvatar.sendUpdate('usedGag', [self.id])

    def unEquip(self):
        SquirtGag.unEquip(self)
        self.cleanup()
        self.reset()

    def cleanup(self):
        if self.flower:
            self.flower.removeNode()
            self.flower = None
        if self.track:
            self.track.pause()
            self.track = Parallel()
        return

    def setHandJoint(self):
        if self.avatar:
            self.handJoint = self.avatar.find('**/def_joint_left_hold')
            if not self.handJoint:
                print self.avatar.findAllMatches('**/*joint*')

    def buildFlower(self):
        if self.flower:
            self.flower.removeNode()
            self.flower = None
        self.flower = loader.loadModel(GagGlobals.getProp(3.5, 'squirting-flower'))
        self.flower.setScale(GagGlobals.PNT3NEAR0)
        return
Beispiel #2
0
class DodgeballFirstPerson(FirstPerson):
    """The first person controls for the local player in Winter Dodgeball"""

    notify = directNotify.newCategory("DodgeballFirstPerson")

    MaxPickupDistance = 5.0

    def __init__(self, mg):
        self.mg = mg
        self.crosshair = None
        self.soundCatch = None
        self.vModelRoot = None
        self.vModel = None
        self.ival = None
        self.soundPickup = base.loadSfx(
            'phase_4/audio/sfx/MG_snowball_pickup.wav')
        self.fakeSnowball = loader.loadModel(
            "phase_5/models/props/snowball.bam")
        self.hasSnowball = False
        self.mySnowball = None
        self.fsm = ClassicFSM.ClassicFSM("DodgeballFirstPerson", [
            State.State("off", self.enterOff, self.exitOff),
            State.State("hold", self.enterHold, self.exitHold),
            State.State("catch", self.enterCatch, self.exitCatch),
            State.State("throw", self.enterThrow, self.exitThrow)
        ], "off", "off")
        self.fsm.enterInitialState()

        FirstPerson.__init__(self)

    def enterOff(self):
        if self.vModel:
            self.vModel.hide()

    def exitOff(self):
        if self.vModel:
            self.vModel.show()

    def enterHold(self):
        self.ival = Sequence(ActorInterval(self.vModel, "hold-start"),
                             Func(self.vModel.loop, "hold"))
        self.ival.start()

    def exitHold(self):
        if self.ival:
            self.ival.finish()
            self.ival = None
        self.vModel.stop()

    def enterThrow(self):
        self.ival = Parallel(
            Sequence(Wait(0.4), Func(self.mySnowball.b_throw)),
            Sequence(ActorInterval(self.vModel, "throw"),
                     Func(self.fsm.request, 'off')))
        self.ival.start()

    def exitThrow(self):
        if self.ival:
            self.ival.pause()
            self.ival = None
        self.vModel.stop()

    def enterCatch(self):
        self.ival = Parallel(
            Sequence(Wait(0.2), Func(self.__tryToCatchOrGrab)),
            Sequence(ActorInterval(self.vModel, "catch"),
                     Func(self.__maybeHold)))
        self.ival.start()

    def __maybeHold(self):
        if self.hasSnowball:
            self.fsm.request('hold')
        else:
            self.fsm.request('off')

    def __tryToCatchOrGrab(self):
        snowballs = list(self.mg.snowballs)
        snowballs.sort(
            key=lambda snowball: snowball.getDistance(base.localAvatar))
        for i in xrange(len(snowballs)):
            snowball = snowballs[i]
            if (not snowball.hasOwner() and not snowball.isAirborne
                    and snowball.getDistance(base.localAvatar) <=
                    DodgeballFirstPerson.MaxPickupDistance):
                snowball.b_pickup()
                self.mySnowball = snowball
                self.fakeSnowball.setPosHpr(0, 0.73, 0, 0, 0, 0)
                self.fakeSnowball.reparentTo(
                    self.vModel.exposeJoint(None, "modelRoot", "Bone.011"))
                base.playSfx(self.soundPickup)
                self.hasSnowball = True
                break

    def exitCatch(self):
        self.vModel.stop()
        if self.ival:
            self.ival.pause()
            self.ival = None

    def start(self):
        # Black crosshair because basically the entire arena is white.
        self.crosshair = getCrosshair(color=(0, 0, 0, 1), hidden=False)

        self.soundCatch = base.loadSfx(
            "phase_4/audio/sfx/MG_sfx_vine_game_catch.ogg")

        self.vModelRoot = camera.attachNewNode('vModelRoot')
        self.vModelRoot.setPos(-0.09, 1.38, -2.48)

        self.vModel = Actor(
            "phase_4/models/minigames/v_dgm.egg", {
                "hold": "phase_4/models/minigames/v_dgm-ball-hold.egg",
                "hold-start":
                "phase_4/models/minigames/v_dgm-ball-hold-start.egg",
                "throw": "phase_4/models/minigames/v_dgm-ball-throw.egg",
                "catch": "phase_4/models/minigames/v_dgm-ball-catch.egg"
            })
        self.vModel.setBlend(frameBlend=True)
        self.vModel.reparentTo(self.vModelRoot)
        self.vModel.setBin("fixed", 40)
        self.vModel.setDepthTest(False)
        self.vModel.setDepthWrite(False)
        self.vModel.hide()

        base.localAvatar.walkControls.setWalkSpeed(ToonForwardSpeed,
                                                   ToonJumpForce,
                                                   ToonReverseSpeed,
                                                   ToonRotateSpeed)

        FirstPerson.start(self)

    def reallyStart(self):
        FirstPerson.reallyStart(self)
        base.localAvatar.startTrackAnimToSpeed()

        self.accept('mouse3', self.__handleCatchOrGrabButton)
        self.accept('mouse1', self.__handleThrowButton)

    def __handleThrowButton(self):
        if self.hasSnowball and self.mySnowball and self.fsm.getCurrentState(
        ).getName() == 'hold':
            self.fakeSnowball.reparentTo(hidden)
            self.fsm.request('throw')

    def __handleCatchOrGrabButton(self):
        if not self.hasSnowball and not self.mySnowball and self.fsm.getCurrentState(
        ).getName() == 'off':
            self.fsm.request('catch')

    def reallyEnd(self):
        base.localAvatar.setWalkSpeedNormal()
        FirstPerson.reallyEnd(self)
class DistributedIceGame(DistributedMinigame.DistributedMinigame,
                         DistributedIceWorld.DistributedIceWorld):
    notify = directNotify.newCategory('DistributedIceGame')
    MaxLocalForce = 100
    MaxPhysicsForce = 25000

    def __init__(self, cr):
        DistributedMinigame.DistributedMinigame.__init__(self, cr)
        DistributedIceWorld.DistributedIceWorld.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedIceGame', [
            State.State('off', self.enterOff, self.exitOff, ['inputChoice']),
            State.State(
                'inputChoice', self.enterInputChoice, self.exitInputChoice,
                ['waitServerChoices', 'moveTires', 'displayVotes', 'cleanup']),
            State.State('waitServerChoices', self.enterWaitServerChoices,
                        self.exitWaitServerChoices, ['moveTires', 'cleanup']),
            State.State('moveTires', self.enterMoveTires, self.exitMoveTires,
                        ['synch', 'cleanup']),
            State.State('synch', self.enterSynch, self.exitSynch,
                        ['inputChoice', 'scoring', 'cleanup']),
            State.State('scoring', self.enterScoring, self.exitScoring,
                        ['cleanup', 'finalResults', 'inputChoice']),
            State.State('finalResults', self.enterFinalResults,
                        self.exitFinalResults, ['cleanup']),
            State.State('cleanup', self.enterCleanup, self.exitCleanup, [])
        ], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        self.cameraThreeQuarterView = (0, -22, 45, 0, -62.890000000000001, 0)
        self.tireDict = {}
        self.forceArrowDict = {}
        self.canDrive = False
        self.timer = None
        self.timerStartTime = None
        self.curForce = 0
        self.curHeading = 0
        self.headingMomentum = 0.0
        self.forceMomentum = 0.0
        self.allTireInputs = None
        self.curRound = 0
        self.curMatch = 0
        self.controlKeyWarningLabel = DirectLabel(
            text=TTLocalizer.IceGameControlKeyWarning,
            text_fg=VBase4(1, 0, 0, 1),
            relief=None,
            pos=(0.0, 0, 0),
            scale=0.14999999999999999)
        self.controlKeyWarningLabel.hide()
        self.waitingMoveLabel = DirectLabel(
            text=TTLocalizer.IceGameWaitingForPlayersToFinishMove,
            text_fg=VBase4(1, 1, 1, 1),
            relief=None,
            pos=(-0.59999999999999998, 0, -0.75),
            scale=0.074999999999999997)
        self.waitingMoveLabel.hide()
        self.waitingSyncLabel = DirectLabel(
            text=TTLocalizer.IceGameWaitingForAISync,
            text_fg=VBase4(1, 1, 1, 1),
            relief=None,
            pos=(-0.59999999999999998, 0, -0.75),
            scale=0.074999999999999997)
        self.waitingSyncLabel.hide()
        self.infoLabel = DirectLabel(text='',
                                     text_fg=VBase4(0, 0, 0, 1),
                                     relief=None,
                                     pos=(0.0, 0, 0.69999999999999996),
                                     scale=0.074999999999999997)
        self.updateInfoLabel()
        self.lastForceArrowUpdateTime = 0
        self.sendForceArrowUpdateAsap = False
        self.treasures = []
        self.penalties = []
        self.obstacles = []
        self.controlKeyPressed = False
        self.controlKeyWarningIval = None

    def delete(self):
        DistributedIceWorld.DistributedIceWorld.delete(self)
        DistributedMinigame.DistributedMinigame.delete(self)
        if self.controlKeyWarningIval:
            self.controlKeyWarningIval.finish()
            self.controlKeyWarningIval = None

        self.controlKeyWarningLabel.destroy()
        del self.controlKeyWarningLabel
        self.waitingMoveLabel.destroy()
        del self.waitingMoveLabel
        self.waitingSyncLabel.destroy()
        del self.waitingSyncLabel
        self.infoLabel.destroy()
        del self.infoLabel
        for treasure in self.treasures:
            treasure.destroy()

        del self.treasures
        for penalty in self.penalties:
            penalty.destroy()

        del self.penalties
        for obstacle in self.obstacles:
            obstacle.removeNode()

        del self.obstacles
        del self.gameFSM

    def announceGenerate(self):
        DistributedMinigame.DistributedMinigame.announceGenerate(self)
        DistributedIceWorld.DistributedIceWorld.announceGenerate(self)
        self.debugTaskName = self.uniqueName('debugTask')

    def getTitle(self):
        return TTLocalizer.IceGameTitle

    def getInstructions(self):
        szId = self.getSafezoneId()
        numPenalties = IceGameGlobals.NumPenalties[szId]
        result = TTLocalizer.IceGameInstructions
        if numPenalties == 0:
            result = TTLocalizer.IceGameInstructionsNoTnt

        return result

    def getMaxDuration(self):
        return 0

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.DistributedMinigame.load(self)
        self.music = base.loadMusic('phase_4/audio/bgm/MG_IceGame.mid')
        self.gameBoard = loader.loadModel(
            'phase_4/models/minigames/ice_game_icerink')
        background = loader.loadModel('phase_4/models/minigames/ice_game_2d')
        background.reparentTo(self.gameBoard)
        self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0)
        self.gameBoard.setScale(1.0)
        self.setupSimulation()
        index = 0
        for avId in self.avIdList:
            self.setupTire(avId, index)
            self.setupForceArrow(avId)
            index += 1

        for index in xrange(len(self.avIdList), 4):
            self.setupTire(-index, index)
            self.setupForceArrow(-index)

        self.showForceArrows(realPlayersOnly=True)
        self.westWallModel = NodePath()
        if not self.westWallModel.isEmpty():
            self.westWallModel.reparentTo(self.gameBoard)
            self.westWallModel.setPos(IceGameGlobals.MinWall[0],
                                      IceGameGlobals.MinWall[1], 0)
            self.westWallModel.setScale(4)

        self.eastWallModel = NodePath()
        if not self.eastWallModel.isEmpty():
            self.eastWallModel.reparentTo(self.gameBoard)
            self.eastWallModel.setPos(IceGameGlobals.MaxWall[0],
                                      IceGameGlobals.MaxWall[1], 0)
            self.eastWallModel.setScale(4)
            self.eastWallModel.setH(180)

        self.arrowKeys = ArrowKeys.ArrowKeys()
        self.target = loader.loadModel('phase_3/models/misc/sphere')
        self.target.setScale(0.01)
        self.target.reparentTo(self.gameBoard)
        self.target.setPos(0, 0, 0)
        self.scoreCircle = loader.loadModel(
            'phase_4/models/minigames/ice_game_score_circle')
        self.scoreCircle.setScale(0.01)
        self.scoreCircle.reparentTo(self.gameBoard)
        self.scoreCircle.setZ(IceGameGlobals.TireRadius / 2.0)
        self.scoreCircle.setAlphaScale(0.5)
        self.scoreCircle.setTransparency(1)
        self.scoreCircle.hide()
        self.treasureModel = loader.loadModel(
            'phase_4/models/minigames/ice_game_barrel')
        self.penaltyModel = loader.loadModel(
            'phase_4/models/minigames/ice_game_tnt2')
        self.penaltyModel.setScale(0.75, 0.75, 0.69999999999999996)
        szId = self.getSafezoneId()
        obstacles = IceGameGlobals.Obstacles[szId]
        index = 0
        cubicObstacle = IceGameGlobals.ObstacleShapes[szId]
        for pos in obstacles:
            newPos = Point3(pos[0], pos[1], IceGameGlobals.TireRadius)
            newObstacle = self.createObstacle(newPos, index, cubicObstacle)
            self.obstacles.append(newObstacle)
            index += 1

        self.countSound = loader.loadSfx(
            'phase_3.5/audio/sfx/tick_counter.mp3')
        self.treasureGrabSound = loader.loadSfx(
            'phase_4/audio/sfx/MG_sfx_vine_game_bananas.mp3')
        self.penaltyGrabSound = loader.loadSfx(
            'phase_4/audio/sfx/MG_cannon_fire_alt.mp3')
        self.tireSounds = []
        for tireIndex in xrange(4):
            tireHit = loader.loadSfx(
                'phase_4/audio/sfx/Golf_Hit_Barrier_1.mp3')
            wallHit = loader.loadSfx('phase_4/audio/sfx/MG_maze_pickup.mp3')
            obstacleHit = loader.loadSfx(
                'phase_4/audio/sfx/Golf_Hit_Barrier_2.mp3')
            self.tireSounds.append({
                'tireHit': tireHit,
                'wallHit': wallHit,
                'obstacleHit': obstacleHit
            })

        self.arrowRotateSound = loader.loadSfx(
            'phase_4/audio/sfx/MG_sfx_ice_force_rotate.wav')
        self.arrowUpSound = loader.loadSfx(
            'phase_4/audio/sfx/MG_sfx_ice_force_increase_3sec.mp3')
        self.arrowDownSound = loader.loadSfx(
            'phase_4/audio/sfx/MG_sfx_ice_force_decrease_3sec.mp3')
        self.scoreCircleSound = loader.loadSfx(
            'phase_4/audio/sfx/MG_sfx_ice_scoring_1.mp3')

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.DistributedMinigame.unload(self)
        del self.music
        self.gameBoard.removeNode()
        del self.gameBoard
        for forceArrow in self.forceArrowDict.values():
            forceArrow.removeNode()

        del self.forceArrowDict
        self.scoreCircle.removeNode()
        del self.scoreCircle
        del self.countSound

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.DistributedMinigame.onstage(self)
        self.gameBoard.reparentTo(render)
        self._DistributedIceGame__placeToon(self.localAvId)
        self.moveCameraToTop()
        self.scorePanels = []
        base.playMusic(self.music, looping=1, volume=0.80000000000000004)

    def offstage(self):
        self.notify.debug('offstage')
        self.music.stop()
        self.gameBoard.hide()
        self.infoLabel.hide()
        for avId in self.tireDict:
            self.tireDict[avId]['tireNodePath'].hide()

        for panel in self.scorePanels:
            panel.cleanup()

        del self.scorePanels
        for obstacle in self.obstacles:
            obstacle.hide()

        for treasure in self.treasures:
            treasure.nodePath.hide()

        for penalty in self.penalties:
            penalty.nodePath.hide()

        for avId in self.avIdList:
            av = self.getAvatar(avId)
            if av:
                av.dropShadow.show()
                av.resetLOD()
                continue

        taskMgr.remove(self.uniqueName('aimtask'))
        self.arrowKeys.destroy()
        del self.arrowKeys
        DistributedMinigame.DistributedMinigame.offstage(self)

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

    def setGameReady(self):
        if not self.hasLocalToon:
            return None

        self.notify.debug('setGameReady')
        if DistributedMinigame.DistributedMinigame.setGameReady(self):
            return None

        for index in xrange(self.numPlayers):
            avId = self.avIdList[index]
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                self._DistributedIceGame__placeToon(avId)
                toon.forwardSpeed = 0
                toon.rotateSpeed = False
                toon.dropShadow.hide()
                toon.setAnimState('Sit')
                if avId in self.tireDict:
                    tireNp = self.tireDict[avId]['tireNodePath']
                    toon.reparentTo(tireNp)
                    toon.setY(1.0)
                    toon.setZ(-3)

                toon.startLookAround()
                continue

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return None

        self.notify.debug('setGameStart')
        DistributedMinigame.DistributedMinigame.setGameStart(self, timestamp)
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.stopLookAround()
                continue

        self.scores = [0] * self.numPlayers
        spacing = 0.40000000000000002
        for i in xrange(self.numPlayers):
            avId = self.avIdList[i]
            avName = self.getAvatarName(avId)
            scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(
                avId, avName)
            scorePanel.setScale(0.90000000000000002)
            scorePanel.setPos(0.75 - spacing * (self.numPlayers - 1 - i), 0.0,
                              0.875)
            scorePanel.makeTransparent(0.75)
            self.scorePanels.append(scorePanel)

        self.arrowKeys.setPressHandlers([
            self._DistributedIceGame__upArrowPressed,
            self._DistributedIceGame__downArrowPressed,
            self._DistributedIceGame__leftArrowPressed,
            self._DistributedIceGame__rightArrowPressed,
            self._DistributedIceGame__controlPressed
        ])

    def isInPlayState(self):
        if not self.gameFSM.getCurrentState():
            return False

        if not self.gameFSM.getCurrentState().getName() == 'play':
            return False

        return True

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

    def exitOff(self):
        pass

    def enterInputChoice(self):
        self.notify.debug('enterInputChoice')
        self.forceLocalToonToTire()
        self.controlKeyPressed = False
        if self.curRound == 0:
            self.setupStartOfMatch()
        else:
            self.notify.debug('self.curRound = %s' % self.curRound)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.hide()
        if self.timerStartTime != None:
            self.startTimer()

        self.showForceArrows(realPlayersOnly=True)
        self.localForceArrow().setPosHpr(0, 0, -1.0, 0, 0, 0)
        self.localForceArrow().reparentTo(self.localTireNp())
        self.localForceArrow().setY(IceGameGlobals.TireRadius)
        self.localTireNp().headsUp(self.target)
        self.notify.debug('self.localForceArrow() heading = %s' %
                          self.localForceArrow().getH())
        self.curHeading = self.localTireNp().getH()
        self.curForce = 25
        self.updateLocalForceArrow()
        for avId in self.forceArrowDict:
            forceArrow = self.forceArrowDict[avId]
            forceArrow.setPosHpr(0, 0, -1.0, 0, 0, 0)
            tireNp = self.tireDict[avId]['tireNodePath']
            forceArrow.reparentTo(tireNp)
            forceArrow.setY(IceGameGlobals.TireRadius)
            tireNp.headsUp(self.target)
            self.updateForceArrow(avId, tireNp.getH(), 25)

        taskMgr.add(self._DistributedIceGame__aimTask,
                    self.uniqueName('aimtask'))
        if base.localAvatar.laffMeter:
            base.localAvatar.laffMeter.stop()

        self.sendForceArrowUpdateAsap = False

    def exitInputChoice(self):
        if not self.controlKeyPressed:
            if self.controlKeyWarningIval:
                self.controlKeyWarningIval.finish()
                self.controlKeyWarningIval = None

            self.controlKeyWarningIval = Sequence(
                Func(self.controlKeyWarningLabel.show),
                self.controlKeyWarningLabel.colorScaleInterval(
                    10, VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1,
                                                                   1)),
                Func(self.controlKeyWarningLabel.hide))
            self.controlKeyWarningIval.start()

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

        self.timerStartTime = None
        self.hideForceArrows()
        self.arrowRotateSound.stop()
        self.arrowUpSound.stop()
        self.arrowDownSound.stop()
        taskMgr.remove(self.uniqueName('aimtask'))

    def enterWaitServerChoices(self):
        self.waitingMoveLabel.show()
        self.showForceArrows(True)

    def exitWaitServerChoices(self):
        self.waitingMoveLabel.hide()
        self.hideForceArrows()

    def enterMoveTires(self):
        for key in self.tireDict:
            body = self.tireDict[key]['tireBody']
            body.setAngularVel(0, 0, 0)
            body.setLinearVel(0, 0, 0)

        for index in xrange(len(self.allTireInputs)):
            input = self.allTireInputs[index]
            avId = self.avIdList[index]
            body = self.getTireBody(avId)
            degs = input[1] + 90
            tireNp = self.getTireNp(avId)
            tireH = tireNp.getH()
            self.notify.debug('tireH = %s' % tireH)
            radAngle = deg2Rad(degs)
            foo = NodePath('foo')
            dirVector = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
            self.notify.debug('dirVector is now=%s' % dirVector)
            inputForce = input[0]
            inputForce /= self.MaxLocalForce
            inputForce *= self.MaxPhysicsForce
            force = dirVector * inputForce
            self.notify.debug('adding force %s to %d' % (force, avId))
            body.addForce(force)

        self.enableAllTireBodies()
        self.totalPhysicsSteps = 0
        self.startSim()
        taskMgr.add(self._DistributedIceGame__moveTiresTask,
                    self.uniqueName('moveTiresTtask'))

    def exitMoveTires(self):
        self.forceLocalToonToTire()
        self.disableAllTireBodies()
        self.stopSim()
        self.notify.debug('total Physics steps = %d' % self.totalPhysicsSteps)
        taskMgr.remove(self.uniqueName('moveTiresTtask'))

    def enterSynch(self):
        self.waitingSyncLabel.show()

    def exitSynch(self):
        self.waitingSyncLabel.hide()

    def enterScoring(self):
        sortedByDistance = []
        for avId in self.avIdList:
            np = self.getTireNp(avId)
            pos = np.getPos()
            pos.setZ(0)
            sortedByDistance.append((avId, pos.length()))

        def compareDistance(x, y):
            if x[1] - y[1] > 0:
                return 1
            elif x[1] - y[1] < 0:
                return -1
            else:
                return 0

        sortedByDistance.sort(cmp=compareDistance)
        self.scoreMovie = Sequence()
        curScale = 0.01
        curTime = 0
        self.scoreCircle.setScale(0.01)
        self.scoreCircle.show()
        self.notify.debug('newScores = %s' % self.newScores)
        circleStartTime = 0
        for index in xrange(len(sortedByDistance)):
            distance = sortedByDistance[index][1]
            avId = sortedByDistance[index][0]
            scorePanelIndex = self.avIdList.index(avId)
            time = (distance - curScale) / IceGameGlobals.ExpandFeetPerSec
            if time < 0:
                time = 0.01

            scaleXY = distance + IceGameGlobals.TireRadius
            self.notify.debug('circleStartTime = %s' % circleStartTime)
            self.scoreMovie.append(
                Parallel(
                    LerpScaleInterval(self.scoreCircle, time,
                                      Point3(scaleXY, scaleXY, 1.0)),
                    SoundInterval(self.scoreCircleSound,
                                  duration=time,
                                  startTime=circleStartTime)))
            circleStartTime += time
            startScore = self.scorePanels[scorePanelIndex].getScore()
            destScore = self.newScores[scorePanelIndex]
            self.notify.debug('for avId %d, startScore=%d, newScores=%d' %
                              (avId, startScore, destScore))

            def increaseScores(t,
                               scorePanelIndex=scorePanelIndex,
                               startScore=startScore,
                               destScore=destScore):
                oldScore = self.scorePanels[scorePanelIndex].getScore()
                diff = destScore - startScore
                newScore = int(startScore + diff * t)
                if newScore > oldScore:
                    base.playSfx(self.countSound)

                self.scorePanels[scorePanelIndex].setScore(newScore)
                self.scores[scorePanelIndex] = newScore

            duration = (destScore -
                        startScore) * IceGameGlobals.ScoreCountUpRate
            tireNp = self.tireDict[avId]['tireNodePath']
            self.scoreMovie.append(
                Parallel(
                    LerpFunctionInterval(increaseScores, duration),
                    Sequence(
                        LerpColorScaleInterval(tireNp, duration / 6.0,
                                               VBase4(1, 0, 0, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0,
                                               VBase4(1, 1, 1, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0,
                                               VBase4(1, 0, 0, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0,
                                               VBase4(1, 1, 1, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0,
                                               VBase4(1, 0, 0, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0,
                                               VBase4(1, 1, 1, 1)))))
            curScale += distance

        self.scoreMovie.append(
            Func(self.sendUpdate, 'reportScoringMovieDone', []))
        self.scoreMovie.start()

    def exitScoring(self):
        self.scoreMovie.finish()
        self.scoreMovie = None
        self.scoreCircle.hide()

    def enterFinalResults(self):
        lerpTrack = Parallel()
        lerpDur = 0.5
        tY = 0.59999999999999998
        bY = -0.050000000000000003
        lX = -0.5
        cX = 0
        rX = 0.5
        scorePanelLocs = (((cX, bY), ), ((lX, bY), (rX, bY)),
                          ((cX, tY), (lX, bY), (rX, bY)), ((lX, tY), (rX, tY),
                                                           (lX, bY), (rX, bY)))
        scorePanelLocs = scorePanelLocs[self.numPlayers - 1]
        for i in xrange(self.numPlayers):
            panel = self.scorePanels[i]
            pos = scorePanelLocs[i]
            lerpTrack.append(
                Parallel(
                    LerpPosInterval(panel,
                                    lerpDur,
                                    Point3(pos[0], 0, pos[1]),
                                    blendType='easeInOut'),
                    LerpScaleInterval(panel,
                                      lerpDur,
                                      Vec3(panel.getScale()) * 2.0,
                                      blendType='easeInOut')))

        self.showScoreTrack = Parallel(
            lerpTrack,
            Sequence(Wait(IceGameGlobals.ShowScoresDuration),
                     Func(self.gameOver)))
        self.showScoreTrack.start()

    def exitFinalResults(self):
        self.showScoreTrack.pause()
        del self.showScoreTrack

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        if base.localAvatar.laffMeter:
            base.localAvatar.laffMeter.start()

    def exitCleanup(self):
        pass

    def _DistributedIceGame__placeToon(self, avId):
        toon = self.getAvatar(avId)
        if toon:
            toon.setPos(0, 0, 0)
            toon.setHpr(0, 0, 0)

    def moveCameraToTop(self):
        camera.reparentTo(render)
        p = self.cameraThreeQuarterView
        camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5])

    def setupTire(self, avId, index):
        (tireNp, tireBody, tireOdeGeom) = self.createTire(index)
        self.tireDict[avId] = {
            'tireNodePath': tireNp,
            'tireBody': tireBody,
            'tireOdeGeom': tireOdeGeom
        }
        if avId <= 0:
            tireBlocker = tireNp.find('**/tireblockermesh')
            if not tireBlocker.isEmpty():
                tireBlocker.hide()

        if avId == self.localAvId:
            tireNp = self.tireDict[avId]['tireNodePath']
            self.treasureSphereName = 'treasureCollider'
            self.treasureCollSphere = CollisionSphere(
                0, 0, 0, IceGameGlobals.TireRadius)
            self.treasureCollSphere.setTangible(0)
            self.treasureCollNode = CollisionNode(self.treasureSphereName)
            self.treasureCollNode.setFromCollideMask(
                ToontownGlobals.PieBitmask)
            self.treasureCollNode.addSolid(self.treasureCollSphere)
            self.treasureCollNodePath = tireNp.attachNewNode(
                self.treasureCollNode)
            self.treasureHandler = CollisionHandlerEvent()
            self.treasureHandler.addInPattern('%fn-intoTreasure')
            base.cTrav.addCollider(self.treasureCollNodePath,
                                   self.treasureHandler)
            eventName = '%s-intoTreasure' % self.treasureCollNodePath.getName()
            self.notify.debug('eventName = %s' % eventName)
            self.accept(eventName, self.toonHitSomething)

    def setupForceArrow(self, avId):
        arrow = loader.loadModel('phase_4/models/minigames/ice_game_arrow')
        priority = 0
        if avId < 0:
            priority = -avId
        else:
            priority = self.avIdList.index(avId)
            if avId == self.localAvId:
                priority = 10

        self.forceArrowDict[avId] = arrow

    def hideForceArrows(self):
        for forceArrow in self.forceArrowDict.values():
            forceArrow.hide()

    def showForceArrows(self, realPlayersOnly=True):
        for avId in self.forceArrowDict:
            if realPlayersOnly:
                if avId > 0:
                    self.forceArrowDict[avId].show()
                else:
                    self.forceArrowDict[avId].hide()
            avId > 0
            self.forceArrowDict[avId].show()

    def localForceArrow(self):
        if self.localAvId in self.forceArrowDict:
            return self.forceArrowDict[self.localAvId]
        else:
            return None

    def setChoices(self, input0, input1, input2, input3):
        pass

    def startDebugTask(self):
        taskMgr.add(self.debugTask, self.debugTaskName)

    def stopDebugTask(self):
        taskMgr.remove(self.debugTaskName)

    def debugTask(self, task):
        if self.canDrive and self.tireDict.has_key(localAvatar.doId):
            dt = globalClock.getDt()
            forceMove = 25000
            forceMoveDt = forceMove
            tireBody = self.tireDict[localAvatar.doId]['tireBody']
            if self.arrowKeys.upPressed() and not tireBody.isEnabled():
                x = 0
                y = 1
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))

            if self.arrowKeys.downPressed() and not tireBody.isEnabled():
                x = 0
                y = -1
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))

            if self.arrowKeys.leftPressed() and not tireBody.isEnabled():
                x = -1
                y = 0
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))

            if self.arrowKeys.rightPressed() and not tireBody.isEnabled():
                x = 1
                y = 0
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))

        return task.cont

    def _DistributedIceGame__upArrowPressed(self):
        pass

    def _DistributedIceGame__downArrowPressed(self):
        pass

    def _DistributedIceGame__leftArrowPressed(self):
        pass

    def _DistributedIceGame__rightArrowPressed(self):
        pass

    def _DistributedIceGame__controlPressed(self):
        if self.gameFSM.getCurrentState().getName() == 'inputChoice':
            self.sendForceArrowUpdateAsap = True
            self.updateLocalForceArrow()
            self.controlKeyPressed = True
            self.sendUpdate('setAvatarChoice',
                            [self.curForce, self.curHeading])
            self.gameFSM.request('waitServerChoices')

    def startTimer(self):
        now = globalClock.getFrameTime()
        elapsed = now - self.timerStartTime
        self.timer.posInTopRightCorner()
        self.timer.setTime(IceGameGlobals.InputTimeout)
        self.timer.countdown(IceGameGlobals.InputTimeout - elapsed,
                             self.handleChoiceTimeout)
        self.timer.show()

    def setTimerStartTime(self, timestamp):
        if not self.hasLocalToon:
            return None

        self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp)
        if self.timer != None:
            self.startTimer()

    def handleChoiceTimeout(self):
        self.sendUpdate('setAvatarChoice', [0, 0])
        self.gameFSM.request('waitServerChoices')

    def localTireNp(self):
        ret = None
        if self.localAvId in self.tireDict:
            ret = self.tireDict[self.localAvId]['tireNodePath']

        return ret

    def localTireBody(self):
        ret = None
        if self.localAvId in self.tireDict:
            ret = self.tireDict[self.localAvId]['tireBody']

        return ret

    def getTireBody(self, avId):
        ret = None
        if avId in self.tireDict:
            ret = self.tireDict[avId]['tireBody']

        return ret

    def getTireNp(self, avId):
        ret = None
        if avId in self.tireDict:
            ret = self.tireDict[avId]['tireNodePath']

        return ret

    def updateForceArrow(self, avId, curHeading, curForce):
        forceArrow = self.forceArrowDict[avId]
        tireNp = self.tireDict[avId]['tireNodePath']
        tireNp.setH(curHeading)
        tireBody = self.tireDict[avId]['tireBody']
        tireBody.setQuaternion(tireNp.getQuat())
        self.notify.debug('curHeading = %s' % curHeading)
        yScale = curForce / 100.0
        yScale *= 1
        headY = yScale * 15
        xScale = (yScale - 1) / 2.0 + 1.0
        shaft = forceArrow.find('**/arrow_shaft')
        head = forceArrow.find('**/arrow_head')
        shaft.setScale(xScale, yScale, 1)
        head.setPos(0, headY, 0)
        head.setScale(xScale, xScale, 1)

    def updateLocalForceArrow(self):
        avId = self.localAvId
        self.b_setForceArrowInfo(avId, self.curHeading, self.curForce)

    def _DistributedIceGame__aimTask(self, task):
        if not hasattr(self, 'arrowKeys'):
            return task.done

        dt = globalClock.getDt()
        headingMomentumChange = dt * 60.0
        forceMomentumChange = dt * 160.0
        arrowUpdate = False
        arrowRotating = False
        arrowUp = False
        arrowDown = False
        if self.arrowKeys.upPressed() and not self.arrowKeys.downPressed():
            self.forceMomentum += forceMomentumChange
            if self.forceMomentum < 0:
                self.forceMomentum = 0

            if self.forceMomentum > 50:
                self.forceMomentum = 50

            oldForce = self.curForce
            self.curForce += self.forceMomentum * dt
            arrowUpdate = True
            if oldForce < self.MaxLocalForce:
                arrowUp = True

        elif self.arrowKeys.downPressed() and not self.arrowKeys.upPressed():
            self.forceMomentum += forceMomentumChange
            if self.forceMomentum < 0:
                self.forceMomentum = 0

            if self.forceMomentum > 50:
                self.forceMomentum = 50

            oldForce = self.curForce
            self.curForce -= self.forceMomentum * dt
            arrowUpdate = True
            if oldForce > 0.01:
                arrowDown = True

        else:
            self.forceMomentum = 0
        if self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed():
            self.headingMomentum += headingMomentumChange
            if self.headingMomentum < 0:
                self.headingMomentum = 0

            if self.headingMomentum > 50:
                self.headingMomentum = 50

            self.curHeading += self.headingMomentum * dt
            arrowUpdate = True
            arrowRotating = True
        elif self.arrowKeys.rightPressed(
        ) and not self.arrowKeys.leftPressed():
            self.headingMomentum += headingMomentumChange
            if self.headingMomentum < 0:
                self.headingMomentum = 0

            if self.headingMomentum > 50:
                self.headingMomentum = 50

            self.curHeading -= self.headingMomentum * dt
            arrowUpdate = True
            arrowRotating = True
        else:
            self.headingMomentum = 0
        if arrowUpdate:
            self.normalizeHeadingAndForce()
            self.updateLocalForceArrow()

        if arrowRotating:
            if not self.arrowRotateSound.status(
            ) == self.arrowRotateSound.PLAYING:
                base.playSfx(self.arrowRotateSound, looping=True)

        else:
            self.arrowRotateSound.stop()
        if arrowUp:
            if not self.arrowUpSound.status() == self.arrowUpSound.PLAYING:
                base.playSfx(self.arrowUpSound, looping=False)

        else:
            self.arrowUpSound.stop()
        if arrowDown:
            if not self.arrowDownSound.status() == self.arrowDownSound.PLAYING:
                base.playSfx(self.arrowDownSound, looping=False)

        else:
            self.arrowDownSound.stop()
        return task.cont

    def normalizeHeadingAndForce(self):
        if self.curForce > self.MaxLocalForce:
            self.curForce = self.MaxLocalForce

        if self.curForce < 0.01:
            self.curForce = 0.01

    def setTireInputs(self, tireInputs):
        if not self.hasLocalToon:
            return None

        self.allTireInputs = tireInputs
        self.gameFSM.request('moveTires')

    def enableAllTireBodies(self):
        for avId in self.tireDict.keys():
            self.tireDict[avId]['tireBody'].enable()

    def disableAllTireBodies(self):
        for avId in self.tireDict.keys():
            self.tireDict[avId]['tireBody'].disable()

    def areAllTiresDisabled(self):
        for avId in self.tireDict.keys():
            if self.tireDict[avId]['tireBody'].isEnabled():
                return False
                continue

        return True

    def _DistributedIceGame__moveTiresTask(self, task):
        if self.areAllTiresDisabled():
            self.sendTirePositions()
            self.gameFSM.request('synch')
            return task.done

        return task.cont

    def sendTirePositions(self):
        tirePositions = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            tire = self.getTireBody(avId)
            pos = Point3(tire.getPosition())
            tirePositions.append([pos[0], pos[1], pos[2]])

        for index in xrange(len(self.avIdList), 4):
            avId = -index
            tire = self.getTireBody(avId)
            pos = Point3(tire.getPosition())
            tirePositions.append([pos[0], pos[1], pos[2]])

        self.sendUpdate('endingPositions', [tirePositions])

    def setFinalPositions(self, finalPos):
        if not self.hasLocalToon:
            return None

        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            tire = self.getTireBody(avId)
            np = self.getTireNp(avId)
            pos = finalPos[index]
            tire.setPosition(pos[0], pos[1], pos[2])
            np.setPos(pos[0], pos[1], pos[2])

        for index in xrange(len(self.avIdList), 4):
            avId = -index
            tire = self.getTireBody(avId)
            np = self.getTireNp(avId)
            pos = finalPos[index]
            tire.setPosition(pos[0], pos[1], pos[2])
            np.setPos(pos[0], pos[1], pos[2])

    def updateInfoLabel(self):
        self.infoLabel['text'] = TTLocalizer.IceGameInfo % {
            'curMatch': self.curMatch + 1,
            'numMatch': IceGameGlobals.NumMatches,
            'curRound': self.curRound + 1,
            'numRound': IceGameGlobals.NumRounds
        }

    def setMatchAndRound(self, match, round):
        if not self.hasLocalToon:
            return None

        self.curMatch = match
        self.curRound = round
        self.updateInfoLabel()

    def setScores(self, match, round, scores):
        if not self.hasLocalToon:
            return None

        self.newMatch = match
        self.newRound = round
        self.newScores = scores

    def setNewState(self, state):
        if not self.hasLocalToon:
            return None

        self.notify.debug('setNewState gameFSM=%s newState=%s' %
                          (self.gameFSM, state))
        self.gameFSM.request(state)

    def putAllTiresInStartingPositions(self):
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            np = self.tireDict[avId]['tireNodePath']
            np.setPos(IceGameGlobals.StartingPositions[index])
            self.notify.debug('avId=%s newPos=%s' % (avId, np.getPos))
            np.setHpr(0, 0, 0)
            quat = np.getQuat()
            body = self.tireDict[avId]['tireBody']
            body.setPosition(IceGameGlobals.StartingPositions[index])
            body.setQuaternion(quat)

        for index in xrange(len(self.avIdList), 4):
            avId = -index
            np = self.tireDict[avId]['tireNodePath']
            np.setPos(IceGameGlobals.StartingPositions[index])
            self.notify.debug('avId=%s newPos=%s' % (avId, np.getPos))
            np.setHpr(0, 0, 0)
            quat = np.getQuat()
            body = self.tireDict[avId]['tireBody']
            body.setPosition(IceGameGlobals.StartingPositions[index])
            body.setQuaternion(quat)

    def b_setForceArrowInfo(self, avId, force, heading):
        self.setForceArrowInfo(avId, force, heading)
        self.d_setForceArrowInfo(avId, force, heading)

    def d_setForceArrowInfo(self, avId, force, heading):
        sendIt = False
        curTime = self.getCurrentGameTime()
        if self.sendForceArrowUpdateAsap:
            sendIt = True
        elif curTime - self.lastForceArrowUpdateTime > 0.20000000000000001:
            sendIt = True

        if sendIt:
            self.sendUpdate('setForceArrowInfo', [avId, force, heading])
            self.sendForceArrowUpdateAsap = False
            self.lastForceArrowUpdateTime = self.getCurrentGameTime()

    def setForceArrowInfo(self, avId, force, heading):
        if not self.hasLocalToon:
            return None

        self.updateForceArrow(avId, force, heading)

    def setupStartOfMatch(self):
        self.putAllTiresInStartingPositions()
        szId = self.getSafezoneId()
        self.numTreasures = IceGameGlobals.NumTreasures[szId]
        if self.treasures:
            for treasure in self.treasures:
                treasure.destroy()

            self.treasures = []

        index = 0
        treasureMargin = IceGameGlobals.TireRadius + 1.0
        while len(self.treasures) < self.numTreasures:
            xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5,
                                               IceGameGlobals.MaxWall[0] - 5)
            yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5,
                                               IceGameGlobals.MaxWall[1] - 5)
            self.notify.debug('yPos=%s' % yPos)
            pos = Point3(xPos, yPos, IceGameGlobals.TireRadius)
            newTreasure = IceTreasure.IceTreasure(self.treasureModel,
                                                  pos,
                                                  index,
                                                  self.doId,
                                                  penalty=False)
            goodSpot = True
            for obstacle in self.obstacles:
                if newTreasure.nodePath.getDistance(obstacle) < treasureMargin:
                    goodSpot = False
                    break
                    continue

            if goodSpot:
                for treasure in self.treasures:
                    if newTreasure.nodePath.getDistance(
                            treasure.nodePath) < treasureMargin:
                        goodSpot = False
                        break
                        continue

            if goodSpot:
                self.treasures.append(newTreasure)
                index += 1
                continue
            newTreasure.destroy()
        self.numPenalties = IceGameGlobals.NumPenalties[szId]
        if self.penalties:
            for penalty in self.penalties:
                penalty.destroy()

            self.penalties = []

        index = 0
        while len(self.penalties) < self.numPenalties:
            xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5,
                                               IceGameGlobals.MaxWall[0] - 5)
            yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5,
                                               IceGameGlobals.MaxWall[1] - 5)
            self.notify.debug('yPos=%s' % yPos)
            pos = Point3(xPos, yPos, IceGameGlobals.TireRadius)
            newPenalty = IceTreasure.IceTreasure(self.penaltyModel,
                                                 pos,
                                                 index,
                                                 self.doId,
                                                 penalty=True)
            goodSpot = True
            for obstacle in self.obstacles:
                if newPenalty.nodePath.getDistance(obstacle) < treasureMargin:
                    goodSpot = False
                    break
                    continue

            if goodSpot:
                for treasure in self.treasures:
                    if newPenalty.nodePath.getDistance(
                            treasure.nodePath) < treasureMargin:
                        goodSpot = False
                        break
                        continue

            if goodSpot:
                for penalty in self.penalties:
                    if newPenalty.nodePath.getDistance(
                            penalty.nodePath) < treasureMargin:
                        goodSpot = False
                        break
                        continue

            if goodSpot:
                self.penalties.append(newPenalty)
                index += 1
                continue
            newPenalty.destroy()

    def toonHitSomething(self, entry):
        self.notify.debug('---- treasure Enter ---- ')
        self.notify.debug('%s' % entry)
        name = entry.getIntoNodePath().getName()
        parts = name.split('-')
        if len(parts) < 3:
            self.notify.debug('collided with %s, but returning' % name)
            return None

        if not int(parts[1]) == self.doId:
            self.notify.debug("collided with %s, but doId doesn't match" %
                              name)
            return None

        treasureNum = int(parts[2])
        if 'penalty' in parts[0]:
            self._DistributedIceGame__penaltyGrabbed(treasureNum)
        else:
            self._DistributedIceGame__treasureGrabbed(treasureNum)

    def _DistributedIceGame__treasureGrabbed(self, treasureNum):
        self.treasures[treasureNum].showGrab()
        self.treasureGrabSound.play()
        self.sendUpdate('claimTreasure', [treasureNum])

    def setTreasureGrabbed(self, avId, treasureNum):
        if not self.hasLocalToon:
            return None

        self.notify.debug('treasure %s grabbed by %s' % (treasureNum, avId))
        if avId != self.localAvId:
            self.treasures[treasureNum].showGrab()

        i = self.avIdList.index(avId)
        self.scores[i] += 1
        self.scorePanels[i].setScore(self.scores[i])

    def _DistributedIceGame__penaltyGrabbed(self, penaltyNum):
        self.penalties[penaltyNum].showGrab()
        self.sendUpdate('claimPenalty', [penaltyNum])

    def setPenaltyGrabbed(self, avId, penaltyNum):
        if not self.hasLocalToon:
            return None

        self.notify.debug('penalty %s grabbed by %s' % (penaltyNum, avId))
        if avId != self.localAvId:
            self.penalties[penaltyNum].showGrab()

        i = self.avIdList.index(avId)
        self.scores[i] -= 1
        self.scorePanels[i].setScore(self.scores[i])

    def postStep(self):
        DistributedIceWorld.DistributedIceWorld.postStep(self)
        for count in range(self.colCount):
            (c0, c1) = self.getOrderedContacts(count)
            if c1 in self.tireCollideIds:
                tireIndex = self.tireCollideIds.index(c1)
                if c0 in self.tireCollideIds:
                    self.tireSounds[tireIndex]['tireHit'].play()
                elif c0 == self.wallCollideId:
                    self.tireSounds[tireIndex]['wallHit'].play()
                elif c0 == self.obstacleCollideId:
                    self.tireSounds[tireIndex]['obstacleHit'].play()

            c0 in self.tireCollideIds

    def forceLocalToonToTire(self):
        toon = localAvatar
        if toon and self.localAvId in self.tireDict:
            tireNp = self.tireDict[self.localAvId]['tireNodePath']
            toon.reparentTo(tireNp)
            toon.setPosHpr(0, 0, 0, 0, 0, 0)
            toon.setY(1.0)
            toon.setZ(-3)
class TestCutscene:
    def __init__(self, dcs):
        self.dcs = dcs
        self.ival = None

    def run(self):

        boss = base.bspLoader.getPyEntityByTargetName("groom_boss_suit")
        boss.show()
        boss.cleanupPropeller()
        boss.animFSM.request('neutral')
        camera.reparentTo(boss)

        onBtn = base.bspLoader.getPyEntityByTargetName("powerovrd_on_button")

        # press animation (fingerwag): 1-31 32-1

        buttonPos = (-1105 / 16.0, 2683 / 16.0, 76 / 16.0)
        camBtnPosHpr = (-1027 / 16.0, 2638 / 16.0, 96 / 16.0, 135 - 90, 30, 0)
        camBtnPosHpr2 = (-1009 / 16.0, 2764 / 16.0, 169 / 16.0, 232 - 90, 0, 0)

        def __pingpongboss_speak():
            boss.pingpong("speak", fromFrame=95, toFrame=100)

        def __boss_pressseq():
            return Sequence(
                ActorInterval(boss, "fingerwag", startFrame=1, endFrame=31),
                Func(onBtn.d_requestPress),
                ActorInterval(boss, "fingerwag", startFrame=32, endFrame=1))

        def __localavplay_duck():
            base.localAvatar.setAnimState('off')
            base.localAvatar.play("duck", fromFrame=19)

        numGoons = 6

        goonWakeup = Parallel()
        for i in xrange(numGoons):
            delay = random.uniform(0.0, 0.5)
            goon = base.bspLoader.getPyEntityByTargetName(
                "groom_goon_{0}".format(i))
            goonWakeup.append(Sequence(Wait(delay), Func(goon.wakeup)))

        bdoor = base.bspLoader.getPyEntityByTargetName("to_groom_botdoor")
        tdoor = base.bspLoader.getPyEntityByTargetName("to_groom_topdoor")

        oldHpr = boss.getHpr(render)

        bossTrack = Parallel(
            Sequence(
                Wait(10.0),
                Func(boss.setHpr, oldHpr),
                ActorInterval(boss,
                              "speak",
                              startFrame=55,
                              endFrame=105,
                              playRate=1.5),
                #Func(__pingpongboss_speak),
                Wait(2.0),
                Func(boss.loop, "neutral")),
            Sequence(
                Func(boss.play, "speak"),
                Func(boss.setChat,
                     "What's this?! How did a Toon get down here?"), Wait(2.5),
                Func(
                    boss.setChat,
                    "The only place you'll be going is back to the playground."
                ), Func(boss.loop, "walk"), Func(boss.headsUp, *buttonPos),
                LerpPosInterval(boss, 2.5, buttonPos), __boss_pressseq(),
                Wait(2.5), Func(boss.setChat, "Goons, ATTACK!!!")))
        camTrack = Sequence(
            Func(camera.setPos, 2.5, 14, 7), Func(camera.lookAt, boss, 0, 0,
                                                  4), Wait(2.5),
            Func(camera.reparentTo, render),
            Func(camera.setPosHpr, *camBtnPosHpr), Wait(2.0),
            Func(camera.setPosHpr, *camBtnPosHpr2), Wait(0.5), Wait(3.0),
            Func(camera.setPosHpr, 4.351, 126.686, 5.5, -154.16, 16.11, 0),
            Wait(0.3), Func(bdoor.request, 'Closing'),
            Func(tdoor.request, 'Closing'), Func(__localavplay_duck),
            Wait(0.9), Func(base.doCamShake, 0.5, 0.35), Wait(0.9),
            Func(camera.reparentTo, boss), Func(camera.setPos, 2.5, 17, 7),
            Func(camera.lookAt, boss, 0, 0, 4),
            LerpPosHprInterval(camera,
                               duration=0.75,
                               blendType='easeOut',
                               pos=(1, 5, 4),
                               hpr=(165, 20, 0)), Wait(1.75),
            Func(camera.reparentTo, render),
            Func(camera.setPosHpr, 88 / 16.0, 2746 / 16.0, 127 / 16.0, 71 - 90,
                 -10, 0))
        goonTrack = Sequence(Wait(12.6), goonWakeup)

        self.ival = Parallel(camTrack, bossTrack, goonTrack)
        self.ival.start()

    def stop(self):
        if self.ival:
            self.ival.pause()
            self.ival = None

    def cleanup(self):
        self.dcs = None
class DistributedIceGame(DistributedMinigame.DistributedMinigame, DistributedIceWorld.DistributedIceWorld):
    notify = directNotify.newCategory("DistributedIceGame")
    MaxLocalForce = 100
    MaxPhysicsForce = 25000

    def __init__(self, cr):
        DistributedMinigame.DistributedMinigame.__init__(self, cr)
        DistributedIceWorld.DistributedIceWorld.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM(
            "DistributedIceGame",
            [
                State.State("off", self.enterOff, self.exitOff, ["inputChoice"]),
                State.State(
                    "inputChoice",
                    self.enterInputChoice,
                    self.exitInputChoice,
                    ["waitServerChoices", "moveTires", "displayVotes", "cleanup"],
                ),
                State.State(
                    "waitServerChoices",
                    self.enterWaitServerChoices,
                    self.exitWaitServerChoices,
                    ["moveTires", "cleanup"],
                ),
                State.State("moveTires", self.enterMoveTires, self.exitMoveTires, ["synch", "cleanup"]),
                State.State("synch", self.enterSynch, self.exitSynch, ["inputChoice", "scoring", "cleanup"]),
                State.State("scoring", self.enterScoring, self.exitScoring, ["cleanup", "finalResults", "inputChoice"]),
                State.State("finalResults", self.enterFinalResults, self.exitFinalResults, ["cleanup"]),
                State.State("cleanup", self.enterCleanup, self.exitCleanup, []),
            ],
            "off",
            "cleanup",
        )
        self.addChildGameFSM(self.gameFSM)
        self.cameraThreeQuarterView = (0, -22, 45, 0, -62.89, 0)
        self.tireDict = {}
        self.forceArrowDict = {}
        self.canDrive = False
        self.timer = None
        self.timerStartTime = None
        self.curForce = 0
        self.curHeading = 0
        self.headingMomentum = 0.0
        self.forceMomentum = 0.0
        self.allTireInputs = None
        self.curRound = 0
        self.curMatch = 0
        self.controlKeyWarningLabel = DirectLabel(
            text=TTLocalizer.IceGameControlKeyWarning,
            text_fg=VBase4(1, 0, 0, 1),
            relief=None,
            pos=(0.0, 0, 0),
            scale=0.15,
        )
        self.controlKeyWarningLabel.hide()
        self.waitingMoveLabel = DirectLabel(
            text=TTLocalizer.IceGameWaitingForPlayersToFinishMove,
            text_fg=VBase4(1, 1, 1, 1),
            relief=None,
            pos=(-0.6, 0, -0.75),
            scale=0.075,
        )
        self.waitingMoveLabel.hide()
        self.waitingSyncLabel = DirectLabel(
            text=TTLocalizer.IceGameWaitingForAISync,
            text_fg=VBase4(1, 1, 1, 1),
            relief=None,
            pos=(-0.6, 0, -0.75),
            scale=0.075,
        )
        self.waitingSyncLabel.hide()
        self.infoLabel = DirectLabel(text="", text_fg=VBase4(0, 0, 0, 1), relief=None, pos=(0.0, 0, 0.7), scale=0.075)
        self.updateInfoLabel()
        self.lastForceArrowUpdateTime = 0
        self.sendForceArrowUpdateAsap = False
        self.treasures = []
        self.penalties = []
        self.obstacles = []
        self.controlKeyPressed = False
        self.controlKeyWarningIval = None
        return

    def delete(self):
        DistributedIceWorld.DistributedIceWorld.delete(self)
        DistributedMinigame.DistributedMinigame.delete(self)
        if self.controlKeyWarningIval:
            self.controlKeyWarningIval.finish()
            self.controlKeyWarningIval = None
        self.controlKeyWarningLabel.destroy()
        del self.controlKeyWarningLabel
        self.waitingMoveLabel.destroy()
        del self.waitingMoveLabel
        self.waitingSyncLabel.destroy()
        del self.waitingSyncLabel
        self.infoLabel.destroy()
        del self.infoLabel
        for treasure in self.treasures:
            treasure.destroy()

        del self.treasures
        for penalty in self.penalties:
            penalty.destroy()

        del self.penalties
        for obstacle in self.obstacles:
            obstacle.removeNode()

        del self.obstacles
        del self.gameFSM
        return

    def announceGenerate(self):
        DistributedMinigame.DistributedMinigame.announceGenerate(self)
        DistributedIceWorld.DistributedIceWorld.announceGenerate(self)
        self.debugTaskName = self.uniqueName("debugTask")

    def getTitle(self):
        return TTLocalizer.IceGameTitle

    def getInstructions(self):
        szId = self.getSafezoneId()
        numPenalties = IceGameGlobals.NumPenalties[szId]
        result = TTLocalizer.IceGameInstructions
        if numPenalties == 0:
            result = TTLocalizer.IceGameInstructionsNoTnt
        return result

    def getMaxDuration(self):
        return 0

    def load(self):
        self.notify.debug("load")
        DistributedMinigame.DistributedMinigame.load(self)
        self.music = base.loadMusic("phase_4/audio/bgm/MG_IceGame.ogg")
        self.gameBoard = loader.loadModel("phase_4/models/minigames/ice_game_icerink")
        background = loader.loadModel("phase_4/models/minigames/ice_game_2d")
        backgroundWide = loader.loadModel("phase_4/models/minigames/iceslide_ground")
        background.reparentTo(self.gameBoard)
        backgroundWide.reparentTo(self.gameBoard)
        backgroundWide.setPos(0, -0.3, -0.5)
        self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0)
        self.gameBoard.setScale(1.0)
        self.setupSimulation()
        index = 0
        for avId in self.avIdList:
            self.setupTire(avId, index)
            self.setupForceArrow(avId)
            index += 1

        for index in xrange(len(self.avIdList), 4):
            self.setupTire(-index, index)
            self.setupForceArrow(-index)

        self.showForceArrows(realPlayersOnly=True)
        self.westWallModel = NodePath()
        if not self.westWallModel.isEmpty():
            self.westWallModel.reparentTo(self.gameBoard)
            self.westWallModel.setPos(IceGameGlobals.MinWall[0], IceGameGlobals.MinWall[1], 0)
            self.westWallModel.setScale(4)
        self.eastWallModel = NodePath()
        if not self.eastWallModel.isEmpty():
            self.eastWallModel.reparentTo(self.gameBoard)
            self.eastWallModel.setPos(IceGameGlobals.MaxWall[0], IceGameGlobals.MaxWall[1], 0)
            self.eastWallModel.setScale(4)
            self.eastWallModel.setH(180)
        self.arrowKeys = ArrowKeys.ArrowKeys()
        self.target = loader.loadModel("phase_3/models/misc/sphere")
        self.target.setScale(0.01)
        self.target.reparentTo(self.gameBoard)
        self.target.setPos(0, 0, 0)
        self.scoreCircle = loader.loadModel("phase_4/models/minigames/ice_game_score_circle")
        self.scoreCircle.setScale(0.01)
        self.scoreCircle.reparentTo(self.gameBoard)
        self.scoreCircle.setZ(IceGameGlobals.TireRadius / 2.0)
        self.scoreCircle.setAlphaScale(0.5)
        self.scoreCircle.setTransparency(1)
        self.scoreCircle.hide()
        self.treasureModel = loader.loadModel("phase_4/models/minigames/ice_game_barrel")
        self.penaltyModel = loader.loadModel("phase_4/models/minigames/ice_game_tnt2")
        self.penaltyModel.setScale(0.75, 0.75, 0.7)
        szId = self.getSafezoneId()
        obstacles = IceGameGlobals.Obstacles[szId]
        index = 0
        cubicObstacle = IceGameGlobals.ObstacleShapes[szId]
        for pos in obstacles:
            newPos = Point3(pos[0], pos[1], IceGameGlobals.TireRadius)
            newObstacle = self.createObstacle(newPos, index, cubicObstacle)
            self.obstacles.append(newObstacle)
            index += 1

        self.countSound = loader.loadSfx("phase_3.5/audio/sfx/tick_counter.ogg")
        self.treasureGrabSound = loader.loadSfx("phase_4/audio/sfx/MG_sfx_vine_game_bananas.ogg")
        self.penaltyGrabSound = loader.loadSfx("phase_4/audio/sfx/MG_cannon_fire_alt.ogg")
        self.tireSounds = []
        for tireIndex in xrange(4):
            tireHit = loader.loadSfx("phase_4/audio/sfx/Golf_Hit_Barrier_1.ogg")
            wallHit = loader.loadSfx("phase_4/audio/sfx/MG_maze_pickup.ogg")
            obstacleHit = loader.loadSfx("phase_4/audio/sfx/Golf_Hit_Barrier_2.ogg")
            self.tireSounds.append({"tireHit": tireHit, "wallHit": wallHit, "obstacleHit": obstacleHit})

        self.arrowRotateSound = loader.loadSfx("phase_4/audio/sfx/MG_sfx_ice_force_rotate.ogg")
        self.arrowUpSound = loader.loadSfx("phase_4/audio/sfx/MG_sfx_ice_force_increase_3sec.ogg")
        self.arrowDownSound = loader.loadSfx("phase_4/audio/sfx/MG_sfx_ice_force_decrease_3sec.ogg")
        self.scoreCircleSound = loader.loadSfx("phase_4/audio/sfx/MG_sfx_ice_scoring_1.ogg")

    def unload(self):
        self.notify.debug("unload")
        DistributedMinigame.DistributedMinigame.unload(self)
        del self.music
        self.gameBoard.removeNode()
        del self.gameBoard
        for forceArrow in self.forceArrowDict.values():
            forceArrow.removeNode()

        del self.forceArrowDict
        self.scoreCircle.removeNode()
        del self.scoreCircle
        del self.countSound

    def onstage(self):
        self.notify.debug("onstage")
        DistributedMinigame.DistributedMinigame.onstage(self)
        self.gameBoard.reparentTo(render)
        self.__placeToon(self.localAvId)
        self.moveCameraToTop()
        self.scorePanels = []
        base.playMusic(self.music, looping=1, volume=0.8)

    def offstage(self):
        self.notify.debug("offstage")
        self.music.stop()
        self.gameBoard.hide()
        self.infoLabel.hide()
        for avId in self.tireDict:
            self.tireDict[avId]["tireNodePath"].hide()

        for panel in self.scorePanels:
            panel.cleanup()

        del self.scorePanels
        for obstacle in self.obstacles:
            obstacle.hide()

        for treasure in self.treasures:
            treasure.nodePath.hide()

        for penalty in self.penalties:
            penalty.nodePath.hide()

        for avId in self.avIdList:
            av = self.getAvatar(avId)
            if av:
                av.dropShadow.show()
                av.resetLOD()

        taskMgr.remove(self.uniqueName("aimtask"))
        self.arrowKeys.destroy()
        del self.arrowKeys
        DistributedMinigame.DistributedMinigame.offstage(self)

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

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug("setGameReady")
        if DistributedMinigame.DistributedMinigame.setGameReady(self):
            return
        for index in xrange(self.numPlayers):
            avId = self.avIdList[index]
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                self.__placeToon(avId)
                toon.forwardSpeed = 0
                toon.rotateSpeed = False
                toon.dropShadow.hide()
                toon.setAnimState("Sit")
                if avId in self.tireDict:
                    tireNp = self.tireDict[avId]["tireNodePath"]
                    toon.reparentTo(tireNp)
                    toon.setY(1.0)
                    toon.setZ(-3)
                toon.startLookAround()

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug("setGameStart")
        DistributedMinigame.DistributedMinigame.setGameStart(self, timestamp)
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.stopLookAround()

        self.scores = [0] * self.numPlayers
        spacing = 0.4
        for i in xrange(self.numPlayers):
            avId = self.avIdList[i]
            avName = self.getAvatarName(avId)
            scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName)
            scorePanel.setScale(0.9)
            scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15)
            scorePanel.reparentTo(base.a2dTopRight)
            scorePanel.makeTransparent(0.75)
            self.scorePanels.append(scorePanel)

        self.arrowKeys.setPressHandlers(
            [
                self.__upArrowPressed,
                self.__downArrowPressed,
                self.__leftArrowPressed,
                self.__rightArrowPressed,
                self.__controlPressed,
            ]
        )

    def isInPlayState(self):
        if not self.gameFSM.getCurrentState():
            return False
        if not self.gameFSM.getCurrentState().getName() == "play":
            return False
        return True

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

    def exitOff(self):
        pass

    def enterInputChoice(self):
        self.notify.debug("enterInputChoice")
        self.forceLocalToonToTire()
        self.controlKeyPressed = False
        if self.curRound == 0:
            self.setupStartOfMatch()
        else:
            self.notify.debug("self.curRound = %s" % self.curRound)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.hide()
        if self.timerStartTime != None:
            self.startTimer()
        self.showForceArrows(realPlayersOnly=True)
        self.localForceArrow().setPosHpr(0, 0, -1.0, 0, 0, 0)
        self.localForceArrow().reparentTo(self.localTireNp())
        self.localForceArrow().setY(IceGameGlobals.TireRadius)
        self.localTireNp().headsUp(self.target)
        self.notify.debug("self.localForceArrow() heading = %s" % self.localForceArrow().getH())
        self.curHeading = self.localTireNp().getH()
        self.curForce = 25
        self.updateLocalForceArrow()
        for avId in self.forceArrowDict:
            forceArrow = self.forceArrowDict[avId]
            forceArrow.setPosHpr(0, 0, -1.0, 0, 0, 0)
            tireNp = self.tireDict[avId]["tireNodePath"]
            forceArrow.reparentTo(tireNp)
            forceArrow.setY(IceGameGlobals.TireRadius)
            tireNp.headsUp(self.target)
            self.updateForceArrow(avId, tireNp.getH(), 25)

        taskMgr.add(self.__aimTask, self.uniqueName("aimtask"))
        if base.localAvatar.laffMeter:
            base.localAvatar.laffMeter.stop()
        self.sendForceArrowUpdateAsap = False
        return

    def exitInputChoice(self):
        if not self.controlKeyPressed:
            if self.controlKeyWarningIval:
                self.controlKeyWarningIval.finish()
                self.controlKeyWarningIval = None
            self.controlKeyWarningIval = Sequence(
                Func(self.controlKeyWarningLabel.show),
                self.controlKeyWarningLabel.colorScaleInterval(
                    10, VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)
                ),
                Func(self.controlKeyWarningLabel.hide),
            )
            self.controlKeyWarningIval.start()
        if self.timer != None:
            self.timer.destroy()
            self.timer = None
        self.timerStartTime = None
        self.hideForceArrows()
        self.arrowRotateSound.stop()
        self.arrowUpSound.stop()
        self.arrowDownSound.stop()
        taskMgr.remove(self.uniqueName("aimtask"))
        return

    def enterWaitServerChoices(self):
        self.waitingMoveLabel.show()
        self.showForceArrows(True)

    def exitWaitServerChoices(self):
        self.waitingMoveLabel.hide()
        self.hideForceArrows()

    def enterMoveTires(self):
        for key in self.tireDict:
            body = self.tireDict[key]["tireBody"]
            body.setAngularVel(0, 0, 0)
            body.setLinearVel(0, 0, 0)

        for index in xrange(len(self.allTireInputs)):
            input = self.allTireInputs[index]
            avId = self.avIdList[index]
            body = self.getTireBody(avId)
            degs = input[1] + 90
            tireNp = self.getTireNp(avId)
            tireH = tireNp.getH()
            self.notify.debug("tireH = %s" % tireH)
            radAngle = deg2Rad(degs)
            foo = NodePath("foo")
            dirVector = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
            self.notify.debug("dirVector is now=%s" % dirVector)
            inputForce = input[0]
            inputForce /= self.MaxLocalForce
            inputForce *= self.MaxPhysicsForce
            force = dirVector * inputForce
            self.notify.debug("adding force %s to %d" % (force, avId))
            body.addForce(force)

        self.enableAllTireBodies()
        self.totalPhysicsSteps = 0
        self.startSim()
        taskMgr.add(self.__moveTiresTask, self.uniqueName("moveTiresTtask"))

    def exitMoveTires(self):
        self.forceLocalToonToTire()
        self.disableAllTireBodies()
        self.stopSim()
        self.notify.debug("total Physics steps = %d" % self.totalPhysicsSteps)
        taskMgr.remove(self.uniqueName("moveTiresTtask"))

    def enterSynch(self):
        self.waitingSyncLabel.show()

    def exitSynch(self):
        self.waitingSyncLabel.hide()

    def enterScoring(self):
        sortedByDistance = []
        for avId in self.avIdList:
            np = self.getTireNp(avId)
            pos = np.getPos()
            pos.setZ(0)
            sortedByDistance.append((avId, pos.length()))

        def compareDistance(x, y):
            if x[1] - y[1] > 0:
                return 1
            elif x[1] - y[1] < 0:
                return -1
            else:
                return 0

        sortedByDistance.sort(cmp=compareDistance)
        self.scoreMovie = Sequence()
        curScale = 0.01
        curTime = 0
        self.scoreCircle.setScale(0.01)
        self.scoreCircle.show()
        self.notify.debug("newScores = %s" % self.newScores)
        circleStartTime = 0
        for index in xrange(len(sortedByDistance)):
            distance = sortedByDistance[index][1]
            avId = sortedByDistance[index][0]
            scorePanelIndex = self.avIdList.index(avId)
            time = (distance - curScale) / IceGameGlobals.ExpandFeetPerSec
            if time < 0:
                time = 0.01
            scaleXY = distance + IceGameGlobals.TireRadius
            self.notify.debug("circleStartTime = %s" % circleStartTime)
            self.scoreMovie.append(
                Parallel(
                    LerpScaleInterval(self.scoreCircle, time, Point3(scaleXY, scaleXY, 1.0)),
                    SoundInterval(self.scoreCircleSound, duration=time, startTime=circleStartTime),
                )
            )
            circleStartTime += time
            startScore = self.scorePanels[scorePanelIndex].getScore()
            destScore = self.newScores[scorePanelIndex]
            self.notify.debug("for avId %d, startScore=%d, newScores=%d" % (avId, startScore, destScore))

            def increaseScores(t, scorePanelIndex=scorePanelIndex, startScore=startScore, destScore=destScore):
                oldScore = self.scorePanels[scorePanelIndex].getScore()
                diff = destScore - startScore
                newScore = int(startScore + diff * t)
                if newScore > oldScore:
                    base.playSfx(self.countSound)
                self.scorePanels[scorePanelIndex].setScore(newScore)
                self.scores[scorePanelIndex] = newScore

            duration = (destScore - startScore) * IceGameGlobals.ScoreCountUpRate
            tireNp = self.tireDict[avId]["tireNodePath"]
            self.scoreMovie.append(
                Parallel(
                    LerpFunctionInterval(increaseScores, duration),
                    Sequence(
                        LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)),
                        LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)),
                    ),
                )
            )
            curScale += distance

        self.scoreMovie.append(Func(self.sendUpdate, "reportScoringMovieDone", []))
        self.scoreMovie.start()

    def exitScoring(self):
        self.scoreMovie.finish()
        self.scoreMovie = None
        self.scoreCircle.hide()
        return

    def enterFinalResults(self):
        lerpTrack = Parallel()
        lerpDur = 0.5
        tY = 0.6
        bY = -0.05
        lX = -0.5
        cX = 0
        rX = 0.5
        scorePanelLocs = (
            ((cX, bY),),
            ((lX, bY), (rX, bY)),
            ((cX, tY), (lX, bY), (rX, bY)),
            ((lX, tY), (rX, tY), (lX, bY), (rX, bY)),
        )
        scorePanelLocs = scorePanelLocs[self.numPlayers - 1]
        for i in xrange(self.numPlayers):
            panel = self.scorePanels[i]
            pos = scorePanelLocs[i]
            panel.wrtReparentTo(aspect2d)
            lerpTrack.append(
                Parallel(
                    LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType="easeInOut"),
                    LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType="easeInOut"),
                )
            )

        self.showScoreTrack = Parallel(
            lerpTrack, Sequence(Wait(IceGameGlobals.ShowScoresDuration), Func(self.gameOver))
        )
        self.showScoreTrack.start()

    def exitFinalResults(self):
        self.showScoreTrack.pause()
        del self.showScoreTrack

    def enterCleanup(self):
        self.notify.debug("enterCleanup")
        if base.localAvatar.laffMeter:
            base.localAvatar.laffMeter.start()

    def exitCleanup(self):
        pass

    def __placeToon(self, avId):
        toon = self.getAvatar(avId)
        if toon:
            toon.setPos(0, 0, 0)
            toon.setHpr(0, 0, 0)

    def moveCameraToTop(self):
        camera.reparentTo(render)
        p = self.cameraThreeQuarterView
        camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5])

    def setupTire(self, avId, index):
        tireNp, tireBody, tireOdeGeom = self.createTire(index)
        self.tireDict[avId] = {"tireNodePath": tireNp, "tireBody": tireBody, "tireOdeGeom": tireOdeGeom}
        if avId <= 0:
            tireBlocker = tireNp.find("**/tireblockermesh")
            if not tireBlocker.isEmpty():
                tireBlocker.hide()
        if avId == self.localAvId:
            tireNp = self.tireDict[avId]["tireNodePath"]
            self.treasureSphereName = "treasureCollider"
            self.treasureCollSphere = CollisionSphere(0, 0, 0, IceGameGlobals.TireRadius)
            self.treasureCollSphere.setTangible(0)
            self.treasureCollNode = CollisionNode(self.treasureSphereName)
            self.treasureCollNode.setFromCollideMask(ToontownGlobals.PieBitmask)
            self.treasureCollNode.addSolid(self.treasureCollSphere)
            self.treasureCollNodePath = tireNp.attachNewNode(self.treasureCollNode)
            self.treasureHandler = CollisionHandlerEvent()
            self.treasureHandler.addInPattern("%fn-intoTreasure")
            base.cTrav.addCollider(self.treasureCollNodePath, self.treasureHandler)
            eventName = "%s-intoTreasure" % self.treasureCollNodePath.getName()
            self.notify.debug("eventName = %s" % eventName)
            self.accept(eventName, self.toonHitSomething)

    def setupForceArrow(self, avId):
        arrow = loader.loadModel("phase_4/models/minigames/ice_game_arrow")
        priority = 0
        if avId < 0:
            priority = -avId
        else:
            priority = self.avIdList.index(avId)
            if avId == self.localAvId:
                priority = 10
        self.forceArrowDict[avId] = arrow

    def hideForceArrows(self):
        for forceArrow in self.forceArrowDict.values():
            forceArrow.hide()

    def showForceArrows(self, realPlayersOnly=True):
        for avId in self.forceArrowDict:
            if realPlayersOnly:
                if avId > 0:
                    self.forceArrowDict[avId].show()
                else:
                    self.forceArrowDict[avId].hide()
            else:
                self.forceArrowDict[avId].show()

    def localForceArrow(self):
        if self.localAvId in self.forceArrowDict:
            return self.forceArrowDict[self.localAvId]
        else:
            return None
        return None

    def setChoices(self, input0, input1, input2, input3):
        pass

    def startDebugTask(self):
        taskMgr.add(self.debugTask, self.debugTaskName)

    def stopDebugTask(self):
        taskMgr.remove(self.debugTaskName)

    def debugTask(self, task):
        if self.canDrive and self.tireDict.has_key(localAvatar.doId):
            dt = globalClock.getDt()
            forceMove = 25000
            forceMoveDt = forceMove
            tireBody = self.tireDict[localAvatar.doId]["tireBody"]
            if self.arrowKeys.upPressed() and not tireBody.isEnabled():
                x = 0
                y = 1
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))
            if self.arrowKeys.downPressed() and not tireBody.isEnabled():
                x = 0
                y = -1
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))
            if self.arrowKeys.leftPressed() and not tireBody.isEnabled():
                x = -1
                y = 0
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))
            if self.arrowKeys.rightPressed() and not tireBody.isEnabled():
                x = 1
                y = 0
                tireBody.enable()
                tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0))
        return task.cont

    def __upArrowPressed(self):
        pass

    def __downArrowPressed(self):
        pass

    def __leftArrowPressed(self):
        pass

    def __rightArrowPressed(self):
        pass

    def __controlPressed(self):
        if self.gameFSM.getCurrentState().getName() == "inputChoice":
            self.sendForceArrowUpdateAsap = True
            self.updateLocalForceArrow()
            self.controlKeyPressed = True
            self.sendUpdate("setAvatarChoice", [self.curForce, self.curHeading])
            self.gameFSM.request("waitServerChoices")

    def startTimer(self):
        now = globalClock.getFrameTime()
        elapsed = now - self.timerStartTime
        self.timer.posInTopRightCorner()
        self.timer.setTime(IceGameGlobals.InputTimeout)
        self.timer.countdown(IceGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout)
        self.timer.show()

    def setTimerStartTime(self, timestamp):
        if not self.hasLocalToon:
            return
        self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp)
        if self.timer != None:
            self.startTimer()
        return

    def handleChoiceTimeout(self):
        self.sendUpdate("setAvatarChoice", [0, 0])
        self.gameFSM.request("waitServerChoices")

    def localTireNp(self):
        ret = None
        if self.localAvId in self.tireDict:
            ret = self.tireDict[self.localAvId]["tireNodePath"]
        return ret

    def localTireBody(self):
        ret = None
        if self.localAvId in self.tireDict:
            ret = self.tireDict[self.localAvId]["tireBody"]
        return ret

    def getTireBody(self, avId):
        ret = None
        if avId in self.tireDict:
            ret = self.tireDict[avId]["tireBody"]
        return ret

    def getTireNp(self, avId):
        ret = None
        if avId in self.tireDict:
            ret = self.tireDict[avId]["tireNodePath"]
        return ret

    def updateForceArrow(self, avId, curHeading, curForce):
        forceArrow = self.forceArrowDict[avId]
        tireNp = self.tireDict[avId]["tireNodePath"]
        tireNp.setH(curHeading)
        tireBody = self.tireDict[avId]["tireBody"]
        tireBody.setQuaternion(tireNp.getQuat())
        self.notify.debug("curHeading = %s" % curHeading)
        yScale = curForce / 100.0
        yScale *= 1
        headY = yScale * 15
        xScale = (yScale - 1) / 2.0 + 1.0
        shaft = forceArrow.find("**/arrow_shaft")
        head = forceArrow.find("**/arrow_head")
        shaft.setScale(xScale, yScale, 1)
        head.setPos(0, headY, 0)
        head.setScale(xScale, xScale, 1)

    def updateLocalForceArrow(self):
        avId = self.localAvId
        self.b_setForceArrowInfo(avId, self.curHeading, self.curForce)

    def __aimTask(self, task):
        if not hasattr(self, "arrowKeys"):
            return task.done
        dt = globalClock.getDt()
        headingMomentumChange = dt * 60.0
        forceMomentumChange = dt * 160.0
        arrowUpdate = False
        arrowRotating = False
        arrowUp = False
        arrowDown = False
        if self.arrowKeys.upPressed() and not self.arrowKeys.downPressed():
            self.forceMomentum += forceMomentumChange
            if self.forceMomentum < 0:
                self.forceMomentum = 0
            if self.forceMomentum > 50:
                self.forceMomentum = 50
            oldForce = self.curForce
            self.curForce += self.forceMomentum * dt
            arrowUpdate = True
            if oldForce < self.MaxLocalForce:
                arrowUp = True
        elif self.arrowKeys.downPressed() and not self.arrowKeys.upPressed():
            self.forceMomentum += forceMomentumChange
            if self.forceMomentum < 0:
                self.forceMomentum = 0
            if self.forceMomentum > 50:
                self.forceMomentum = 50
            oldForce = self.curForce
            self.curForce -= self.forceMomentum * dt
            arrowUpdate = True
            if oldForce > 0.01:
                arrowDown = True
        else:
            self.forceMomentum = 0
        if self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed():
            self.headingMomentum += headingMomentumChange
            if self.headingMomentum < 0:
                self.headingMomentum = 0
            if self.headingMomentum > 50:
                self.headingMomentum = 50
            self.curHeading += self.headingMomentum * dt
            arrowUpdate = True
            arrowRotating = True
        elif self.arrowKeys.rightPressed() and not self.arrowKeys.leftPressed():
            self.headingMomentum += headingMomentumChange
            if self.headingMomentum < 0:
                self.headingMomentum = 0
            if self.headingMomentum > 50:
                self.headingMomentum = 50
            self.curHeading -= self.headingMomentum * dt
            arrowUpdate = True
            arrowRotating = True
        else:
            self.headingMomentum = 0
        if arrowUpdate:
            self.normalizeHeadingAndForce()
            self.updateLocalForceArrow()
        if arrowRotating:
            if not self.arrowRotateSound.status() == self.arrowRotateSound.PLAYING:
                base.playSfx(self.arrowRotateSound, looping=True)
        else:
            self.arrowRotateSound.stop()
        if arrowUp:
            if not self.arrowUpSound.status() == self.arrowUpSound.PLAYING:
                base.playSfx(self.arrowUpSound, looping=False)
        else:
            self.arrowUpSound.stop()
        if arrowDown:
            if not self.arrowDownSound.status() == self.arrowDownSound.PLAYING:
                base.playSfx(self.arrowDownSound, looping=False)
        else:
            self.arrowDownSound.stop()
        return task.cont

    def normalizeHeadingAndForce(self):
        if self.curForce > self.MaxLocalForce:
            self.curForce = self.MaxLocalForce
        if self.curForce < 0.01:
            self.curForce = 0.01

    def setTireInputs(self, tireInputs):
        if not self.hasLocalToon:
            return
        self.allTireInputs = tireInputs
        self.gameFSM.request("moveTires")

    def enableAllTireBodies(self):
        for avId in self.tireDict.keys():
            self.tireDict[avId]["tireBody"].enable()

    def disableAllTireBodies(self):
        for avId in self.tireDict.keys():
            self.tireDict[avId]["tireBody"].disable()

    def areAllTiresDisabled(self):
        for avId in self.tireDict.keys():
            if self.tireDict[avId]["tireBody"].isEnabled():
                return False

        return True

    def __moveTiresTask(self, task):
        if self.areAllTiresDisabled():
            self.sendTirePositions()
            self.gameFSM.request("synch")
            return task.done
        return task.cont

    def sendTirePositions(self):
        tirePositions = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            tire = self.getTireBody(avId)
            pos = Point3(tire.getPosition())
            tirePositions.append([pos[0], pos[1], pos[2]])

        for index in xrange(len(self.avIdList), 4):
            avId = -index
            tire = self.getTireBody(avId)
            pos = Point3(tire.getPosition())
            tirePositions.append([pos[0], pos[1], pos[2]])

        self.sendUpdate("endingPositions", [tirePositions])

    def setFinalPositions(self, finalPos):
        if not self.hasLocalToon:
            return
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            tire = self.getTireBody(avId)
            np = self.getTireNp(avId)
            pos = finalPos[index]
            tire.setPosition(pos[0], pos[1], pos[2])
            np.setPos(pos[0], pos[1], pos[2])

        for index in xrange(len(self.avIdList), 4):
            avId = -index
            tire = self.getTireBody(avId)
            np = self.getTireNp(avId)
            pos = finalPos[index]
            tire.setPosition(pos[0], pos[1], pos[2])
            np.setPos(pos[0], pos[1], pos[2])

    def updateInfoLabel(self):
        self.infoLabel["text"] = TTLocalizer.IceGameInfo % {
            "curMatch": self.curMatch + 1,
            "numMatch": IceGameGlobals.NumMatches,
            "curRound": self.curRound + 1,
            "numRound": IceGameGlobals.NumRounds,
        }

    def setMatchAndRound(self, match, round):
        if not self.hasLocalToon:
            return
        self.curMatch = match
        self.curRound = round
        self.updateInfoLabel()

    def setScores(self, match, round, scores):
        if not self.hasLocalToon:
            return
        self.newMatch = match
        self.newRound = round
        self.newScores = scores

    def setNewState(self, state):
        if not self.hasLocalToon:
            return
        self.notify.debug("setNewState gameFSM=%s newState=%s" % (self.gameFSM, state))
        self.gameFSM.request(state)

    def putAllTiresInStartingPositions(self):
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            np = self.tireDict[avId]["tireNodePath"]
            np.setPos(IceGameGlobals.StartingPositions[index])
            self.notify.debug("avId=%s newPos=%s" % (avId, np.getPos))
            np.setHpr(0, 0, 0)
            quat = np.getQuat()
            body = self.tireDict[avId]["tireBody"]
            body.setPosition(IceGameGlobals.StartingPositions[index])
            body.setQuaternion(quat)

        for index in xrange(len(self.avIdList), 4):
            avId = -index
            np = self.tireDict[avId]["tireNodePath"]
            np.setPos(IceGameGlobals.StartingPositions[index])
            self.notify.debug("avId=%s newPos=%s" % (avId, np.getPos))
            np.setHpr(0, 0, 0)
            quat = np.getQuat()
            body = self.tireDict[avId]["tireBody"]
            body.setPosition(IceGameGlobals.StartingPositions[index])
            body.setQuaternion(quat)

    def b_setForceArrowInfo(self, avId, force, heading):
        self.setForceArrowInfo(avId, force, heading)
        self.d_setForceArrowInfo(avId, force, heading)

    def d_setForceArrowInfo(self, avId, force, heading):
        sendIt = False
        curTime = self.getCurrentGameTime()
        if self.sendForceArrowUpdateAsap:
            sendIt = True
        elif curTime - self.lastForceArrowUpdateTime > 0.2:
            sendIt = True
        if sendIt:
            self.sendUpdate("setForceArrowInfo", [avId, force, heading])
            self.sendForceArrowUpdateAsap = False
            self.lastForceArrowUpdateTime = self.getCurrentGameTime()

    def setForceArrowInfo(self, avId, force, heading):
        if not self.hasLocalToon:
            return
        self.updateForceArrow(avId, force, heading)

    def setupStartOfMatch(self):
        self.putAllTiresInStartingPositions()
        szId = self.getSafezoneId()
        self.numTreasures = IceGameGlobals.NumTreasures[szId]
        if self.treasures:
            for treasure in self.treasures:
                treasure.destroy()

            self.treasures = []
        index = 0
        treasureMargin = IceGameGlobals.TireRadius + 1.0
        while len(self.treasures) < self.numTreasures:
            xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5, IceGameGlobals.MaxWall[0] - 5)
            yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5, IceGameGlobals.MaxWall[1] - 5)
            self.notify.debug("yPos=%s" % yPos)
            pos = Point3(xPos, yPos, IceGameGlobals.TireRadius)
            newTreasure = IceTreasure.IceTreasure(self.treasureModel, pos, index, self.doId, penalty=False)
            goodSpot = True
            for obstacle in self.obstacles:
                if newTreasure.nodePath.getDistance(obstacle) < treasureMargin:
                    goodSpot = False
                    break

            if goodSpot:
                for treasure in self.treasures:
                    if newTreasure.nodePath.getDistance(treasure.nodePath) < treasureMargin:
                        goodSpot = False
                        break

            if goodSpot:
                self.treasures.append(newTreasure)
                index += 1
            else:
                newTreasure.destroy()

        self.numPenalties = IceGameGlobals.NumPenalties[szId]
        if self.penalties:
            for penalty in self.penalties:
                penalty.destroy()

            self.penalties = []
        index = 0
        while len(self.penalties) < self.numPenalties:
            xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5, IceGameGlobals.MaxWall[0] - 5)
            yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5, IceGameGlobals.MaxWall[1] - 5)
            self.notify.debug("yPos=%s" % yPos)
            pos = Point3(xPos, yPos, IceGameGlobals.TireRadius)
            newPenalty = IceTreasure.IceTreasure(self.penaltyModel, pos, index, self.doId, penalty=True)
            goodSpot = True
            for obstacle in self.obstacles:
                if newPenalty.nodePath.getDistance(obstacle) < treasureMargin:
                    goodSpot = False
                    break

            if goodSpot:
                for treasure in self.treasures:
                    if newPenalty.nodePath.getDistance(treasure.nodePath) < treasureMargin:
                        goodSpot = False
                        break

            if goodSpot:
                for penalty in self.penalties:
                    if newPenalty.nodePath.getDistance(penalty.nodePath) < treasureMargin:
                        goodSpot = False
                        break

            if goodSpot:
                self.penalties.append(newPenalty)
                index += 1
            else:
                newPenalty.destroy()

    def toonHitSomething(self, entry):
        self.notify.debug("---- treasure Enter ---- ")
        self.notify.debug("%s" % entry)
        name = entry.getIntoNodePath().getName()
        parts = name.split("-")
        if len(parts) < 3:
            self.notify.debug("collided with %s, but returning" % name)
            return
        if not int(parts[1]) == self.doId:
            self.notify.debug("collided with %s, but doId doesn't match" % name)
            return
        treasureNum = int(parts[2])
        if "penalty" in parts[0]:
            self.__penaltyGrabbed(treasureNum)
        else:
            self.__treasureGrabbed(treasureNum)

    def __treasureGrabbed(self, treasureNum):
        self.treasures[treasureNum].showGrab()
        self.treasureGrabSound.play()
        self.sendUpdate("claimTreasure", [treasureNum])

    def setTreasureGrabbed(self, avId, treasureNum):
        if not self.hasLocalToon:
            return
        self.notify.debug("treasure %s grabbed by %s" % (treasureNum, avId))
        if avId != self.localAvId:
            self.treasures[treasureNum].showGrab()
        i = self.avIdList.index(avId)
        self.scores[i] += 1
        self.scorePanels[i].setScore(self.scores[i])

    def __penaltyGrabbed(self, penaltyNum):
        self.penalties[penaltyNum].showGrab()
        self.sendUpdate("claimPenalty", [penaltyNum])

    def setPenaltyGrabbed(self, avId, penaltyNum):
        if not self.hasLocalToon:
            return
        self.notify.debug("penalty %s grabbed by %s" % (penaltyNum, avId))
        if avId != self.localAvId:
            self.penalties[penaltyNum].showGrab()
        i = self.avIdList.index(avId)
        self.scores[i] -= 1
        self.scorePanels[i].setScore(self.scores[i])

    def postStep(self):
        DistributedIceWorld.DistributedIceWorld.postStep(self)
        if not self.colCount:
            return
        for count in xrange(self.colCount):
            c0, c1 = self.getOrderedContacts(count)
            if c1 in self.tireCollideIds:
                tireIndex = self.tireCollideIds.index(c1)
                if c0 in self.tireCollideIds:
                    self.tireSounds[tireIndex]["tireHit"].play()
                elif c0 == self.wallCollideId:
                    self.tireSounds[tireIndex]["wallHit"].play()
                elif c0 == self.obstacleCollideId:
                    self.tireSounds[tireIndex]["obstacleHit"].play()

    def forceLocalToonToTire(self):
        toon = localAvatar
        if toon and self.localAvId in self.tireDict:
            tireNp = self.tireDict[self.localAvId]["tireNodePath"]
            toon.reparentTo(tireNp)
            toon.setPosHpr(0, 0, 0, 0, 0, 0)
            toon.setY(1.0)
            toon.setZ(-3)
Beispiel #6
0
class SquirtGag(Gag):
    def __init__(self,
                 name,
                 model,
                 damage,
                 hitSfx,
                 spraySfx,
                 missSfx,
                 toonAnim,
                 enableReleaseFrame,
                 completeSquirtFrame,
                 startAnimFrame=0,
                 scale=1,
                 playRate=1):
        Gag.__init__(self,
                     name,
                     model,
                     damage,
                     GagType.SQUIRT,
                     hitSfx,
                     scale=scale,
                     autoRelease=True,
                     playRate=playRate)
        self.sprayScale = GagGlobals.splatSizes.get(self.name)
        self.spraySfx = None
        self.missSfx = None
        self.origin = None
        self.sprayRange = None
        self.spray = None
        self.sprayJoint = None
        self.canSquirt = False
        self.hitSomething = False
        self.toonAnim = toonAnim
        self.startAnimFrame = 0
        self.enableReleaseFrame = enableReleaseFrame
        self.completeSquirtFrame = completeSquirtFrame
        self.lastFrame = 0
        self.tracks = None
        self.sprayTrack = None
        self.sprayAttempt = None
        self.sprayRotation = Vec3(0, 0, 0)

        if game.process == 'client':
            if spraySfx:
                self.spraySfx = base.audio3d.loadSfx(spraySfx)
            if missSfx:
                self.missSfx = base.audio3d.loadSfx(missSfx)

    def start(self):
        Gag.start(self)
        if self.sprayTrack:
            self.sprayTrack.pause()
            self.sprayTrack = None
        if self.tracks:
            self.tracks.pause()
            self.tracks = None
        if self.anim:
            self.build()
            self.equip()
            duration = base.localAvatar.getDuration(
                self.toonAnim, toFrame=self.enableReleaseFrame)
            self.sprayAttempt = Parallel(
                ActorInterval(self.avatar,
                              self.toonAnim,
                              startFrame=self.startAnimFrame,
                              endFrame=self.enableReleaseFrame,
                              playRate=self.playRate), Wait(duration - 0.15),
                Func(self.setSquirtEnabled, True)).start()

    def startSquirt(self, sprayScale, containerHold):
        def startSpray():
            self.doSpray(sprayScale, containerHold, sprayScale)

        Sequence(
            ActorInterval(self.avatar,
                          self.toonAnim,
                          startFrame=self.enableReleaseFrame,
                          endFrame=self.completeSquirtFrame),
            Func(startSpray)).start()

    def setSquirtEnabled(self, flag):
        self.canSquirt = flag
        self.sprayAttempt = None

    def doSpray(self, scaleUp, scaleDown, hold, horizScale=1.0, vertScale=1.0):
        base.audio3d.attachSoundToObject(self.spraySfx, self.gag)
        base.playSfx(self.spraySfx, node=self.gag)
        spraySequence = self.getSprayTrack(self.origin, self.sprayRange,
                                           scaleUp, hold, scaleDown,
                                           horizScale, vertScale)
        self.sprayTrack = spraySequence
        self.sprayTrack.start()

    def completeSquirt(self):
        numFrames = base.localAvatar.getNumFrames(self.toonAnim)
        finishSeq = Sequence()
        finishSeq.append(Wait(0.5))
        finishSeq.append(
            Func(self.avatar.play,
                 self.toonAnim,
                 fromFrame=self.completeSquirtFrame,
                 toFrame=numFrames))
        finishSeq.append(Func(self.reset))
        finishSeq.append(Func(self.avatar.play, 'neutral'))
        finishSeq.append(Func(self.cleanupSpray))
        finishSeq.start()
        if self.avatar == base.localAvatar:
            if base.localAvatar.getBackpack().getSupply() == 0:
                self.cleanupGag()

    def onCollision(self, entry):
        self.hitSomething = True
        base.audio3d.attachSoundToObject(self.hitSfx, self.sprayNP)
        base.playSfx(self.hitSfx, node=self.sprayNP)
        intoNP = entry.getIntoNodePath()
        avNP = intoNP.getParent()
        if self.avatar.doId == base.localAvatar.doId:
            for key in base.cr.doId2do.keys():
                obj = base.cr.doId2do[key]
                if obj.__class__.__name__ in CIGlobals.SuitClasses:
                    if obj.getKey() == avNP.getKey():
                        obj.sendUpdate('hitByGag', [self.getID()])
                elif obj.__class__.__name__ == "DistributedToon":
                    if obj.getKey() == avNP.getKey():
                        if obj.getHealth() < obj.getMaxHealth():
                            self.avatar.sendUpdate(
                                'toonHitByPie',
                                [obj.doId, self.getID()])
        if base.localAvatar == self.avatar:
            self.splatPos = self.sprayNP.getPos(render)
            gagPos = self.sprayNP.getPos(render)
            self.handleSplat()
            self.avatar.sendUpdate(
                'setSplatPos',
                [self.getID(),
                 gagPos.getX(),
                 gagPos.getY(),
                 gagPos.getZ()])

    def handleMiss(self):
        if self.spray and self.hitSomething == False:
            base.audio3d.attachSoundToObject(self.missSfx, self.spray)
            base.playSfx(self.missSfx, node=self.spray)
            self.cleanupSpray()

    def handleSplat(self):
        self.cleanupSplat()
        self.buildSplat(GagGlobals.splatSizes.get(self.getName()),
                        GagGlobals.WATER_SPRAY_COLOR)
        self.splat.setPos(self.splatPos)
        self.splat.reparentTo(render)
        self.splatPos = None
        taskMgr.doMethodLater(0.5, self.delSplat, 'Delete Splat')

    def getSprayTrack(self,
                      origin,
                      target,
                      scaleUp,
                      hold,
                      scaleDown,
                      horizScale=1.0,
                      vertScale=1.0):
        if self.sprayJoint.isEmpty():
            self.build()
            self.origin = self.getSprayStartPos()
        base.localAvatar.stop(self.toonAnim)
        self.lastFrame = self.avatar.getCurrentFrame(self.toonAnim)
        track = Sequence()
        sprayProp = loader.loadModel(GagGlobals.SPRAY_MDL)
        sprayProp.setTwoSided(1)
        sprayScale = hidden.attachNewNode('spray-parent')
        sprayRot = hidden.attachNewNode('spray-rotate')
        sprayRot.setColor(GagGlobals.WATER_SPRAY_COLOR)
        sprayRot.setTransparency(1)

        collNode = CollisionNode('Collision')
        spraySphere = CollisionSphere(0, 0, 0, 1)
        spraySphere.setTangible(0)
        collNode.addSolid(spraySphere)
        collNode.setCollideMask(CIGlobals.WallBitmask)
        sprayNP = sprayRot.attachNewNode(collNode)
        sprayNP.setY(1)
        self.sprayNP = sprayNP
        event = CollisionHandlerEvent()
        event.set_in_pattern("%fn-into")
        event.set_out_pattern("%fn-out")
        base.cTrav.add_collider(sprayNP, event)
        self.avatar.acceptOnce(sprayNP.node().getName() + '-into',
                               self.onCollision)

        def showSpray(sprayScale, sprayProp, origin, target):
            objects = [sprayRot, sprayScale, sprayProp]
            for item in objects:
                index = objects.index(item)
                if index == 0:
                    item.reparentTo(self.sprayJoint)
                    item.setPos(0, 0, 0)
                    item.setHpr(self.sprayRotation)
                    item.wrtReparentTo(render)
                else:
                    item.reparentTo(objects[index - 1])

        track.append(Func(showSpray, sprayScale, sprayProp, origin, target))
        self.spray = sprayRot

        def calcTargetScale():
            distance = Vec3(target - origin).length()
            yScale = distance / GagGlobals.SPRAY_LEN
            targetScale = Point3(yScale * horizScale, yScale,
                                 yScale * vertScale)
            return targetScale

        track.append(
            Parallel(
                LerpScaleInterval(sprayScale,
                                  scaleUp,
                                  calcTargetScale,
                                  startScale=GagGlobals.PNT3NEAR0),
                sprayNP.posInterval(
                    0.25,
                    self.spray.getPos(render) + Point3(0, 50, 0),
                    startPos=self.spray.getPos(render) + Point3(0, 5, 0))))
        track.append(Wait(hold))
        track.append(Func(self.handleMiss))
        track.append(LerpScaleInterval(sprayScale, 0.75, GagGlobals.PNT3NEAR0))

        def hideSpray():
            lambda prop: prop.removeNode(), [sprayProp, sprayRot, sprayScale]

        track.append(Func(hideSpray))
        track.append(Func(self.completeSquirt))
        return track

    def getSprayRange(self):
        sprayRange = NodePath('Squirt Range')
        sprayRange.reparentTo(self.avatar)
        sprayRange.setScale(render, 1)
        sprayRange.setPos(0, 160, -90)
        sprayRange.setHpr(90, -90, 90)
        return sprayRange

    @abc.abstractmethod
    def getSprayStartPos(self):
        return Point3(0, 0, 0)

    def cleanupSpray(self):
        if self.spray:
            self.spray.removeNode()
            self.spray = None
        if self.sprayAttempt:
            self.sprayAttempt.pause()
            self.sprayAttempt = None
        self.hitSomething = False
        self.canSquirt = False
class DistributedPieTurret(DistributedAvatar, DistributedSmoothNode):
    notify = directNotify.newCategory('DistributedPieTurret')

    def __init__(self, cr):
        DistributedAvatar.__init__(self, cr)
        DistributedSmoothNode.__init__(self, cr)
        self.fsm = ClassicFSM(
            'DistributedPieTurret',
            [
                State('off', self.enterOff, self.exitOff),
                State('scan', self.enterScan, self.exitScan),
                State('shoot', self.enterShoot, self.exitShoot)
             ],
             'off', 'off'
         )
        self.fsm.enterInitialState()
        self.reloadTime = 0.25
        self.cannon = None
        self.track = None
        self.owner = None
        self.gag = None
        self.readyGag = None
        self.hitGag = None
        self.explosion = None
        self.wallCollNode = None
        self.eventCollNode = None
        self.event = None
        self.suit = None
        self.eventId = None
        self.entities = []
        self.upgradeID = None
        self.deathEvent = None

    def setOwner(self, avatar):
        self.owner = avatar

    def getOwner(self):
        return self.owner

    def setGag(self, upgradeId):
        gags = {0 : CIGlobals.WholeCreamPie, 1 : CIGlobals.WholeFruitPie, 2 : CIGlobals.BirthdayCake, 3 : CIGlobals.WeddingCake}
        self.gag = gags.get(upgradeId)
        if not self.readyGag:
            self.loadGagInTurret()

    def b_setGag(self, upgradeId):
        self.sendUpdate('setGag', [upgradeId])
        self.setGag(upgradeId)
        self.upgradeID = upgradeId

    def getGag(self):
        return self.gag

    def getGagID(self):
        return self.upgradeID

    def generate(self):
        DistributedAvatar.generate(self)
        DistributedSmoothNode.generate(self)

    def announceGenerate(self):
        DistributedAvatar.announceGenerate(self)
        DistributedSmoothNode.announceGenerate(self)
        self.healthLabel.setScale(1.1)
        self.deathEvent = self.uniqueName('DistributedPieTurret-death')
        self.makeTurret()

    def disable(self):
        self.fsm.requestFinalState()
        del self.fsm

        # This should fix crashes related to Sequences.
        if self.track:
            self.track.pause()
            self.track = None

        # Cleanup entities.
        for ent in self.entities:
            ent.cleanup()
        self.entities = None

        # Get rid of explosions.
        if self.explosion:
            self.explosion.removeNode()
            self.explosion = None

        self.removeTurret()
        DistributedSmoothNode.disable(self)
        DistributedAvatar.disable(self)

    def showAndMoveHealthLabel(self):
        self.unstashHpLabel()
        self.stopMovingHealthLabel()
        moveTrack = LerpPosInterval(self.healthLabel,
                                duration = 0.5,
                                pos = Point3(0, 0, 5),
                                startPos = Point3(0, 0, 0),
                                blendType = 'easeOut')
        self.healthLabelTrack = Sequence(moveTrack, Wait(1.0), Func(self.stashHpLabel))
        self.healthLabelTrack.start()

    # BEGIN STATES

    def enterShoot(self, suitId):
        if self.cannon:
            smoke = loader.loadModel("phase_4/models/props/test_clouds.bam")
            smoke.setBillboardPointEye()
            smoke.reparentTo(self.cannon.find('**/cannon'))
            smoke.setPos(0, 6, -3)
            smoke.setScale(0.5)
            smoke.wrtReparentTo(render)
            self.suit = self.cr.doId2do.get(suitId)
            self.cannon.find('**/cannon').lookAt(self.suit.find('**/joint_head'))
            self.cannon.find('**/square_drop_shadow').headsUp(self.suit.find('**/joint_head'))
            self.track = Sequence(Parallel(LerpScaleInterval(smoke, 0.5, 3), LerpColorScaleInterval(smoke, 0.5, Vec4(2, 2, 2, 0))), Func(smoke.removeNode))
            self.track.start()
            self.createAndShootGag()

    def exitShoot(self):
        if hasattr(self, 'suit'):
            del self.suit

    def shoot(self, suitId):
        self.fsm.request('shoot', [suitId])

    def scan(self, timestamp = None, afterShooting = 0):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)

        self.fsm.request('scan', [ts, afterShooting])
        
    def buildScanTrack(self, ts = None):
        if self.track:
            self.track.pause()
            self.track = None
        self.track = Parallel(
            Sequence(
                LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (60, 0, 0),
                    startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
                    startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
            ),
            Sequence(
                LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (60, 0, 0),
                    startHpr = Vec3(-60, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
                    startHpr = Vec3(60, 0, 0), blendType = 'easeInOut'),
            )
        )
        if ts:
            self.track.loop(ts)
        else:
            self.track.loop()

    def enterScan(self, ts = 0, afterShooting = 0):
        if afterShooting:
            self.track = Parallel(
                LerpQuatInterval(self.cannon.find('**/cannon'), duration = 3, quat = (-60, 0, 0),
                    startHpr = self.cannon.find('**/cannon').getHpr(), blendType = 'easeInOut'),
                LerpQuatInterval(self.cannon.find('**/square_drop_shadow'), duration = 3, quat = (-60, 0, 0),
                    startHpr = self.cannon.find('**/square_drop_shadow').getHpr(), blendType = 'easeInOut'),
                name = "afterShootTrack" + str(id(self))
            )
            self.track.setDoneEvent(self.track.getName())
            self.acceptOnce(self.track.getDoneEvent(), self._afterShootTrackDone)
            self.track.start(ts)
        else:
            self.buildScanTrack(ts)

    def exitScan(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    # END STATES

    def _afterShootTrackDone(self):
        self.buildScanTrack()

    def makeTurret(self):
        self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon.bam')
        self.cannon.reparentTo(self)
        self.loadGagInTurret()
        self.setupWallSphere()
        if self.isLocal():
            self.setupEventSphere()

    def removeTurret(self):
        self.removeWallSphere()
        self.removeGagInTurret()
        if self.cannon:
            self.cannon.removeNode()
            self.cannon = None

    def getCannon(self):
        return self.cannon.find('**/cannon')

    def setupWallSphere(self):
        sphere = CollisionSphere(0.0, 0.0, 0.0, 3.0)
        node = CollisionNode('DistributedPieTurret.WallSphere')
        node.addSolid(sphere)
        node.setCollideMask(CIGlobals.WallBitmask)
        self.wallCollNode = self.cannon.attachNewNode(node)
        self.wallCollNode.setZ(2)
        self.wallCollNode.setY(1.0)

    def removeWallSphere(self):
        if self.wallCollNode:
            self.wallCollNode.removeNode()
            self.wallCollNode = None

    def createAndShootGag(self):
        if not self.readyGag:
            self.loadGagInTurret()
        if self.readyGag:
            self.readyGag.shoot(Point3(0, 200, -90))
            self.entities.append(self.readyGag)
            collideEventName = self.readyGag.getCollideEventName()
            self.readyGag = None
            if self.isLocal():
                self.acceptOnce(collideEventName, self.handleGagCollision)
        Sequence(Wait(self.reloadTime), Func(self.loadGagInTurret)).start()

    def loadGagInTurret(self):
        if self.cannon and self.gag:
            self.removeGagInTurret()
            self.eventId = random.uniform(0, 100000000)
            self.readyGag = TurretGag(self, self.uniqueName('pieTurretCollision') + str(self.eventId), self.gag)
            self.readyGag.build()

    def removeGagInTurret(self):
        if self.readyGag:
            self.readyGag.cleanup()
            self.readyGag = None

    def makeSplat(self, index, pos):
        if index >= len(self.entities):
            return
        ent = self.entities[index]
        gagClass = ent.gagClass
        splat = gagClass.buildSplat(gagClass.splatScale, gagClass.splatColor)
        base.audio3d.attachSoundToObject(gagClass.hitSfx, splat)
        splat.reparentTo(render)
        splat.setPos(pos[0], pos[1], pos[2])
        gagClass.hitSfx.play()
        Sequence(Wait(0.5), Func(splat.cleanup)).start()
        self.hitGag = None

    def d_makeSplat(self, index, pos):
        self.sendUpdate('makeSplat', [index, pos])

    def b_makeSplat(self, index, pos):
        self.d_makeSplat(index, pos)
        self.makeSplat(index, pos)

    def handleGagCollision(self, entry, ent):
        x, y, z = ent.getGag().getPos(render)
        self.b_makeSplat(self.entities.index(ent), [x, y, z])
        if self.isLocal():
            intoNP = entry.getIntoNodePath()
            avNP = intoNP.getParent()
            for key in self.cr.doId2do.keys():
                obj = self.cr.doId2do[key]
                if obj.__class__.__name__ == 'DistributedSuit':
                    if obj.getKey() == avNP.getKey():
                        if obj.getHealth() > 0:
                            obj.sendUpdate('hitByGag', [ent.getID()])
        ent.cleanup()

    def setHealth(self, hp):
        DistributedAvatar.setHealth(self, hp)
        if self.isLocal():
            base.localAvatar.getMyBattle().getTurretManager().updateTurretGui()

    def die(self):
        self.fsm.requestFinalState()
        turretPos = self.cannon.getPos(render)
        self.removeTurret()
        self.explosion = loader.loadModel("phase_3.5/models/props/explosion.bam")
        self.explosion.setScale(0.5)
        self.explosion.reparentTo(render)
        self.explosion.setBillboardPointEye()
        self.explosion.setPos(turretPos + (0, 0, 5))
        sfx = base.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_cogfall_apart.ogg")
        base.audio3d.attachSoundToObject(sfx, self)
        base.playSfx(sfx)
        messenger.send(self.deathEvent)

    def isLocal(self):
        return self.getOwner() == base.localAvatar.doId

    def getDeathEvent(self):
        return self.deathEvent
Beispiel #8
0
class DodgeballFirstPerson(FirstPerson):
    """The first person controls for the local player in Winter Dodgeball"""

    notify = directNotify.newCategory("DodgeballFirstPerson")

    MaxPickupDistance = 5.0

    def __init__(self, mg):
        self.mg = mg
        self.crosshair = None
        self.soundCatch = None
        self.vModelRoot = None
        self.vModel = None
        self.ival = None
        self.soundPickup = base.loadSfx(
            'phase_4/audio/sfx/MG_snowball_pickup.ogg')
        self.fakeSnowball = loader.loadModel(
            "phase_5/models/props/snowball.bam")
        self.hasSnowball = False
        self.mySnowball = None
        self.waitingOnPickupResp = False
        self.camPivotNode = base.localAvatar.attachNewNode('cameraPivotNode')
        self.camFSM = ClassicFSM.ClassicFSM("DFPCamera", [
            State.State('off', self.enterCamOff, self.exitCamOff),
            State.State('frozen', self.enterFrozen, self.exitFrozen),
            State.State('unfrozen', self.enterUnFrozen, self.exitUnFrozen)
        ], 'off', 'off')
        self.camFSM.enterInitialState()
        self.fsm = ClassicFSM.ClassicFSM("DodgeballFirstPerson", [
            State.State("off", self.enterOff, self.exitOff),
            State.State("hold", self.enterHold, self.exitHold),
            State.State("catch", self.enterCatch, self.exitCatch),
            State.State("throw", self.enterThrow, self.exitThrow)
        ], "off", "off")
        self.fsm.enterInitialState()

        FirstPerson.__init__(self)

    def enterCamOff(self):
        self.releaseSnowball()

    def exitCamOff(self):
        pass

    def releaseSnowball(self):
        if self.hasSnowball:
            if self.mySnowball and not self.mySnowball.isAirborne:
                self.hasSnowball = False
                self.mySnowball.resetSnowball()
                self.mySnowball = None
                self.fsm.request('off')

    def enterFrozen(self):
        self.releaseSnowball()

        self.vModel.hide()
        base.localAvatar.getGeomNode().show()
        camera.wrtReparentTo(self.camPivotNode)
        camHeight = max(base.localAvatar.getHeight(), 3.0)
        nrCamHeight = base.localAvatar.getHeight()
        heightScaleFactor = camHeight * 0.3333333333
        defLookAt = Point3(0.0, 1.5, camHeight)
        idealData = (Point3(0.0, -12.0 * heightScaleFactor,
                            camHeight), defLookAt)
        self.camTrack = Parallel(
            LerpPosInterval(camera,
                            duration=1.0,
                            pos=idealData[0],
                            startPos=camera.getPos(),
                            blendType='easeOut'),
            LerpQuatInterval(camera,
                             duration=1.0,
                             hpr=idealData[1],
                             startHpr=camera.getHpr(),
                             blendType='easeOut'))
        self.camTrack.start()
        self.max_camerap = 0.0
        self.disableMouse()

    def cameraMovement(self, task):
        if not self.camFSM:
            return task.done

        if self.camFSM.getCurrentState().getName() == 'frozen':
            if hasattr(self, 'min_camerap') and hasattr(self, 'max_camerap'):
                md = base.win.getPointer(0)
                x = md.getX()
                y = md.getY()
                if base.win.movePointer(0,
                                        base.win.getXSize() / 2,
                                        base.win.getYSize() / 2):
                    self.camPivotNode.setP(self.camPivotNode.getP() -
                                           (y - base.win.getYSize() / 2) * 0.1)
                    self.camPivotNode.setH(self.camPivotNode.getH() -
                                           (x - base.win.getXSize() / 2) * 0.1)
                    if self.camPivotNode.getP() < self.min_camerap:
                        self.camPivotNode.setP(self.min_camerap)
                    elif self.camPivotNode.getP() > self.max_camerap:
                        self.camPivotNode.setP(self.max_camerap)
                return task.cont
            else:
                return task.done

        return FirstPerson.cameraMovement(self, task)

    def exitFrozen(self):
        self.camTrack.finish()
        del self.camTrack
        self.max_camerap = 90.0
        self.vModel.show()
        self.enableMouse()
        base.localAvatar.stopSmartCamera()

    def enterUnFrozen(self):
        base.localAvatar.getGeomNode().hide()
        self.reallyStart()
        camera.setPosHpr(0, 0, 0, 0, 0, 0)
        camera.reparentTo(self.player_node)
        camera.setZ(base.localAvatar.getHeight())

    def exitUnFrozen(self):
        self.end()
        self.enableMouse()

    def enterOff(self):
        if self.vModel:
            self.vModel.hide()
        if self.waitingOnPickupResp:
            taskMgr.add(self.__waitForPickupRespTask, "waitForPickupRespTask")

    def __waitForPickupRespTask(self, task):
        if not self.waitingOnPickupResp:
            if self.hasSnowball:
                self.fsm.request('hold')
            return task.done
        return task.cont

    def exitOff(self):
        if self.vModel:
            self.vModel.show()
        taskMgr.remove("waitForPickupRespTask")

    def enterHold(self):
        self.ival = Sequence(ActorInterval(self.vModel, "hold-start"),
                             Func(self.vModel.loop, "hold"))
        self.ival.start()

    def exitHold(self):
        if self.ival:
            self.ival.finish()
            self.ival = None
        self.vModel.stop()

    def enterThrow(self):
        self.ival = Parallel(
            Sequence(Wait(0.4), Func(self.mySnowball.b_throw)),
            Sequence(ActorInterval(self.vModel, "throw"),
                     Func(self.fsm.request, 'off')))
        self.ival.start()

    def exitThrow(self):
        if self.ival:
            self.ival.pause()
            self.ival = None
        self.vModel.stop()

    def enterCatch(self):
        self.ival = Parallel(
            Sequence(Wait(0.2), Func(self.__tryToCatchOrGrab)),
            Sequence(ActorInterval(self.vModel, "catch"),
                     Func(self.__maybeHold)))
        self.ival.start()

    def __maybeHold(self):
        if self.hasSnowball:
            self.fsm.request('hold')
        else:
            self.fsm.request('off')

    def __tryToCatchOrGrab(self):
        snowballs = list(self.mg.snowballs)
        snowballs.sort(
            key=lambda snowball: snowball.getDistance(base.localAvatar))
        for i in xrange(len(snowballs)):
            snowball = snowballs[i]
            if (not snowball.hasOwner() and not snowball.isAirborne
                    and snowball.getDistance(base.localAvatar) <=
                    DodgeballFirstPerson.MaxPickupDistance):
                self.waitingOnPickupResp = True
                self.mg.sendUpdate('reqPickupSnowball', [snowball.index])
                break

    def snowballPickupResp(self, flag, idx):
        if flag:
            snowball = self.mg.snowballs[idx]
            snowball.b_pickup()
            self.mySnowball = snowball
            self.fakeSnowball.setPosHpr(0, 0.73, 0, 0, 0, 0)
            self.fakeSnowball.reparentTo(
                self.vModel.exposeJoint(None, "modelRoot", "Bone.011"))
            base.playSfx(self.soundPickup)
            self.hasSnowball = True
        self.waitingOnPickupResp = False

    def exitCatch(self):
        self.vModel.stop()
        if self.ival:
            self.ival.pause()
            self.ival = None

    def start(self):
        # Black crosshair because basically the entire arena is white.
        self.crosshair = getCrosshair(color=(0, 0, 0, 1), hidden=False)

        self.soundCatch = base.loadSfx(
            "phase_4/audio/sfx/MG_sfx_vine_game_catch.ogg")

        self.vModelRoot = camera.attachNewNode('vModelRoot')
        self.vModelRoot.setPos(-0.09, 1.38, -2.48)

        self.vModel = Actor(
            "phase_4/models/minigames/v_dgm.egg", {
                "hold": "phase_4/models/minigames/v_dgm-ball-hold.egg",
                "hold-start":
                "phase_4/models/minigames/v_dgm-ball-hold-start.egg",
                "throw": "phase_4/models/minigames/v_dgm-ball-throw.egg",
                "catch": "phase_4/models/minigames/v_dgm-ball-catch.egg"
            })
        self.vModel.setBlend(frameBlend=True)
        self.vModel.reparentTo(self.vModelRoot)
        self.vModel.setBin("fixed", 40)
        self.vModel.setDepthTest(False)
        self.vModel.setDepthWrite(False)
        self.vModel.hide()

        base.localAvatar.walkControls.setWalkSpeed(ToonForwardSpeed,
                                                   ToonJumpForce,
                                                   ToonReverseSpeed,
                                                   ToonRotateSpeed)

        FirstPerson.start(self)

    def reallyStart(self):
        FirstPerson.reallyStart(self)
        base.localAvatar.startTrackAnimToSpeed()

        self.accept('mouse3', self.__handleCatchOrGrabButton)
        self.accept('mouse1', self.__handleThrowButton)

    def end(self):
        base.localAvatar.stopTrackAnimToSpeed()
        self.ignore('mouse3')
        self.ignore('mouse1')
        FirstPerson.end(self)

    def __handleThrowButton(self):
        if self.hasSnowball and self.mySnowball and self.fsm.getCurrentState(
        ).getName() == 'hold':
            self.fakeSnowball.reparentTo(hidden)
            self.fsm.request('throw')

    def __handleCatchOrGrabButton(self):
        if not self.hasSnowball and not self.mySnowball and self.fsm.getCurrentState(
        ).getName() == 'off':
            self.fsm.request('catch')

    def reallyEnd(self):
        base.localAvatar.setWalkSpeedNormal()
        if self.camFSM:
            self.camFSM.requestFinalState()
            self.camFSM = None
        if self.fsm:
            self.fsm.requestFinalState()
            self.fsm = None
        if self.crosshair:
            self.crosshair.destroy()
            self.crosshair = None
        if self.vModel:
            self.vModel.removeNode()
            self.vModel = None
        if self.vModelRoot:
            self.vModelRoot.removeNode()
            self.vModelRoot = None
        self.soundCatch = None
        FirstPerson.reallyEnd(self)
class DistributedTakeOverSuit(DistributedSuit):
    notify = directNotify.newCategory("DistributedTakeOverSuit")

    StartPosFromDoor = Point3(1.6, -10, -0.5)
    AtDoorPos = Point3(1.6, -1, 0)

    def __init__(self, cr):
        DistributedSuit.__init__(self, cr)
        self.showNametagInMargins = True
        self.doorDoId = None
        self.door = None
        self.takeOverTrack = None
        self.fsm = ClassicFSM('DTOS-fsm', [
            State('off', self.enterOff, self.exitOff),
            State('takeOver', self.enterTakeOver, self.exitTakeOver)
        ], 'off', 'off')
        self.fsm.enterInitialState()

    def interruptTakeOver(self):
        if self.takeOverTrack:
            self.takeOverTrack.pause()
            self.takeOverTrack = None

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

    def disable(self):
        taskMgr.remove('posTask')
        self.fsm.requestFinalState()
        self.fsm = None
        self.doorDoId = None
        self.door = None
        if self.takeOverTrack:
            self.takeOverTrack.finish()
            self.takeOverTrack = None

        DistributedSuit.disable(self)

    def setDoorDoId(self, doId):
        self.doorDoId = doId
        self.door = self.cr.doId2do.get(doId)
        if self.door is None:
            taskMgr.add(self.__pollDoor, self.uniqueName('pollDoor'))

    def stateAndTimestamp(self, state, timestamp):
        self.setState(state, timestamp)

    def __pollDoor(self, task):
        self.door = self.cr.doId2do.get(self.doorDoId)
        if self.door:
            self.sendUpdate('requestStateAndTimestamp')
            return task.done
        return task.cont

    def getDoorDoId(self):
        return self.doorDoId

    def enterOff(self, ts=0):
        pass

    def exitOff(self):
        pass

    def enterTakeOver(self, ts=0):
        if not self.door:
            return
        self.stopSmooth()
        self.hasSpawned = False
        self.doingActivity = True
        self.reparentTo(self.door.doorNode)
        self.setHpr(0, 0, 0)
        self.takeOverTrack = Parallel(
            Sequence(
                Func(self.animFSM.request, 'flyDown', [ts]), Wait(6.834),
                Func(self.loop, 'neutral'), Wait(0.5), Func(self.loop, 'walk'),
                LerpPosInterval(
                    self,
                    duration=2.0,
                    pos=render.getRelativePoint(self.door.doorNode,
                                                self.AtDoorPos),
                    startPos=render.getRelativePoint(self.door.doorNode,
                                                     self.StartPosFromDoor)),
                Func(self.loop, 'neutral'), Wait(0.3), Func(self.loop, 'walk'),
                LerpPosInterval(self,
                                duration=0.5,
                                pos=self.door.enterWalkBackPos,
                                startPos=self.AtDoorPos),
                Func(self.loop, 'neutral'), Wait(1.0), Func(self.loop, 'walk'),
                LerpPosInterval(self,
                                duration=1.0,
                                pos=self.door.enterWalkInPos,
                                startPos=self.door.enterWalkBackPos)),
            LerpPosInterval(self,
                            duration=4.375,
                            pos=render.getRelativePoint(
                                self.door.doorNode, self.StartPosFromDoor),
                            startPos=render.getRelativePoint(
                                self.door.doorNode, self.StartPosFromDoor) +
                            (0, 0, 6.5 * 4.8)))
        self.takeOverTrack.start(ts)

    def exitTakeOver(self):
        self.doingActivity = False
        if self.takeOverTrack:
            self.takeOverTrack.pause()
            self.takeOverTrack = None
Beispiel #10
0
class Suit(Avatar):
    notify = directNotify.newCategory('Suit')

    def __init__(self):
        Avatar.__init__(self)
        self.dept = None
        self.suit = None
        self.head = None
        self.headModel = None
        self.variant = None
        self.handColor = None
        self.voice = None
        self.chat = None
        self.chatDial = None
        self.shadow = None
        self.deathSound = None
        self.propeller = None
        self.smallExp = None
        self.largeExp = None
        self.explosion = None
        self.hasSpawned = False
        self.suitTrack = None
        self.timestampAnimTrack = None
        self.propellerSounds = {}
        self.healthBar = None
        self.healthBarGlow = None
        self.condition = 0
        self.avatarType = CIGlobals.Suit
        self.suitPlan = None
        self.footstepSound = None
        self.showNametagInMargins = False
        self.surfaceProp = "metal"

        self.activities = {ACT_WAKE_ANGRY   :   WakeAngry(self),
                           ACT_SMALL_FLINCH :   Flinch(self),
                           ACT_DIE          :   Die(self),
                           ACT_VICTORY_DANCE:   VictoryDance(self),
                           ACT_COG_FLY_DOWN :   FlyDown(self)}

        self.standWalkRunReverse = [('neutral', 'walk', 0.0, 5.0, 1.0, 1.0)]

        self.gruntSound = base.audio3d.loadSfx("phase_14/audio/sfx/cog_grunt.ogg")
        base.audio3d.attachSoundToObject(self.gruntSound, self)

        self.animFSM = ClassicFSM('Suit', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('walk', self.enterWalk, self.exitWalk),
            State('die', self.enterDie, self.exitDie),
            State('win', self.enterWin, self.exitWin),
            State('flail', self.enterFlail, self.exitFlail),
            State('flyDown', self.enterFlyDown, self.exitFlyDown),
            State('flyAway', self.enterFlyAway, self.exitFlyAway),
            State('flyNeutral', self.enterFlyNeutral, self.exitFlyNeutral),
            State('trayWalk', self.enterTrayWalk, self.exitTrayWalk),
            State('trayNeutral', self.enterTrayNeutral, self.exitTrayNeutral),
            State('stunned', self.enterStunned, self.exitStunned),
            State('pie', self.enterPie, self.exitPie),
            State('drop', self.enterDrop, self.exitDrop),
            State('drop-react', self.enterDropReact, self.exitDropReact),
            State('soak', self.enterSoak, self.exitSoak),
            State('squirt-small', self.enterSquirtSmall, self.exitSquirtSmall)
        ], 'off', 'off')
        self.animFSM.enterInitialState()
        
    def getRightHandNode(self):
        return self.find("**/joint_Rhold")

    def getLeftHandNode(self):
        return self.find("**/joint_Lhold")

    def getHeadNode(self):
        return self.headModel

    def getUpperBodySubpart(self):
        return [None]

    def getLowerBodySubpart(self):
        return [None]

    def getMoveAction(self, forward, rotate, strafe):
        return 0

    def enterPie(self, ts = 0):
        self.play('pie')

    def exitPie(self):
        self.stop()

    def enterDrop(self, ts = 0):
        self.play("drop")

    def exitDrop(self):
        self.stop()

    def enterDropReact(self, ts = 0):
        self.play('drop-react')

    def exitDropReact(self):
        self.stop()

    def enterSoak(self, ts = 0):
        self.play('soak')

    def exitSoak(self):
        self.stop()

    def enterSquirtSmall(self, ts = 0):
        self.play('squirt-small')

    def exitSquirtSmall(self):
        self.stop()

    def enterStunned(self, animB4Stun, ts = 0):
        self.show()

        animB4Stun = SuitGlobals.getAnimById(animB4Stun).getName()

        self.stunnedSound = base.loadSfxOnNode("phase_4/audio/sfx/SZ_TC_bird1.ogg", self)
        self.stunnedSound.setLoop(True)
        self.stunnedSound.play()

        self.stunnedIval = Parallel(Sequence(ActorInterval(self, animB4Stun),
                                             Func(self.loop, 'stunned')),
                                    SuitGlobals.createStunInterval(self, 0, 100))
        self.stunnedIval.start()
        
    def clearStunnedIval(self):
        if hasattr(self, 'stunnedSound'):
            self.stunnedSound.stop()
            del self.stunnedSound
        if hasattr(self, 'stunnedIval'):
            self.stunnedIval.pause()
            del self.stunnedIval

    def exitStunned(self):
        self.clearStunnedIval()
        self.stop()
        
    def getLeftHand(self):
        return self.find("**/joint_Lhold")
        
    def getRightHand(self):
        return self.find("**/joint_Rhold")

    def getNametagJoints(self):
        return []

    # BEGIN STATES

    def enterOff(self, ts = 0):
        pass

    def exitOff(self):
        pass

    def exitGeneral(self):
        self.stop()

    def enterTrayWalk(self, ts = 0):
        self.show()
        self.loop('tray-walk')

    def exitTrayWalk(self):
        self.exitGeneral()

    def enterTrayNeutral(self, ts = 0):
        self.loop('tray-neutral')

    def exitTrayNeutral(self):
        self.stop()

    def enterNeutral(self, ts = 0):
        self.show()
        self.loop("neutral")

    def exitNeutral(self):
        self.exitTimestampAnimTrack()
        self.exitGeneral()

    def enterWalk(self, ts = 0):
        self.show()
        self.loop("walk")
        self.enableRay()
        self.disableShadowRay()
        self.startFootsteps()

    def exitWalk(self):
        self.stopFootsteps()
        self.exitTimestampAnimTrack()
        self.exitGeneral()
        self.enableShadowRay()

    def enterFlail(self, ts = 0):
        self.pingpong('flail', fromFrame = 30, toFrame = 35)

    def exitFlail(self):
        self.stop()

    def exitTimestampAnimTrack(self):
        if self.timestampAnimTrack:
            self.timestampAnimTrack.pause()
            self.timestampAnimTrack = None

    def enterFlyNeutral(self, ts = 0):
        self.disableRay()
        self.enableShadowRay()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['neutral']
        base.playSfx(sfx, node = self, looping = 1)
        self.propeller.loop('chan', fromFrame = 0, toFrame = 3)
        self.setPlayRate(0.8, 'land')
        self.pingpong('land', fromFrame = 0, toFrame = 10)

    def exitFlyNeutral(self):
        self.cleanupPropeller()

    def enterFlyDown(self, ts = 0):
        self.disableRay()
        self.enableShadowRay()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['in']
        base.playSfx(sfx, node = self)
        groundF = 28
        dur = self.getDuration('land')
        fr = self.getFrameRate('land')
        if fr:
            animTimeInAir = groundF / fr
        else:
            animTimeInAir = groundF
        impactLength = dur - animTimeInAir
        timeTillLanding = 6.5 - impactLength
        waitTime = timeTillLanding - animTimeInAir
        lastSpinFrame = 8
        propDur = self.propeller.getDuration('chan')
        fr = self.propeller.getFrameRate('chan')
        spinTime = lastSpinFrame / fr
        openTime = (lastSpinFrame + 1) / fr
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyDown')
        else:
            name = 'enterFlyDown'
        animTrack = Sequence(Func(self.pose, 'land', 0), Wait(waitTime), ActorInterval(self, 'land', duration=dur))
        propTrack = Parallel(SoundInterval(sfx, duration=waitTime + dur, node=self), Sequence(ActorInterval(self.propeller, 'chan', constrainedLoop=1, duration=waitTime + spinTime, startTime=0.0, endTime=spinTime), ActorInterval(self.propeller, 'chan', duration=propDur - openTime, startTime=openTime)))
        self.suitTrack = Parallel(animTrack, propTrack, name=self.taskName('flyDown'))
        if not self.hasSpawned:
            self.show()
            fadeInTrack = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Func(self.clearColorScale), Func(self.clearTransparency))
            self.hasSpawned = True
            self.suitTrack.append(fadeInTrack)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyDown)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)

    def exitFlyDown(self):
        self.cleanupPropeller()
        self.enableRay()
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.exitGeneral()

    def enterFlyAway(self, ts = 0, doFadeOut = 0):
        self.show()
        if not self.propeller:
            self.generatePropeller()
        sfx = self.propellerSounds['out']
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyAway')
        else:
            name = 'enterFlyAway'
        dur = self.getDuration('land')
        actInt = ActorInterval(self, 'land', loop=0, startTime=dur, endTime=0.0)
        lastSpinFrame = 8
        propDur = self.propeller.getDuration('chan')
        fr = self.propeller.getFrameRate('chan')
        spinTime = lastSpinFrame / fr
        openTime = (lastSpinFrame + 1) / fr
        propTrack = Parallel(SoundInterval(sfx, node=self), Sequence(Func(self.propeller.show),
            ActorInterval(self.propeller, 'chan', endTime=openTime, startTime=propDur),
            ActorInterval(self.propeller, 'chan', constrainedLoop=1, duration=propDur - openTime,
            startTime=spinTime, endTime=0.0)))
        self.suitTrack = Parallel(actInt, propTrack, name=self.taskName('trackName'))
        if doFadeOut:
            fadeOut = Sequence(Wait(4.0), Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.clearColorScale), Func(self.clearTransparency), Func(self.reparentTo, hidden))
            self.suitTrack.append(fadeOut)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyAway)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)
        self.disableRay()
        self.enableShadowRay()

    def exitFlyAway(self):
        self.cleanupPropeller()
        if self.suitTrack:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.exitGeneral()

    def enterDie(self, ts = 0):
        self.show()
        self.clearStunnedIval()
        self.generateCog(isLose = 1)
        self.nametag.clearChatText()
        self.deleteNameTag()
        self.deathSound = base.audio3d.loadSfx("phase_3.5/audio/sfx/Cog_Death_Full.ogg")
        base.audio3d.attachSoundToObject(self.deathSound, self)
        trackName = self.uniqueName('enterDie')

        smallGears = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosionSmall.ptf')
        smallGears.getParticlesNamed('particles-1').setPoolSize(30)
        smallGears.setShaderOff(1)

        singleGear = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosion.ptf')
        singleGear.getParticlesNamed('particles-1').setPoolSize(1)
        singleGear.setShaderOff(1)

        smallGearExplosion = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosion.ptf')
        smallGearExplosion.getParticlesNamed('particles-1').setPoolSize(10)
        smallGearExplosion.setShaderOff(1)

        bigGearExplosion = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosionBig.ptf')
        bigGearExplosion.getParticlesNamed('particles-1').setPoolSize(30)
        bigGearExplosion.setShaderOff(1)

        smallGears.setDepthWrite(False)
        smallGears.hide(CIGlobals.ShadowCameraBitmask)
        singleGear.setDepthWrite(False)
        singleGear.hide(CIGlobals.ShadowCameraBitmask)
        smallGearExplosion.setDepthWrite(False)
        smallGearExplosion.hide(CIGlobals.ShadowCameraBitmask)
        bigGearExplosion.setDepthWrite(False)
        bigGearExplosion.hide(CIGlobals.ShadowCameraBitmask)

        self.smallGears = smallGears
        self.smallGears.setPos(self.find('**/joint_head').getPos() + (0,0, 2))
        self.singleGear = singleGear
        self.smallGearExp = smallGearExplosion
        self.bigGearExp = bigGearExplosion

        gearTrack = Sequence(Wait(0.7), Func(self.doSingleGear), Wait(1.5), Func(self.doSmallGears), Wait(3.0), Func(self.doBigExp))
        self.suitTrack = Parallel(Sequence(Wait(0.8), SoundInterval(self.deathSound, duration = 4.28)),
                Sequence(Wait(0.7), Func(self.doSingleGear), Wait(4.5),
                Func(self.suitExplode), Wait(1.0), Func(self.disableBodyCollisions)), gearTrack,
                Sequence(ActorInterval(self, 'lose', duration = 6), Func(self.getGeomNode().hide)), name = trackName)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getName(), self.exitDie)
        if self.isDistributed():
            self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, trackName)
        self.suitTrack.start(ts)

    def doSingleGear(self):
        self.singleGear.start(self.getGeomNode())

    def doSmallGears(self):
        self.smallGears.start(self.getGeomNode())

    def doSmallExp(self):
        self.smallGearExp.start(self.getGeomNode())

    def doBigExp(self):
        self.bigGearExp.start(self.getGeomNode())

    def suitExplode(self):
        pos = self.getPart('body').find('**/joint_head').getPos(render) + (0, 0, 2)

        # Force the loser suit to use UnlitGeneric shader, workaround for the has_mat() assertion
        BSPUtility.applyUnlitOverride(self)

        CIGlobals.makeExplosion(pos, 0.5, soundVol = 0.32)

    def exitDie(self):
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getName())
            self.suitTrack.finish()
            if self.isDistributed():
                DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'singleGear'):
            self.singleGear.softStop()
            del self.singleGear
        if hasattr(self, 'smallGears'):
            self.smallGears.softStop()
            del self.smallGears
        if hasattr(self, 'smallGearExp'):
            self.smallGearExp.softStop()
            del self.smallGearExp
        if hasattr(self, 'bigGearExp'):
            self.bigGearExp.softStop()
            del self.bigGearExp
        if self.deathSound:
            self.deathSound.stop()
        self.deathSound = None

    def enterWin(self, ts = 0):
        self.play('win')

    def exitWin(self):
        self.exitGeneral()

    # END STATES

    def generateSuit(self, suitPlan, variant, voice = None, hideFirst = False):
        self.suitPlan = suitPlan
        self.suit = suitPlan.getSuitType()
        self.head = suitPlan.getHead()
        self.dept = suitPlan.getDept()
        self.handColor = suitPlan.getHandColor()
        self.variant = variant
        self.setVoice(voice)
        self.generateCog()

        mat = CIGlobals.getCharacterMaterial(shininess = 50.0, specular = (0.4, 0.4, 0.4, 1))
        self.setMaterial(mat)
        
        #ts = TextureStage('shiny')
        #ts.setMode(TextureStage.MAdd)
        #ts.setRgbScale(2)
        #tex = loader.loadCubeMap('phase_14/maps/cubemap/defaultcubemap_#.png')
        #tex = loader.loadTexture('phase_14/maps/envmap001a_cog.png')
        #self.setTexGen(ts, TexGenAttrib.MEyeSphereMap)
        #self.setTexture(ts, tex)
        
        self.initializeBodyCollisions()

        if hideFirst:
            self.hide()
        else:
            self.show()

    def __blinkRed(self, task):
        self.healthBar.setColor(SuitGlobals.healthColors[3], 1)
        self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[3], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.17)
        return task.done

    def __blinkGray(self, task):
        if not self.healthBar:
            return
        self.healthBar.setColor(SuitGlobals.healthColors[4], 1)
        self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[4], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.0)
        return task.done

    def generateHealthBar(self):
        self.removeHealthBar()
        button = loader.loadModel('phase_3.5/models/gui/matching_game_gui.bam').find('**/minnieCircle')
        button.setScale(3.0)
        button.setH(180)
        button.setColor(SuitGlobals.healthColors[0])
        chestNull = self.find('**/def_joint_attachMeter')
        if chestNull.isEmpty():
            chestNull = self.find('**/joint_attachMeter')
        button.reparentTo(chestNull)
        self.healthBar = button
        self.healthBarGlow = loader.loadModel('phase_3.5/models/props/glow.bam')
        self.healthBarGlow.reparentTo(self.healthBar)
        self.healthBarGlow.setScale(0.28)
        self.healthBarGlow.setPos(-0.005, 0.01, 0.015)
        self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[0])
        button.flattenLight()
        button.setLightOff()
        self.condition = 0
        if hasattr(self, 'getHealth'):
            self.updateHealthBar(self.getHealth())

    def updateHealthBar(self, hp):
        if not self.healthBar:
            return
        if hp > self.health:
            self.health = hp
        health = 0.0
        try:
            health = float(hp) / float(self.maxHealth)
        except: pass
        if health > 0.95:
            condition = 0
        elif health > 0.7:
            condition = 1
        elif health > 0.3:
            condition = 2
        elif health > 0.05:
            condition = 3
        elif health > 0.0:
            condition = 4
        else:
            condition = 5
        if self.condition != condition:
            taskMgr.remove(self.taskName('blink-task'))
            if condition == 4:
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            elif condition == 5:
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25), Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            else:
                self.healthBar.setColor(SuitGlobals.healthColors[condition], 1)
                self.healthBarGlow.setColor(SuitGlobals.healthGlowColors[condition], 1)
            self.condition = condition

    def removeHealthBar(self):
        if self.healthBar:
            self.healthBar.removeNode()
            self.healthBar = None
        if self.condition == 4 or self.condition == 5:
            taskMgr.remove(self.taskName('blink-task'))
        self.healthCondition = 0
        return

    def initializeBodyCollisions(self):
        self.notify.info('Initializing Body Collisions!')
        self.setupPhysics(2, self.getHeight())
        self.enableRay()

    def hideSuit(self):
        self.hide()

    def showSuit(self):
        self.show()
        fadeIn = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(0.6, colorScale=Vec4(1,1,1,1), startColorScale=Vec4(1,1,1,0)), Func(self.clearColorScale), Func(self.clearTransparency), Func(self.reparentTo, render))
        fadeIn.start()

    def doStunEffect(self):
        self.clearStunnedIval()
        self.stunnedIval = SuitGlobals.createStunInterval(self, 0, 2)
        self.stunnedIval.start()

    def doGagEffect(self, flags):
        GagEffects.doGagEffect(self, flags)

    def generateCog(self, isLose = 0, nameTag = True):
        self.cleanup()
        if not isLose:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.loadModel('phase_5/models/char/cog%s_robot-zero.bam' % (str(self.suit)), 'body')
            else:
                self.loadModel('phase_3.5/models/char/suit%s-mod.bam' % (str(self.suit)), 'body')
            animations = SuitGlobals.animations
            anims = {}
            for anim in animations:
                if not self.suit in anim.getSuitTypes():
                    continue
                path = 'phase_%s/models/char/suit%s-%s.bam' % (anim.getPhase(), self.suit, anim.getFile())
                anims[anim.getName()] = path
            self.loadAnims(anims, 'body')
            self.generateHealthBar()

            if self.suitPlan.suitType == SuitType.A:
                self.footstepSound = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_cogafssm.ogg")
            elif self.suitPlan.suitType == SuitType.B:
                self.footstepSound = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_cogbfssm.ogg")
            elif self.suitPlan.suitType == SuitType.C:
                self.footstepSound = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_cogcfssm.ogg")
            if self.footstepSound:
                base.audio3d.attachSoundToObject(self.footstepSound, self)

        else:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.loadModel('phase_5/models/char/cog%s_robot-lose-mod.bam' % (str(self.suit)), 'body')
            else:
                self.loadModel('phase_4/models/char/suit%s-lose-mod.bam' % (str(self.suit)), 'body')
            self.loadAnims({'lose' : 'phase_4/models/char/suit%s-lose.bam' % (str(self.suit))}, 'body')
        if self.variant != Variant.SKELETON:
            self.headModel = self.head.generate()
            self.headModel.reparentTo(self.find("**/joint_head"))
        if self.suitPlan.getName() == SuitGlobals.VicePresident:
            self.headModel.setScale(0.35)
            self.headModel.setHpr(270, 0, 270)
            self.headModel.setZ(-0.10)
            self.headModel.loop('neutral')
        self.setClothes()

        classScale = 1.0#self.suitPlan.getCogClassAttrs().scaleMod
        self.setAvatarScale((self.suitPlan.getScale() / SuitGlobals.scaleFactors[self.suit]) * classScale)
        #self.setHeight(self.suitPlan.getHeight())
        if nameTag:
            self.setupNameTag()

        Avatar.initShadow(self)

        # We've already done all manipulating to the cog, we can just flatten it.
        self.getPart('body').flattenStrong()
        self.postFlatten()
        self.headModel.flattenStrong()
        if isinstance(self.headModel, Actor):
            self.headModel.postFlatten()

    def cleanup(self):
        if self.footstepSound:
            self.footstepSound.stop()
        self.footstepSound = None
        self.cleanupPropeller()
        self.clearChatbox()
        if self.shadow:
            self.deleteShadow()
        if self.headModel:
            self.headModel.removeNode()
        self.headModel = None
        if self.getPart('body'):
            self.removePart('body')
        self.timestampAnimTrack = None

    def generatePropeller(self):
        self.cleanupPropeller()

        self.propeller = Actor('phase_4/models/props/propeller-mod.bam',
                               {'chan' : 'phase_4/models/props/propeller-chan.bam'})
        self.propeller.reparentTo(self.find("**/joint_head"))
        self.propellerSounds['in'] = base.audio3d.loadSfx(SuitGlobals.propellerInSfx)
        self.propellerSounds['out'] = base.audio3d.loadSfx(SuitGlobals.propellerOutSfx)
        self.propellerSounds['neutral'] = base.audio3d.loadSfx(SuitGlobals.propellerNeutSfx)
        for sound in self.propellerSounds.values():
            base.audio3d.attachSoundToObject(sound, self.propeller)

    def cleanupPropeller(self):
        for sound in self.propellerSounds.values():
            base.audio3d.detachSound(sound)
            sound.stop()
        self.propellerSounds = {}
        if self.propeller and not self.propeller.isEmpty():
            self.propeller.cleanup()
        self.propeller = None

    def setVoice(self, voice):
        if not voice:
            if self.variant == Variant.SKELETON or self.variant == Variant.ZOMBIE:
                self.voice = Voice.SKELETON
            else:
                self.voice = Voice.NORMAL
        else:
            self.voice = voice

    def setClothes(self):
        if self.variant == Variant.SKELETON:
            parts = self.findAllMatches('**/pPlane*')
            for partNum in range(0, parts.getNumPaths()):
                bb = parts.getPath(partNum)
                bb.setTwoSided(1)
            tie = 'phase_5/maps/cog_robot_tie_%s.mat' % self.dept.getTie()
            #tie.setMinfilter(Texture.FTLinearMipmapLinear)
            #tie.setMagfilter(Texture.FTLinear)
            self.find('**/tie').setBSPMaterial(tie, 1)
        else:
            prefix = 'phase_3.5/maps/' + self.dept.getClothingPrefix() + '_%s.mat'
            if self.variant == Variant.WAITER:
                prefix = 'phase_3.5/maps/waiter_m_%s.mat'
            elif self.variant == Variant.CORRODED:
                prefix = 'phase_3.5/maps/' + self.dept.getClothingPrefix() + '_rust_%s.mat'

            legTex = prefix % 'leg'
            armTex = prefix % 'sleeve'
            blazTex = prefix % 'blazer'
                
            texs = [legTex, armTex, blazTex]

            #for texture in texs:
            #    texture.setMinfilter(Texture.FTLinearMipmapLinear)
            #    texture.setMagfilter(Texture.FTLinear)

            self.find('**/legs').setBSPMaterial(legTex, 1)
            self.find('**/arms').setBSPMaterial(armTex, 1)
            self.find('**/torso').setBSPMaterial(blazTex, 1)
            
            self.find('**/hands').setBSPMaterial("phase_3.5/maps/tt_t_ene_sellbotRental_hand.mat", 1)
            
            if not self.variant == Variant.CORRODED:
                self.find('**/hands').setColor(self.handColor)
            else:
                self.find('**/hands').setColor(Variant.CORRODED_HAND_COLOR)

    def startFootsteps(self):
        classAttrs = self.suitPlan.getCogClassAttrs()

        if not self.footstepSound or not classAttrs.footsteps:
            return

        self.footstepSound.setPlayRate(classAttrs.walkMod)
        self.footstepSound.setLoop(True)
        self.footstepSound.play()

    def stopFootsteps(self):
        if not self.footstepSound:
            return

        self.footstepSound.stop()

    def setName(self, nameString, charName):
        Avatar.setName(self, nameString, charName = charName, createNow = 1)

    def setupNameTag(self, tempName = None):
        Avatar.setupNameTag(self, tempName = tempName)
        if self.nametag:
            if self.level > 0:
                self.nametag.setText(self.nametag.getText() + '\n%s\nLevel %s %s' % (self.dept.getName(), self.level, self.suitPlan.getCogClassName()))
            else:
                self.nametag.setText(self.nametag.getText() + '\n%s' % (self.dept.getName()))

    def chatStompComplete(self, chatString):
        if not CIGlobals.getSettingsMgr().getSetting("chs").getValue():
            return

        self.chat = chatString
        chatDial = None
        questionDial = self.voice.getSoundFile('question')
        question02Dial = None
        gruntDial = self.voice.getSoundFile('grunt')
        statementDial = self.voice.getSoundFile('statement')
        if self.voice == Voice.NORMAL:
            question02Dial = self.voice.getSoundFile('question_2')

        if '!' in self.chat:
            chatDial = base.audio3d.loadSfx(gruntDial)
        elif '?' in self.chat:
            questionDials = [questionDial]
            if self.voice == Voice.NORMAL:
                questionDials.append(question02Dial)
            chatDial = base.audio3d.loadSfx(random.choice(questionDials))
        else:
            chatDial = base.audio3d.loadSfx(statementDial)
        self.chatDial = chatDial

        # Make the voice higher/lower based on the class
        voiceMod = self.suitPlan.getCogClassAttrs().voiceMod
        self.chatDial.setPlayRate(voiceMod)

        if self.variant == Variant.SKELETON:
            base.audio3d.attachSoundToObject(self.chatDial, self)
        else:
            base.audio3d.attachSoundToObject(self.chatDial, self.headModel)
        base.playSfx(self.chatDial, node = self)

    def setChat(self, chat):
        self.clearChatbox()
        Avatar.setChat(self, chat)
        self.chat = chat

    def clearChatbox(self):
        self.clearChat()
        self.chat = None
        if self.chatDial:
            base.audio3d.detachSound(self.chatDial)
            self.chatDial.stop()
            self.chatDial = None

    def getDept(self):
        return self.dept

    def getVariant(self):
        return self.variant

    def disable(self):
        if self.suitTrack:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.animFSM.requestFinalState()
        self.cleanup()
        Avatar.disable(self)

    def delete(self):
        Avatar.delete(self)
        self.cleanup()
Beispiel #11
0
class TrainController:
    """Object to control the locomotive.

    Implements changing the locomotive speed and animation.
    Also manages moving the locomotive along motion paths.

    Args:
        model (panda3d.core.NodePath): The locomotive model.
    """
    def __init__(self, model):
        self._model = model
        self._move_snd, self._stop_snd, self._brake_snd = self._set_sounds(
            model)

        self._move_anim_int = model.actorInterval("move_forward", playRate=14)
        self._move_anim_int.loop()
        # parallel with the Train model and camera move intervals
        self._move_par = Parallel()
        self._is_stopped = False
        self._outing_available = None
        self._move_snd_volume = 1
        self._garland = None
        self._confetti = None
        self._confetti_cooldown = False
        self._confetti_snd = None

        self.on_et = False
        self.critical_damage = False
        self.max_speed = 1

    @property
    def current_speed(self):
        """Current locomotive speed.

        Returns
            float: Current locomotive speed (play rate).
        """
        return 0 if self._is_stopped else self._move_anim_int.getPlayRate()

    def _change_speed(self, diff, task):
        """Actually change the locomotive speed.

        Args:
            diff (float): Coefficient to change the Train speed.
        """
        if self._is_stopped and diff > 0:
            self.start_move()

        new_rate = round(self._move_anim_int.getPlayRate() + diff, 2)
        if (self.on_et  # don't stop on enemy territory
                and diff < 0
                # stop on enemy territory only
                # in case of critical damage
                and not self.critical_damage and new_rate < MIN_SPEED):
            return task.again

        if new_rate > self.max_speed and diff > 0:
            return task.again

        # change speed
        if 0 < new_rate <= 1:
            self._move_anim_int.setPlayRate(new_rate)
            self._move_par.setPlayRate(new_rate)

            new_snd_rate = new_rate * 1.2
            if 0.25 <= new_snd_rate <= 1:
                self._move_snd.setPlayRate(new_snd_rate)

            return task.again

        if new_rate == 0:
            self._stop_move()

        return task.done

    def _change_speed_delayed(self, diff):
        """Start changing the locomotive speed.

        To make speed changing smoother delayed task is used.

        Args:
            diff (float): Coefficient to change speed.
        """
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._change_speed,
            "change_train_speed",
            extraArgs=[diff],
            appendTask=True,
        )

    def _finish_stopping(self, task):
        """Finish stopping the damaged Train."""
        taskMgr.remove("stop_train")  # noqa: F821
        base.train.stop_sparks()  # noqa: F821

        base.main_menu.show(is_game_over=True)  # noqa: F821
        return task.done

    def _set_sounds(self, model):
        """Set interactive locomotive sounds.

        Args:
            model (panda3d.core.NodePath): The locomotive model.

        Returns:
            (panda3d.core.AudioSound...):
                Interactive locomotive sounds.
        """
        move_snd = base.sound_mgr.loadSfx(
            "sounds/train/ride.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(move_snd, model)  # noqa: F821
        move_snd.setLoop(True)
        move_snd.setVolume(0)
        move_snd.play()

        stop_snd = base.sound_mgr.loadSfx(
            "sounds/train/stop.ogg")  # noqa: F821
        base.sound_mgr.attachSoundToObject(stop_snd, model)  # noqa: F821

        brake_snd = base.sound_mgr.loadSfx(
            "sounds/train/brake.ogg")  # noqa: F821
        brake_snd.setLoop(True)
        base.sound_mgr.attachSoundToObject(brake_snd, model)  # noqa: F821
        return move_snd, stop_snd, brake_snd

    def start_move_sound(self):
        """Start playing the move sound."""
        self._move_snd.setVolume(1)

    def _shot_flapper(self):
        """Play an effect and sound of a flapper."""
        if not self._confetti_cooldown and self._confetti is not None:
            for effect in self._confetti:
                effect.start(self._model, render)  # noqa: F821
                effect.softStart()

            taskMgr.doMethodLater(  # noqa: F821
                1.97, self._stop_flapper, "stop_flapper")
            self._confetti_cooldown = True
            self._confetti_snd.play()

    def _stop_flapper(self, task):
        """Stop all the flapper effects."""
        for effect in self._confetti or []:
            effect.softStop()

        self._confetti_cooldown = False
        return task.done

    def _stop_move(self):
        """Stop the Train movement."""
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._stop_snd.play,
            "train_stop_snd",
            extraArgs=[])
        base.train.stop_sparks()  # noqa: F821
        self._move_par.pause()
        self._move_anim_int.pause()
        taskMgr.doMethodLater(  # noqa: F821
            0.7,
            base.train.stop,
            "release_steam",
            extraArgs=[]  # noqa: F821
        )

        self._is_stopped = True
        taskMgr.doMethodLater(  # noqa: F821
            0.07,
            drown_snd,
            "drown_move_snd",
            extraArgs=[self._move_snd],
            appendTask=True,
        )
        if self._outing_available:
            base.world.start_outing(self._outing_available)  # noqa: F821
            self._outing_available = None

    def _switch_garland(self):
        """Christmas related method.

        Enables or removes the locomotive garland
        and an ability to shot a flapper.
        """
        if self._garland is None:
            self._garland = loader.loadModel(address("garland"))  # noqa: F821
            self._garland.reparentTo(self._model)

            self._confetti = []

            for color in ("red", "blue", "green"):
                confetti = ParticleEffect()
                confetti.loadConfig("effects/confetti_{}.ptf".format(color))
                confetti.setPos(0, 0.32, 0.29)
                self._confetti.append(confetti)

            self._confetti_snd = base.sound_mgr.loadSfx(  # noqa: F821
                "sounds/train/flapper.ogg")
            base.sound_mgr.attachSoundToObject(  # noqa: F821
                self._confetti_snd, self._model)
        else:
            self._garland.removeNode()
            self._garland = None

            for effect in self._confetti:
                effect.softStop()
                effect.cleanup()

            self._confetti = None
            base.sound_mgr.detach_sound(self._confetti_snd)  # noqa: F821
            self._confetti_snd = None

    def brake_down_to(self, target):
        """Slow down the Train to the given speed.

        Args:
            target (float): Target speed.
        """
        if self._brake_snd.status() == AudioSound.PLAYING:
            self._brake_snd.setVolume(1)
        else:
            self._brake_snd.play()
            taskMgr.doMethodLater(  # noqa: F821
                3,
                drown_snd,
                "drown_brake_snd",
                extraArgs=[self._brake_snd],
                appendTask=True,
            )
        self.slow_down_to(target)

    def drown_move_snd(self):
        """Reduce the main move sound volume."""
        if self._move_snd_volume == 1:
            self._move_snd.setVolume(0.5)
            self._move_snd_volume = 0.5

    def move_along_block(self, block, train_np, do_turn):
        """Start the locomotive move intervals for the given block.

        There are two intervals: the locomotive movement
        and synchronous camera movement.

        Args:
            block (world.block.Block):
                The World block to move along.
            train_np (panda3d.core.NodePath): Train node.
            do_turn (int):
                0 if default direction was chosen,
                1 if a turn is needed.
        """
        self.on_et = block.enemy_territory
        self._outing_available = block.outing_available
        # use speed value from the last block
        rate = self._move_par.getPlayRate() if self._move_par else 1

        is_fork = block.name in ("r_fork", "l_fork", "exit_from_fork")

        self._move_par = Parallel(
            MopathInterval(  # locomotive movement
                block.path[do_turn] if is_fork else block.path,
                self._model,
                duration=4.4,
                name="current_path",
            ),
            MopathInterval(  # camera movement
                block.cam_path[do_turn] if is_fork else block.cam_path,
                train_np,
                duration=4.4,
                name="current_camera_path",
            ),
        )
        self._move_par.setDoneEvent("block_finished")
        self._move_par.start()
        self._move_par.setPlayRate(rate)

    def load_speed(self, speed):
        """Load previously saved locomotive speed.

        Args:
            speed (float): Rate to set for animation, move and sounds.
        """
        self._move_par.setPlayRate(speed)
        self._move_anim_int.setPlayRate(speed)
        self._move_snd.setPlayRate(min(max(0.25, speed * 1.2), 1))
        if not speed:
            self._move_snd.stop()
            self._is_stopped = True

    def pause_movement(self):
        """Make a movement pause (used when a tutorial page is shown)."""
        self._move_par.pause()
        self._move_anim_int.pause()
        self._move_snd.stop()

    def set_controls(self, train):
        """Configure the locomotive control keys.

        Args:
            train (train.Train): The locomotive object.
        """
        # speed smoothly changes while holding w/s keys
        base.accept("w", self._change_speed_delayed, [0.05])  # noqa: F821
        base.accept("s", self._change_speed_delayed, [-0.05])  # noqa: F821
        base.accept("w-up", taskMgr.remove,
                    ["change_train_speed"])  # noqa: F821
        base.accept("s-up", taskMgr.remove,
                    ["change_train_speed"])  # noqa: F821
        base.accept("7", self._switch_garland)  # noqa: F821
        base.accept("8", self._shot_flapper)  # noqa: F821
        base.accept("9", base.effects_mgr.love_fog.switch)  # noqa: F821

        base.accept("f", train.toggle_lights)  # noqa: F821

    def start_move(self):
        """Start the Train movement."""
        self._move_par.resume()
        self._move_anim_int.resume()
        self._move_snd.play()
        self._is_stopped = False

    def speed_to_min(self):
        """Accelerate to minimum speed.

        Used when locomotive got on enemy territory.
        """
        taskMgr.remove("change_train_speed")  # noqa: F821
        speed = self._move_anim_int.getPlayRate()
        if speed >= MIN_SPEED:
            return

        # calculate acceleration length
        acc_steps = (MIN_SPEED - speed) / 0.05

        # start accelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._change_speed,
            "speed_up_train",
            extraArgs=[0.05],
            appendTask=True)
        # stop accelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6 * acc_steps + 0.2,
            taskMgr.remove,  # noqa: F821
            "stop_speedind_up",
            extraArgs=["speed_up_train"],
        )

    def slow_down_to(self, target):
        """Slow down the locomotive to the given speed.

        Args:
            target (float): Target speed.
        """
        taskMgr.remove("change_train_speed")  # noqa: F821

        speed = self._move_anim_int.getPlayRate()
        if speed <= target:
            return

        # calculate deceleration length
        acc_steps = (speed - target) / 0.05

        # start decelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6,
            self._change_speed,
            "slow_down_train",
            extraArgs=[-0.05],
            appendTask=True,
        )
        # stop decelerating
        taskMgr.doMethodLater(  # noqa: F821
            0.6 * acc_steps + 0.2,
            taskMgr.remove,  # noqa: F821
            "stop_slowing_down",
            extraArgs=["slow_down_train"],
        )

    def stop(self, urgent=False, place_of_interest=False):
        """Completely stop the locomotive.

        Args:
            urgent (bool):
                If True, deceleration speed will be much higher.
                Used for stopping in places of interest and cities.
            place_of_interest (bool):
                If True, that means the stop is
                initiated by a place of interest.
        """
        base.ignore("w")  # noqa: F821
        base.ignore("s")  # noqa: F821
        taskMgr.remove("change_train_speed")  # noqa: F821

        delay = 0.25 if urgent else 0.6

        # calculate deceleration length
        speed = self._move_anim_int.getPlayRate()
        taskMgr.doMethodLater(  # noqa: F821
            delay,
            self._change_speed,
            "stop_train",
            extraArgs=[-0.05],
            appendTask=True)

        if urgent:
            if place_of_interest:
                taskMgr.doMethodLater(  # noqa: F821
                    delay * (speed / 0.05) + 0.8,
                    base.scenario.start_chapter,  # noqa: F821
                    "start_scenario_chapter",
                )
        else:
            # stop decelerating
            taskMgr.doMethodLater(  # noqa: F821
                delay * (speed / 0.05) + 0.8, self._finish_stopping,
                "finish_stopping")
            taskMgr.doMethodLater(  # noqa: F821
                delay * (speed / 0.05) + 0.2,
                base.world.enemy.stop_ride_anim,  # noqa: F821
                "stop_riding",
            )

    def raise_move_snd(self):
        """Restore full volume of the main move sound."""
        if self._move_snd_volume == 0.5:
            self._move_snd.setVolume(1)
            self._move_snd_volume = 1

    def silence_move_snd(self):
        """Silence the movement sound."""
        self._stop_snd.setVolume(0)
        taskMgr.doMethodLater(  # noqa: F821
            0.1,
            drown_snd,
            "stop_snd",
            extraArgs=[self._move_snd],
            appendTask=True)

    def unset_controls(self):
        """Disable all the locomotive controls."""
        for key in ("w", "s", "w-up", "s-up", "f"):
            base.ignore(key)  # noqa: F821
Beispiel #12
0
class Movement(DirectObject.DirectObject):
    def __init__(self, parent):
        self.parent = parent

        self.hovered_unit_id = None
        self.hovered_tile = None
        self.hovered_compass_tile = None

        self.mouse_node = aspect2d.attachNewNode('mouse_node')
        self.move_node = aspect2d.attachNewNode('move_node')
        self.move_outline_node = NodePath('')
        self.move_np_list = []
        self.target_info_node = None

        self.accept("mouse1", self.mouseLeftClick)
        self.accept("mouse1-up", self.mouseLeftClickUp)

        #taskMgr.add(self.rayupdate, 'rayupdate_task')
        taskMgr.add(self.pickerTask, 'picker_task')
        taskMgr.add(self.positionTask, 'position_task', sort=2)

        self.color_scale_parallel = None

        # Create movement compass nodes
        self.turn_node = NodePath('turn_node')
        self.turn_node.setTag('show', '0')
        self.turn_np_list = []
        for i in xrange(9):
            text = TextNode('node name')
            text.setAlign(TextNode.ACenter)
            if i == 0:
                text.setText('NW')
                key = utils.HEADING_NW
            elif i == 1:
                text.setText('N')
                key = utils.HEADING_N
            elif i == 2:
                text.setText('NE')
                key = utils.HEADING_NE
            elif i == 3:
                text.setText('W')
                key = utils.HEADING_W
            elif i == 4:
                text.setText('E')
                key = utils.HEADING_E
            elif i == 5:
                text.setText('SW')
                key = utils.HEADING_SW
            elif i == 6:
                text.setText('S')
                key = utils.HEADING_S
            elif i == 7:
                text.setText('SE')
                key = utils.HEADING_SE
            elif i == 8:
                text.setText('X')
                key = utils.HEADING_NONE
            textNodePath = self.turn_node.attachNewNode(text)
            textNodePath.setColor(1, 1, 1)
            textNodePath.setScale(0.06)
            textNodePath.setTag('key', str(key))
            self.turn_np_list.append(textNodePath)

    def calcUnitAvailMove(self, unit_id):
        """Displays visual indicator of tiles which are in movement range of the selected unit."""
        # First delete old movement list
        for c in self.move_np_list:
            c.removeNode()
        self.move_np_list = []
        # Start calculation of new list
        unit = self.parent.local_engine.units[unit_id]
        if self.parent.turn_player != self.parent.player_id:
            return
        if unit:
            unit['move_dict'] = getMoveDict(unit,
                                            self.parent.local_engine.level,
                                            self.parent.local_engine.units)
            self.parent.local_engine.units[unit_id]['move_dict'] = unit[
                'move_dict']
            move_dict = unit['move_dict']
            for tile in move_dict:
                text = TextNode('node name')
                text.setText("%s" % move_dict[tile])
                text.setAlign(TextNode.ACenter)
                textNodePath = self.move_node.attachNewNode(text)
                textNodePath.setColor(1, 1, 1)
                textNodePath.setScale(0.06)
                textNodePath.setPythonTag('pos', tile)
                pos2d = utils.pointCoordIn2d(
                    Point3(utils.TILE_SIZE * (tile[0] + 0.5),
                           utils.TILE_SIZE * (tile[1] + 0.5),
                           utils.GROUND_LEVEL))
                textNodePath.setPos(pos2d)
                self.move_np_list.append(textNodePath)

    def showUnitAvailMove(self):
        self.move_node.reparentTo(aspect2d)
        self.hovered_tile = None
        #self.drawMoveOutline(self.calcMoveOutline(move_dict, self.parent.local_engine.units[unit_id]['pos']))

    def hideUnitAvailMove(self):
        self.move_node.detachNode()

    """
    def calcMoveOutline(self, move_dict, pos):
        outline = {}
        for tile in move_dict:
            dir = []
            if not (tile[0]-1, tile[1]) in move_dict and (tile[0]-1, tile[1]) != pos:
                dir.append('W')
            if not (tile[0], tile[1]-1) in move_dict and (tile[0], tile[1]-1) != pos:
                dir.append('S')
            if not (tile[0]+1, tile[1]) in move_dict and (tile[0]+1, tile[1]) != pos:
                dir.append('E')                
            if not (tile[0], tile[1]+1) in move_dict and (tile[0], tile[1]+1) != pos:
                dir.append('N')
            if dir != []:
                outline[tile] = dir
        return outline
    
    def drawMoveOutline(self, outline):
        self.move_outline_node.removeNode()
        zpos = utils.GROUND_LEVEL + 0.01
        off = 0.1
        segs = LineSegs()
        segs.setThickness(3)
        segs.setColor(Vec4(0.686,1,0.992,1))
        for tile in outline:
            for dir in outline[tile]:
                if dir == 'N':
                    d1 = 0
                    d2 = 0
                    if (tile[0]+1, tile[1]) in outline and 'N' in outline[(tile[0]+1, tile[1])]:
                        d2 = off
                    elif (tile[0]+1, tile[1]+1) in outline:
                        d2 = 2*off
                    if (tile[0]-1, tile[1]) in outline and 'N' in outline[(tile[0]-1, tile[1])]:
                        d1 = off
                    elif (tile[0]-1, tile[1]+1) in outline:
                        d1 = 2*off
                    segs.moveTo(tile[0]+off-d1, tile[1]+1-off, zpos)
                    segs.drawTo(tile[0]+1-off+d2, tile[1]+1-off, zpos)
                elif dir == 'S':
                    d1 = 0
                    d2 = 0
                    if (tile[0]+1, tile[1]) in outline and 'S' in outline[(tile[0]+1, tile[1])]:
                        d2 = off
                    elif (tile[0]+1, tile[1]-1) in outline:
                        d2 = 2*off
                    if (tile[0]-1, tile[1]) in outline and 'S' in outline[(tile[0]-1, tile[1])]:
                        d1 = off
                    elif (tile[0]-1, tile[1]-1) in outline:
                        d1 = 2*off                  
                    segs.moveTo(tile[0]+off-d1, tile[1]+off, zpos)
                    segs.drawTo(tile[0]+1-off+d2, tile[1]+off, zpos)
                elif dir == 'W':
                    d1 = 0
                    d2 = 0
                    if (tile[0], tile[1]+1) in outline and 'W' in outline[(tile[0], tile[1]+1)]:
                        d2 = off
                    elif (tile[0]-1, tile[1]+1) in outline:
                        d2 = 2*off
                    if (tile[0], tile[1]-1) in outline and 'W' in outline[(tile[0], tile[1]-1)]:
                        d1 = off                     
                    elif (tile[0]-1, tile[1]-1) in outline:
                        d1 = 2*off
                    segs.moveTo(tile[0]+off, tile[1]+off-d1, zpos)
                    segs.drawTo(tile[0]+off, tile[1]+1-off+d2, zpos)
                elif dir == 'E':
                    d1 = 0
                    d2 = 0
                    if (tile[0], tile[1]+1) in outline and 'E' in outline[(tile[0], tile[1]+1)]:
                        d2 = off
                    elif (tile[0]+1, tile[1]+1) in outline:
                        d2 = 2*off
                    if (tile[0], tile[1]-1) in outline and 'E' in outline[(tile[0], tile[1]-1)]:
                        d1 = off                     
                    elif (tile[0]+1, tile[1]-1) in outline:
                        d1 = 2*off                   
                    segs.moveTo(tile[0]+1-off, tile[1]+off-d1, zpos)
                    segs.drawTo(tile[0]+1-off, tile[1]+1-off+d2, zpos)                                        
        self.move_outline_node = render.attachNewNode(segs.create())
        self.move_outline_node.setBin("fixed", 40)
        #self.move_outline_node.setDepthTest(False)
        #self.move_outline_node.setDepthWrite(False)
        self.move_outline_node.setLightOff()  
    """

    def showMoveCompass(self, dest):
        # Calculate postion of compass nodes based on destination tile
        for i in self.turn_np_list:
            i.setPythonTag('pos', utils.getHeadingTile(i.getTag('key'), dest))
        # Show compass nodes
        self.turn_node.reparentTo(aspect2d)
        self.turn_node.setPythonTag('pos', dest)
        self.turn_node.setTag('show', '1')

        # Hide move nodes
        self.move_node.detachNode()

    def hideMoveCompass(self):
        # Hide compass nodes
        self.turn_node.detachNode()
        self.turn_node.setTag('show', '0')
        if self.hovered_compass_tile != None:
            self.hovered_compass_tile.setColor(1, 1, 1)
            self.hovered_compass_tile.setScale(0.05)
            self.color_scale_parallel.pause()
            self.hovered_compass_tile = None

    def mouseLeftClick(self):
        if self.parent.interface.hovered_gui != None:
            return

        if self.hovered_unit_id != None:
            unit_id = int(self.hovered_unit_id)
            pickedCoord = self.parent.local_engine.getCoordsByUnit(unit_id)
            # Player can only select his own units
            if self.parent.local_engine.isThisMyUnit(unit_id):
                if unit_id != self.parent.sel_unit_id:
                    self.parent.selectUnit(unit_id)
                else:
                    # Remember movement tile so we can send orientation message when mouse is depressed
                    # Do this only if it is our turn
                    if self.parent.player_id == self.parent.turn_player:
                        self.unit_move_destination = pickedCoord
                        self.showMoveCompass(self.unit_move_destination)
            elif self.parent.local_engine.isThisEnemyUnit(unit_id):
                if self.parent.sel_unit_id != None and self.parent.player_id == self.parent.turn_player:
                    self.parent.render_manager.unit_renderer_dict[
                        self.parent.
                        sel_unit_id].target_unit = self.parent.render_manager.unit_renderer_dict[
                            unit_id]
                    if self.parent._anim_in_process == False:
                        ClientMsg.shoot(self.parent.sel_unit_id, unit_id)

        elif self.hovered_tile != None:
            if self.parent.sel_unit_id != None and self.parent.player_id == self.parent.turn_player:
                # Remember movement tile so we can send movement message when mouse is depressed
                # Do this only if it is our turn
                move_coord = self.hovered_tile.getPythonTag('pos')
                if self.parent.local_engine.units[self.parent.sel_unit_id][
                        'move_dict'].has_key(move_coord):
                    self.unit_move_destination = move_coord
                    self.showMoveCompass(self.unit_move_destination)

    def mouseLeftClickUp(self):
        """Handles left mouse click actions when mouse button is depressed.
           Used for unit movement.
        """
        o = None
        if self.hovered_compass_tile != None:
            x = self.turn_node.getPythonTag('pos')[0]
            y = self.turn_node.getPythonTag('pos')[1]
            orientation = int(self.hovered_compass_tile.getTag('key'))
            if orientation == utils.HEADING_NW:
                o = Point2(x - 1, y + 1)
            elif orientation == utils.HEADING_N:
                o = Point2(x, y + 1)
            elif orientation == utils.HEADING_NE:
                o = Point2(x + 1, y + 1)
            elif orientation == utils.HEADING_W:
                o = Point2(x - 1, y)
            elif orientation == utils.HEADING_E:
                o = Point2(x + 1, y)
            elif orientation == utils.HEADING_SW:
                o = Point2(x - 1, y - 1)
            elif orientation == utils.HEADING_S:
                o = Point2(x, y - 1)
            elif orientation == utils.HEADING_SE:
                o = Point2(x + 1, y - 1)
            else:
                o = None

        self.hideMoveCompass()
        if o != None:
            ClientMsg.move(self.parent.sel_unit_id, (x, y), (o.x, o.y))

    def positionTask(self, task):
        # If turn node is displayed, you have to cancel turning or turn to be able to pick another unit or another tile to move to
        if self.turn_node.getTag('show') == '1':
            for tile in self.turn_np_list:
                pos = Point3(
                    utils.TILE_SIZE * (tile.getPythonTag('pos')[0] + 0.5),
                    utils.TILE_SIZE * (tile.getPythonTag('pos')[1]) + 0.5,
                    utils.GROUND_LEVEL)
                pos2d = utils.pointCoordIn2d(pos)
                tile.setPos(pos2d)
        else:
            for unit in self.parent.render_manager.unit_renderer_dict.itervalues(
            ):
                p = utils.nodeCoordIn2d(unit.model)
                unit.node_2d.setPos(p)
            for tile in self.move_np_list:
                pos = Point3(
                    utils.TILE_SIZE * (tile.getPythonTag('pos')[0] + 0.5),
                    utils.TILE_SIZE * (tile.getPythonTag('pos')[1] + 0.5),
                    utils.GROUND_LEVEL)
                pos2d = utils.pointCoordIn2d(pos)
                tile.setPos(pos2d)
        return task.cont

    def pickerTask(self, task):
        if self.parent._anim_in_process:
            self.hovered_unit_id = None
            self.hovered_tile = None
            self.hovered_compass_tile = None
            return task.cont

        if self.parent.interface.hovered_gui != None:
            if self.hovered_unit_id != None:
                self.parent.render_manager.unit_marker_renderer.unmarkHovered(
                    self.hovered_unit_id)
                self.hovered_unit_id = None
            if self.hovered_tile != None and not self.hovered_tile.isEmpty():
                self.hovered_tile.setColor(1, 1, 1)
                self.hovered_tile.setScale(0.05)
                self.color_scale_parallel.pause()
            if self.hovered_compass_tile != None and not self.hovered_compass_tile.isEmpty(
            ):
                self.hovered_compass_tile.setColor(1, 1, 1)
                self.hovered_compass_tile.setScale(0.05)
                self.color_scale_parallel.pause()
            return task.cont

        min_distance = 0.5
        min_unit_id = None
        min_tile = None
        min_compass_tile = None
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            r2d = Point3(mpos.getX(), 0, mpos.getY())
            a2d = aspect2d.getRelativePoint(render2d, r2d)
            self.mouse_node.setPos(a2d)

            # At the end of all this we have:
            # - min_distance to a unit or a movement node, IF this distance is lower than 0.5 which is set at the beginning
            # - min_unit_id - ID of unit which is closest to the mouse, or None if there is a movement tile closer, or if everything is further than 0.5
            # - min_tile - nodepath of the movement tile closest to the mouse, or None if there is a unit closer, or if evertythin is further than 0.5
            if self.turn_node.getTag('show') == '1':
                # Get 2d coordinates for every compass node
                for tile in self.turn_np_list:
                    # We cannot do nodepath.getDistance() because tiles are reparented to self.turn_node and are in different coord space than mouse node
                    # So we do simple vector math to get distance
                    d = Vec3(self.mouse_node.getPos() - tile.getPos()).length()
                    if d < min_distance:
                        min_distance = d
                        min_unit_id = None
                        min_tile = None
                        min_compass_tile = tile
            else:
                # Get 2d coordinates of all units
                for unit_id in self.parent.render_manager.unit_renderer_dict:
                    unit = self.parent.render_manager.unit_renderer_dict[
                        unit_id]
                    if self.parent.local_engine.isThisMyUnit(unit_id):
                        # Calculate distance between every friendly unit and mouse cursor and remember closest unit
                        d = self.mouse_node.getDistance(unit.node_2d)
                        if d < min_distance:
                            min_distance = d
                            min_unit_id = unit_id
                    else:
                        # Calculate distance between every enemy unit and mouse cursor and remember closest unit
                        d = self.mouse_node.getDistance(unit.node_2d)
                        # To target enemy unit, distance has to be even smaller than needed for regular selection/movement
                        if d < min_distance and d < 0.1:
                            min_distance = d
                            min_unit_id = unit_id

                # Get 2d coordinates for every movement node of the selected unit
                for tile in self.move_np_list:
                    # We cannot do nodepath.getDistance() because tiles are reparented to self.move_node and are in different coord space than mouse node
                    # So we do simple vector math to get distance
                    d = Vec3(self.mouse_node.getPos() - tile.getPos()).length()
                    if d < min_distance:
                        min_distance = d
                        min_unit_id = None
                        min_tile = tile

        # If a unit is the closest to the mouse:
        # - hide movement nodes (Incubation style!)
        # - unmark last hovered unit
        # - unmark last hovered tile
        # - mark new hovered unit
        # TODO: ogs: potencijalna optimizacija da se provjeri da li je self.hovered_unit_id = min_unit_id, tada ne treba unmark+mark
        if min_compass_tile != None:
            if self.hovered_tile != None and not self.hovered_tile.isEmpty():
                self.hovered_tile.setColor(1, 1, 1)
                self.hovered_tile.setScale(0.05)
                self.color_scale_parallel.pause()
                self.hovered_tile = None
            if min_compass_tile != self.hovered_compass_tile:
                if self.hovered_compass_tile != None and not self.hovered_compass_tile.isEmpty(
                ):
                    self.hovered_compass_tile.setColor(1, 1, 1)
                    self.hovered_compass_tile.setScale(0.05)
                    self.color_scale_parallel.pause()
                min_compass_tile.setColor(1, 0, 0)
                s1 = Sequence(
                    LerpColorInterval(min_compass_tile, 0.5, (1, 1, 0, 1)),
                    LerpColorInterval(min_compass_tile, 0.5, (1, 0, 0, 1)))
                s2 = Sequence(LerpScaleInterval(min_compass_tile, 0.5, (0.1)),
                              LerpScaleInterval(min_compass_tile, 0.5, (0.05)))
                self.color_scale_parallel = Parallel(s1, s2)
                self.color_scale_parallel.loop()
            self.hovered_compass_tile = min_compass_tile
        elif min_unit_id != None:
            self.move_node.detachNode()
            if min_unit_id != self.hovered_unit_id and self.hovered_unit_id != None:
                if self.parent.render_manager.unit_renderer_dict.has_key(
                        int(self.hovered_unit_id)):
                    self.parent.render_manager.unit_marker_renderer.unmarkHovered(
                        self.hovered_unit_id)
            self.parent.render_manager.unit_marker_renderer.markHovered(
                min_unit_id)
            if self.hovered_tile != None and not self.hovered_tile.isEmpty():
                self.hovered_tile.setColor(1, 1, 1)
                self.hovered_tile.setScale(0.05)
                self.color_scale_parallel.pause()
                self.hovered_tile = None
            self.hovered_unit_id = min_unit_id
        # If a movement tile is closest to the mouse:
        # - unmark last hovered unit
        # - unmark last marked tile
        # - show movement nodes (Incubation style!)
        # - mark new hovered tile
        elif min_tile != None:
            if self.hovered_unit_id != None:
                if self.parent.render_manager.unit_renderer_dict.has_key(
                        int(self.hovered_unit_id)):
                    self.parent.render_manager.unit_marker_renderer.unmarkHovered(
                        self.hovered_unit_id)
                self.hovered_unit_id = None
            if min_tile != self.hovered_tile:
                self.move_node.reparentTo(aspect2d)
                if self.hovered_tile != None and not self.hovered_tile.isEmpty(
                ):
                    self.hovered_tile.setColor(1, 1, 1)
                    self.hovered_tile.setScale(0.05)
                    self.color_scale_parallel.pause()
                min_tile.setColor(1, 0, 0)
                s1 = Sequence(LerpColorInterval(min_tile, 0.5, (1, 1, 0, 1)),
                              LerpColorInterval(min_tile, 0.5, (1, 0, 0, 1)))
                s2 = Sequence(LerpScaleInterval(min_tile, 0.5, (0.1)),
                              LerpScaleInterval(min_tile, 0.5, (0.05)))
                self.color_scale_parallel = Parallel(s1, s2)
                self.color_scale_parallel.loop()
            self.hovered_tile = min_tile
        # If neither any of the units nor any of the movement tiles is closer than 0.5:
        # - unmark last hovered unit
        # - unmark last hovered tile
        else:
            if self.hovered_unit_id != None:
                if self.parent.render_manager.unit_renderer_dict.has_key(
                        int(self.hovered_unit_id)):
                    self.parent.render_manager.unit_marker_renderer.unmarkHovered(
                        self.hovered_unit_id)
                self.hovered_unit_id = None
            if self.hovered_tile != None and not self.hovered_tile.isEmpty():
                self.hovered_tile.setColor(1, 1, 1)
                self.hovered_tile.setScale(0.05)
                self.color_scale_parallel.pause()
                self.hovered_tile = None
            if self.hovered_compass_tile != None and not self.hovered_compass_tile.isEmpty(
            ):
                self.hovered_compass_tile.setColor(1, 1, 1)
                self.hovered_compass_tile.setScale(0.05)
                self.color_scale_parallel.pause()
                self.hovered_compass_tile = None
        return task.cont