Пример #1
0
class Player(GameObject, ArmedObject):
    def __init__(self, shipSpec):
        GameObject.__init__(self, Vec3(0, 0,
                                       0), None, None, shipSpec.maxShields,
                            shipSpec.maxSpeed, "player", MASK_INTO_PLAYER, 2)
        ArmedObject.__init__(self)

        self.acceleration = shipSpec.acceleration
        self.turnRate = shipSpec.turnRate

        self.numGuns = len(shipSpec.gunPositions)
        self.numMissiles = shipSpec.numMissiles
        self.maxEnergy = shipSpec.maxEnergy
        self.energyRechargeRate = shipSpec.energyRechargeRate
        self.shieldRechargeRate = shipSpec.shieldRechargeRate

        self.energy = shipSpec.maxEnergy

        for gunPos in shipSpec.gunPositions:
            np = self.actor.attachNewNode(PandaNode("gun node"))
            np.setPos(gunPos)

            gun = BlasterWeapon()
            self.addWeapon(gun, 0, np)

        missileSetCounter = 1
        for missilePos in shipSpec.missilePositions:
            np = self.actor.attachNewNode(PandaNode("missile node"))
            np.setPos(missilePos)

            gun = RocketWeapon()
            self.addWeapon(gun, missileSetCounter, np)
            missileSetCounter += 1

        self.numMissileSets = missileSetCounter - 1
        self.missileSetIndex = 0

        light = PointLight("basic light")
        light.setColor(Vec4(1, 1, 1, 1))
        light.setAttenuation((1, 0.01, 0.001))
        self.lightNP = self.root.attachNewNode(light)
        self.lightNP.setZ(1)
        Common.framework.showBase.render.setLight(self.lightNP)

        self.colliderNP.node().setFromCollideMask(MASK_WALLS
                                                  | MASK_FROM_PLAYER)

        Common.framework.pusher.addCollider(self.colliderNP, self.root)
        Common.framework.traverser.addCollider(self.colliderNP,
                                               Common.framework.pusher)

        Common.framework.showBase.camera.reparentTo(self.actor)
        Common.framework.showBase.camera.setPos(0, 0, 0)
        Common.framework.showBase.camera.setHpr(0, 0, 0)

        lens = Common.framework.showBase.camLens

        lens.setNear(0.03)

        ratio = lens.getAspectRatio()

        lens.setFov(75 * ratio)

        self.lastMousePos = Vec2(0, 0)
        self.mouseSpeedHori = 50.0
        self.mouseSpeedVert = 30.0
        self.mouseSensitivity = 1.0

        self.targetingRay = CollisionSegment(0, 0, 0, 0, 100, 0)
        self.targetingRayNode = CollisionNode("lock ray")
        self.targetingRayNode.addSolid(self.targetingRay)
        self.targetingRayNode.setFromCollideMask(MASK_ENEMY_LOCK_SPHERE)
        self.targetingRayNode.setIntoCollideMask(0)
        self.targetingRayNP = self.actor.attachNewNode(self.targetingRayNode)
        self.targetingQueue = CollisionHandlerQueue()

        self.prospectiveLockTarget = None
        self.lockTargetTimer = 0
        self.lockDuration = 1

        Common.framework.traverser.addCollider(self.targetingRayNP,
                                               self.targetingQueue)

        #rayNodePath.show()

        self.uiRoot = aspect2d.attachNewNode(PandaNode("player UI"))

        cardMaker = CardMaker("UI maker")
        cardMaker.setFrame(-1, 1, -1, 1)

        self.centreSpot = self.uiRoot.attachNewNode(cardMaker.generate())
        self.centreSpot.setTexture(
            Common.framework.showBase.loader.loadTexture(
                "../Section2SpaceflightDocking/UI/spot.png"))
        self.centreSpot.setTransparency(True)
        self.centreSpot.setPos(0, 0, 0)
        self.centreSpot.setScale(0.01)
        self.centreSpot.setAlphaScale(0.5)

        self.directionIndicator = self.uiRoot.attachNewNode(
            cardMaker.generate())
        self.directionIndicator.setTexture(
            Common.framework.showBase.loader.loadTexture(
                "../Section2SpaceflightDocking/UI/directionIndicator.png"))
        self.directionIndicator.setTransparency(True)
        self.directionIndicator.setScale(0.05)
        self.directionIndicator.hide()

        self.lockMarkerRoot = self.uiRoot.attachNewNode(
            PandaNode("lock marker root"))
        for i in range(4):
            markerRotationNP = self.lockMarkerRoot.attachNewNode(
                PandaNode("lock marker rotation"))
            marker = markerRotationNP.attachNewNode(cardMaker.generate())
            marker.setTexture(
                Common.framework.showBase.loader.loadTexture(
                    "../Section2SpaceflightDocking/UI/lockMarker.png"))
            marker.setTransparency(True)
            markerRotationNP.setScale(0.04)
            markerRotationNP.setR(i * 90)
        self.lockMarkerRoot.hide()

        self.lockBar = Common.framework.showBase.loader.loadModel(
            "../Section2SpaceflightDocking/UI/uiLockBar")
        self.lockBar.reparentTo(self.uiRoot)
        self.lockBar.setScale(0.15)
        #self.lockBar.hide()

        cardMaker.setFrame(-1, 1, 0, 1)

        self.cockpit = Common.framework.showBase.loader.loadModel(
            "../Section2SpaceflightDocking/Models/{0}".format(
                shipSpec.cockpitModelFile))
        self.cockpit.reparentTo(self.actor)

        healthBarRoot = self.cockpit.find("**/healthBar")
        if healthBarRoot is None or healthBarRoot.isEmpty():
            healthBarRoot = self.uiRoot.attachNewNode(
                PandaNode("health bar root"))
            print("No health bar root found!")

        energyBarRoot = self.cockpit.find("**/energyBar")
        if energyBarRoot is None or energyBarRoot.isEmpty():
            energyBarRoot = self.uiRoot.attachNewNode(
                PandaNode("energy bar root"))
            print("No energy bar root found!")

        missileCounterRoot = self.cockpit.find("**/missileCounter")
        if missileCounterRoot is None or missileCounterRoot.isEmpty():
            missileCounterRoot = self.uiRoot.attachNewNode(
                PandaNode("missile counter root"))
            print("No missile counter root found!")

        radarRoot = self.cockpit.find("**/radar")
        if radarRoot is None or radarRoot.isEmpty():
            radarRoot = self.uiRoot.attachNewNode(PandaNode("radar root"))
            print("No radar root found!")

        speedometerRoot = self.cockpit.find("**/speedometer")
        if speedometerRoot is None or speedometerRoot.isEmpty():
            speedometerRoot = self.uiRoot.attachNewNode(
                PandaNode("speedometer root"))
            print("No speedometer root found!")

        self.radarDrawer = MeshDrawer()
        self.radarDrawer.setBudget(4096)

        self.radarDrawerNP = self.radarDrawer.getRoot()
        self.radarDrawerNP.reparentTo(radarRoot)
        self.radarDrawerNP.setTwoSided(True)
        self.radarDrawerNP.setLightOff()
        self.radarDrawerNP.setDepthWrite(False)
        self.radarDrawerNP.setTransparency(True)

        self.healthBar = healthBarRoot.attachNewNode(cardMaker.generate())
        self.healthBar.setSx(0.05)

        self.energyBar = energyBarRoot.attachNewNode(cardMaker.generate())
        self.energyBar.setSx(0.05)

        self.healthBarScalar = 0.00175
        self.energyBarScalar = 0.00175

        self.missileCounter = DirectLabel(text="",
                                          text_mayChange=True,
                                          scale=0.09,
                                          relief=None,
                                          parent=missileCounterRoot)

        self.maxRadarRange = 700
        self.radarSize = 0.3

        self.speedometer = DirectLabel(text="",
                                       text_mayChange=True,
                                       scale=0.09,
                                       relief=None,
                                       parent=speedometerRoot)

        self.updateHealthUI()
        self.updateEnergyUI()
        self.updateMissileUI()
        self.updateRadar()
        self.updateSpeedometer()

        self.updatingEffects = []

    def update(self, keys, dt):
        GameObject.update(self, dt)

        self.updateSpeedometer()

        self.walking = False

        quat = self.root.getQuat(Common.framework.showBase.render)
        forward = quat.getForward()
        right = quat.getRight()
        up = quat.getUp()

        if keys["up"]:
            self.walking = True
            self.velocity += forward * self.acceleration * dt
        if keys["down"]:
            self.walking = True
            self.velocity -= forward * self.acceleration * dt
        if keys["left"]:
            self.walking = True
            self.velocity -= right * self.acceleration * dt
        if keys["right"]:
            self.walking = True
            self.velocity += right * self.acceleration * dt
        if self.walking:
            self.inControl = True

        mouseWatcher = base.mouseWatcherNode
        if mouseWatcher.hasMouse():
            xSize = base.win.getXSize()
            ySize = base.win.getYSize()
            xPix = float(xSize % 2) / xSize
            yPix = float(ySize % 2) / ySize
            mousePos = Vec2(base.mouseWatcherNode.getMouse())
            mousePos.addX(-xPix)
            mousePos.addY(-yPix)
            if abs(mousePos.x) < xPix:
                mousePos.x = 0
            if abs(mousePos.y) < yPix:
                mousePos.y = 0

        else:
            mousePos = self.lastMousePos

        if mousePos.length() > 0.01:
            axis = right * (mousePos.y) + up * (-mousePos.x)
            axis.normalize()
            angle = mousePos.length() * self.turnRate * dt

            rotQuat = Quat()
            rotQuat.setFromAxisAngle(angle, axis)

            self.root.setQuat(quat * rotQuat)

        if not self.weaponSets[0][0].active:
            self.alterEnergy(
                math.sin(1.071 * self.energy / self.maxEnergy + 0.5) *
                self.energyRechargeRate * dt)

        self.updateEnergyUI()
        self.updateHealthUI()
        self.updateRadar()

        #self.root.setH(self.root.getH() - mousePos.x*self.mouseSpeedHori*self.mouseSensitivity)
        #self.actor.setP(self.actor.getP() + mousePos.y*self.mouseSpeedVert*self.mouseSensitivity)

        if keys["shoot"]:
            self.startFiringSet(0)
        else:
            self.ceaseFiringSet(0)

        if keys["shootSecondary"]:
            self.startFiringSet(self.missileSetIndex + 1)
        else:
            for i in range(self.numMissileSets):
                self.ceaseFiringSet(i + 1)

        [effect.update(self, dt) for effect in self.updatingEffects]
        [
            effect.cleanup() for effect in self.updatingEffects
            if not effect.active
        ]
        self.updatingEffects = [
            effect for effect in self.updatingEffects if effect.active
        ]

        if self.targetingQueue.getNumEntries() > 0:
            self.targetingQueue.sortEntries()
            entry = self.targetingQueue.getEntry(0)
            intoNP = entry.getIntoNodePath()
            if intoNP.hasPythonTag(TAG_OWNER):
                other = intoNP.getPythonTag(TAG_OWNER)
                if other is self.prospectiveLockTarget and other is not self.lockedTarget:
                    self.lockTargetTimer += dt
                    if self.lockTargetTimer >= self.lockDuration:
                        self.lockedTarget = other
                else:
                    self.lockTargetTimer = 0
                self.prospectiveLockTarget = other
            else:
                self.lockTargetTimer = 0
        else:
            self.lockTargetTimer = 0

        perc = self.lockTargetTimer / self.lockDuration
        self.lockBar.setTexOffset(TextureStage.getDefault(), 0, -perc * 1.1)

        if self.lockedTarget is not None:
            if self.lockedTarget.health <= 0:
                self.lockedTarget = None
            else:
                relPos = self.lockedTarget.root.getPos(self.root)
                planarVec = relPos.getXz()
                relDist = relPos.length()

                if relDist == 0:
                    angle = 0
                else:
                    angle = math.acos(relPos.y / relDist)

                if relDist > 200 or angle > 1.7453:
                    self.lockedTarget = None
                else:

                    if self.lockMarkerRoot.isHidden():
                        self.lockMarkerRoot.show()

                    camPt = Point2()
                    convertedPt = Common.framework.showBase.cam.getRelativePoint(
                        Common.framework.showBase.render,
                        self.lockedTarget.root.getPos(
                            Common.framework.showBase.render))
                    if Common.framework.showBase.camLens.project(
                            convertedPt, camPt):
                        self.lockMarkerRoot.setPos(
                            Common.framework.showBase.render2d, camPt.x, 0,
                            camPt.y)
                        if self.lockMarkerRoot.isHidden():
                            self.lockMarkerRoot.show()
                        for child in self.lockMarkerRoot.getChildren():
                            child.getChild(0).setZ(
                                (1.0 - min(1, relDist / 100)) * 5 + 0.2)
                    elif not self.lockMarkerRoot.isHidden():
                        self.lockMarkerRoot.hide()

                    if relPos.y < 0 or angle > 0.6:
                        planarVec.normalize()

                        self.directionIndicator.setPos(planarVec.x * 0.4, 0,
                                                       planarVec.y * 0.4)

                        angle = math.degrees(
                            math.atan2(planarVec.x, planarVec.y))
                        self.directionIndicator.setR(angle)

                        if self.directionIndicator.isHidden():
                            self.directionIndicator.show()
                    elif not self.directionIndicator.isHidden():
                        self.directionIndicator.hide()
        else:
            if not self.directionIndicator.isHidden():
                self.directionIndicator.hide()
            if not self.lockMarkerRoot.isHidden():
                self.lockMarkerRoot.hide()

    def weaponReset(self, weapon):
        ArmedObject.weaponFired(self, weapon)

        if isinstance(weapon, RocketWeapon):
            self.ceaseFiringSet(self.missileSetIndex + 1)
            self.missileSetIndex += 1
            if self.missileSetIndex >= self.numMissileSets:
                self.missileSetIndex = 0

    def attackPerformed(self, weapon):
        ArmedObject.attackPerformed(self, weapon)

    def postTraversalUpdate(self, dt):
        ArmedObject.update(self, dt)

    def alterHealth(self,
                    dHealth,
                    incomingImpulse,
                    knockback,
                    flinchValue,
                    overcharge=False):
        GameObject.alterHealth(self, dHealth, incomingImpulse, knockback,
                               flinchValue, overcharge)

        self.updateHealthUI()

        #self.hurtSound.play()

    def alterEnergy(self, dEnergy):
        self.energy += dEnergy
        if self.energy < 0:
            self.energy = 0
        elif self.energy > self.maxEnergy:
            self.energy = self.maxEnergy

    def alterMissileCount(self, dMissiles):
        self.numMissiles += dMissiles
        if self.numMissiles < 0:
            self.numMissiles = 0
        self.updateMissileUI()

    def updateHealthUI(self):
        perc = self.health / self.maxHealth
        newVal = max(0.01, self.health * self.healthBarScalar)
        self.healthBar.setSz(newVal)
        self.healthBar.setColorScale(1.0 - (perc - 0.5) / 0.5,
                                     min(1, perc / 0.5), 0, 1)
        #self.healthCounter.setText("{0:-.0f}".format(self.health))
        #self.healthCounter.setColorScale(1.0 - (perc - 0.5)/0.5, min(1, perc/0.5), 0, 1)

    def updateEnergyUI(self):
        perc = self.energy / self.maxEnergy
        newVal = max(0.01, self.energy * self.energyBarScalar)
        self.energyBar.setSz(newVal)
        self.energyBar.setColorScale(1.0 - (perc - 0.5) / 0.5,
                                     min(1, perc / 0.5), 0, 1)

    def updateMissileUI(self):
        self.missileCounter["text"] = "Missiles:\n{0}".format(self.numMissiles)
        self.missileCounter.setText()
        self.missileCounter.resetFrameSize()

    def updateSpeedometer(self):
        self.speedometer["text"] = "Speed:\n{0:0=2.0f}m/s".format(
            self.velocity.length() * 2)
        self.speedometer.setText()
        self.speedometer.resetFrameSize()

    def updateRadar(self):
        if Common.framework.currentLevel is not None:
            self.radarDrawer.begin(Common.framework.showBase.cam,
                                   Common.framework.showBase.render)

            uvs = Vec2(0, 0)

            spotSize = 0.015

            self.radarDrawer.tri(Vec3(-spotSize, 0,
                                      -spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(spotSize, 0,
                                      -spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(-spotSize, 0, spotSize),
                                 Vec4(0, 1, 0, 1), uvs)
            self.radarDrawer.tri(Vec3(-spotSize, 0,
                                      spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(spotSize, 0,
                                      -spotSize), Vec4(0, 1, 0, 1), uvs,
                                 Vec3(spotSize, 0, spotSize), Vec4(0, 1, 0, 1),
                                 uvs)

            selfForward = Vec3(0, 1, 0)

            for enemy in Common.framework.currentLevel.enemies:
                enemyPos = enemy.root.getPos(self.root)
                dist = enemyPos.length()
                if dist < self.maxRadarRange:
                    distPerc = dist / self.maxRadarRange
                    enemyPos.normalize()
                    anglePerc = selfForward.angleDeg(enemyPos) / 180
                    enemyPos.setY(0)
                    enemyPos.normalize()
                    enemyPos *= anglePerc * self.radarSize
                    colour = Vec4(1, 0, 0,
                                  math.sin(max(0, 1 - distPerc) * 1.571))

                    self.radarDrawer.tri(
                        Vec3(-spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(0, 0, spotSize) + enemyPos, colour, uvs)
                    self.radarDrawer.tri(
                        Vec3(spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(-spotSize, 0, 0) + enemyPos, colour, uvs,
                        Vec3(0, 0, -spotSize) + enemyPos, colour, uvs)

            self.radarDrawer.end()

    def addUpdatingEffect(self, effect):
        self.updatingEffects.append(effect)
        effect.start(self)

    def cleanup(self):
        if self.uiRoot is not None:
            self.uiRoot.removeNode()
            self.uiRoot = None
        self.healthBar = None

        if self.lightNP is not None:
            Common.framework.showBase.render.clearLight(self.lightNP)
            self.lightNP.removeNode()
            self.lightNP = None

        for effect in self.updatingEffects:
            effect.cleanup()
        self.updatingEffects = []

        ArmedObject.cleanup(self)
        GameObject.cleanup(self)
Пример #2
0
class DirectTooltip():
    def __init__(self):

        self.tooltipText = DirectLabel(
            text = "Tooltip",
            text_fg = (1,1,1,1),
            text_scale = 0.05,
            text_align = TextNode.ALeft,
            frameColor = (0, 0, 0, 0.75),
            borderWidth = (0.1, 0.1))
        self.tooltipText.setTransparency(True)

        self.textXShift = 0.05
        self.textYShift = -0.08

        self.mousePos = None

        # this will determine when the tooltip should be moved in the
        # respective direction, whereby
        # 1  : display edge
        # <1 : margin inside the window
        # >1 : margin outside the window
        self.xEdgeStartShift = 0.99
        self.yEdgeStartShift = 0.99

        self.tooltipText.hide()

    def show(self, text=None, args=None):
        if text is not None:
            self.tooltipText.setText(text)
            self.tooltipText.resetFrameSize()
        self.tooltipText.show()

        # add the tooltips update task so it will be updated every frame
        base.taskMgr.add(self.updateTooltipPos, "task_updateTooltipPos")

    def delete(self, args=None):
        self.tooltipText.removeNode()

        # remove the tooltips update task
        base.taskMgr.remove("task_updateTooltipPos")

    def updateTooltipPos(self, task):
        # calculate new aspec tratio
        wp = base.win.getProperties()
        aspX = 1.0
        aspY = 1.0
        wpXSize = wp.getXSize()
        wpYSize = wp.getYSize()
        if wpXSize > wpYSize:
            aspX = wpXSize / float(wpYSize)
        else:
            aspY = wpYSize / float(wpXSize)

        # variables to store the mouses current x and y position
        x = 0.0
        y = 0.0
        if base.mouseWatcherNode.hasMouse():
            self.tooltipText.show()
            # get the mouse position
            x = base.mouseWatcherNode.getMouseX()
            y = base.mouseWatcherNode.getMouseY()

            # Move the tooltip to the mouse

            # set the text to the current mouse position
            self.tooltipText.setPos(
                (x*aspX) + self.textXShift,
                0,
                (y*aspY)+self.textYShift)

            bounds = self.tooltipText.getBounds()
            # bounds = left, right, bottom, top

            # calculate the texts bounds respecting its current position
            xLeft = self.tooltipText.getX() + bounds[0]*self.tooltipText.getScale()[0]
            xRight = self.tooltipText.getX() + bounds[1]*self.tooltipText.getScale()[0]
            yUp = self.tooltipText.getZ() + bounds[3]*self.tooltipText.getScale()[1]
            yDown = self.tooltipText.getZ() + bounds[2]*self.tooltipText.getScale()[1]

            # these will be used to shift the text in the desired direction
            xShift = 0.0
            yShift = 0.0
            if xRight/aspX > self.xEdgeStartShift:
                # shift to the left
                xShift = self.xEdgeStartShift - xRight/aspX
            elif xLeft/aspX < -self.xEdgeStartShift:
                # shift to the right
                xShift = -(self.xEdgeStartShift + xLeft/aspX)
            if yUp/aspY > self.yEdgeStartShift:
                # shift down
                yShift = self.yEdgeStartShift - yUp/aspY
            elif yDown/aspY < -self.yEdgeStartShift:
                # shift up
                yShift = -(self.yEdgeStartShift + yDown/aspY)

            # some aspect ratio calculation
            xShift *= aspX
            yShift *= aspY

            # move the tooltip to the new position
            self.tooltipText.setX(self.tooltipText.getX() + xShift)
            self.tooltipText.setZ(self.tooltipText.getZ() + yShift)
        else:
            self.tooltipText.delete()


        # continue the task until it got manually stopped
        return task.cont
Пример #3
0
class Main(ShowBase, FSM):
    def __init__(self):
        ShowBase.__init__(self)
        FSM.__init__(self, "mainStateMachine")

        # some basic enhancements
        # window background color
        self.setBackgroundColor(0, 0, 0)
        # set antialias for the complete sceen to automatic
        self.render.setAntialias(AntialiasAttrib.MAuto)
        # shader generator
        render.setShaderAuto()
        # Enhance font readability
        DGG.getDefaultFont().setPixelsPerUnit(100)
        # hide the mouse cursor
        hide_cursor()

        #
        # CONFIGURATION LOADING
        #
        # load given variables or set defaults
        # check if audio should be muted
        mute = ConfigVariableBool("audio-mute", False).getValue()
        if mute:
            self.disableAllAudio()
        else:
            self.enableAllAudio()

        base.sfxManagerList[0].setVolume(
            ConfigVariableDouble("audio-volume-sfx", 1.0).getValue())
        base.difficulty = ConfigVariableInt("difficulty", 0).getValue()

        def setFullscreen():
            """Helper function to set the window fullscreen
            with width and height set to the screens size"""
            # get the displays width and height
            w = self.pipe.getDisplayWidth()
            h = self.pipe.getDisplayHeight()
            # set window properties
            # clear all properties not previously set
            base.win.clearRejectedProperties()
            # setup new window properties
            props = WindowProperties()
            # Fullscreen
            props.setFullscreen(True)
            # set the window size to the screen resolution
            props.setSize(w, h)
            # request the new properties
            base.win.requestProperties(props)

        # check if the config file hasn't been created
        if not os.path.exists(prcFile):
            setFullscreen()
        elif base.appRunner:
            # When the application is started as appRunner instance, it
            # doesn't respect our loadPrcFiles configurations specific
            # to the window as the window is already created, hence we
            # need to manually set them here.
            for dec in range(mainConfig.getNumDeclarations()):
                # check if we have the fullscreen variable
                if mainConfig.getVariableName(dec) == "fullscreen":
                    setFullscreen()
        # automatically safe configuration at application exit
        base.exitFunc = self.__writeConfig

        # due to the delayed window resizing and switch to fullscreen
        # we wait some time until everything is set so we can savely
        # proceed with other setups like the menus
        if base.appRunner:
            # this behaviour only happens if run from p3d files and
            # hence the appRunner is enabled
            taskMgr.doMethodLater(0.5,
                                  self.postInit,
                                  "post initialization",
                                  extraArgs=[])
        else:
            self.postInit()

    def postInit(self):
        # Some game related variables
        self.currentLevel = 1
        self.youWon = False

        # Set esc to force exit $ remove
        self.accept('escape', self.exitApp)

        # Menu Events
        self.accept("menu_StartGame", self.startGame)
        self.accept("menu_Options", self.request, ["Options"])
        self.accept("menu_QuitGame", self.exitApp)
        self.accept("menu_Back", self.request, ["Menu"])
        self.accept("n", self.playNextTrack)

        ## Load Menu
        self.mainMenu = MainMenu()
        self.optionsMenu = OptionsMenu()

        ## Load music list
        self.musicList = [
            [
                "Housewell - Housewell - Sea  Sun  Fun",
                loader.loadMusic(
                    "music/Housewell_-_Housewell_-_Sea__Sun__Fun.ogg")
            ],
            [
                "KontrastE - LISTEN TO NIGHT",
                loader.loadMusic("music/KontrastE_-_LISTEN_TO_NIGHT.ogg")
            ],
            [
                "LukHash - THE OTHER SIDE",
                loader.loadMusic("music/LukHash_-_THE_OTHER_SIDE.ogg")
            ],
            [
                "Axl & Arth - Breathe",
                loader.loadMusic("music/Axl__amp__Arth_-_Breathe.ogg")
            ],
            [
                "Lyonn - The Symphony",
                loader.loadMusic("music/Lyonn_-_The_Symphony.ogg")
            ],
        ]
        self.lblNowPlaying = DirectLabel(text="No track running!",
                                         text_align=TextNode.ARight,
                                         text_fg=(240 / 255.0, 255 / 255.0,
                                                  240 / 255.0, 0.75),
                                         pos=(base.a2dRight - 0.05, 0,
                                              base.a2dBottom + 0.1),
                                         scale=0.04,
                                         frameColor=(0, 0, 0, 0.5),
                                         sortOrder=10)
        self.lblNowPlaying.hide()

        # The games Intro
        def create16To9LogoCard(logoPath, tsName):
            cm = CardMaker("fade")
            scale = abs(base.a2dLeft) / 1.7776
            cm.setFrame(-1, 1, -1 * scale, 1 * scale)
            logo = NodePath(cm.generate())
            logo.setTransparency(TransparencyAttrib.MAlpha)
            logoTex = loader.loadTexture(logoPath)
            logoTs = TextureStage(tsName)
            logoTs.setMode(TextureStage.MReplace)
            logo.setTexture(logoTs, logoTex)
            logo.setBin("fixed", 5000)
            logo.reparentTo(render2d)
            logo.hide()
            return logo

        self.gfLogo = create16To9LogoCard("intro/GrimFangLogo.png", "gfLogoTS")
        self.pandaLogo = create16To9LogoCard("intro/Panda3DLogo.png",
                                             "pandaLogoTS")
        self.gameLogo = create16To9LogoCard("intro/GameLogo.png", "gameLogoTS")

        def createFadeIn(logo):
            return LerpColorScaleInterval(logo, 2,
                                          LVecBase4f(0.0, 0.0, 0.0, 1.0),
                                          LVecBase4f(0.0, 0.0, 0.0, 0.0))

        def createFadeOut(logo):
            return LerpColorScaleInterval(logo, 2,
                                          LVecBase4f(0.0, 0.0, 0.0, 0.0),
                                          LVecBase4f(0.0, 0.0, 0.0, 1.0))

        gfFadeInInterval = createFadeIn(self.gfLogo)
        gfFadeOutInterval = createFadeOut(self.gfLogo)
        p3dFadeInInterval = createFadeIn(self.pandaLogo)
        p3dFadeOutInterval = createFadeOut(self.pandaLogo)
        gameFadeInInterval = createFadeIn(self.gameLogo)
        gameFadeOutInterval = createFadeOut(self.gameLogo)
        self.introFadeInOutSequence = Sequence(Func(self.gfLogo.show),
                                               gfFadeInInterval,
                                               Wait(1.0),
                                               gfFadeOutInterval,
                                               Wait(0.5),
                                               Func(self.gfLogo.hide),
                                               Func(self.pandaLogo.show),
                                               p3dFadeInInterval,
                                               Wait(1.0),
                                               p3dFadeOutInterval,
                                               Wait(0.5),
                                               Func(self.pandaLogo.hide),
                                               Func(self.gameLogo.show),
                                               gameFadeInInterval,
                                               Wait(1.0),
                                               gameFadeOutInterval,
                                               Wait(0.5),
                                               Func(self.gameLogo.hide),
                                               Func(self.messenger.send,
                                                    "intro_done"),
                                               Func(self.startMusic),
                                               name="fadeInOut")
        # game intro end

        # story intro
        self.storyImage1 = create16To9LogoCard("intro1.png", "storyIntro1TS")
        story1FadeInInterval = createFadeIn(self.storyImage1)
        story1FadeOutInterval = createFadeOut(self.storyImage1)
        self.storySequence = Sequence(Func(self.storyImage1.show),
                                      story1FadeInInterval,
                                      Wait(8.0),
                                      story1FadeOutInterval,
                                      Func(self.request, "Game"),
                                      name="story")

        # Outros
        self.outroImage1 = create16To9LogoCard("outro1.png", "storyOutro1TS")
        outro1FadeInInterval = createFadeIn(self.outroImage1)
        outro1FadeOutInterval = createFadeOut(self.outroImage1)
        self.outroWonSequence = Sequence(Func(self.outroImage1.show),
                                         outro1FadeInInterval,
                                         Wait(8.0),
                                         outro1FadeOutInterval,
                                         Func(self.request, "Game"),
                                         name="OutroWon")

        self.outroImage2 = create16To9LogoCard("outro2.png", "storyOutro2TS")
        outro2FadeInInterval = createFadeIn(self.outroImage2)
        outro2FadeOutInterval = createFadeOut(self.outroImage2)
        self.outroLostSequence = Sequence(Func(self.outroImage2.show),
                                          outro2FadeInInterval,
                                          Wait(8.0),
                                          outro2FadeOutInterval,
                                          Func(self.request, "Menu"),
                                          name="OutroLost")

        self.outroImage3 = create16To9LogoCard("outro3.png", "storyOutro3TS")
        outro3FadeInInterval = createFadeIn(self.outroImage3)
        outro3FadeOutInterval = createFadeOut(self.outroImage3)
        self.outroWonGameSequence = Sequence(Func(self.outroImage3.show),
                                             outro3FadeInInterval,
                                             Wait(8.0),
                                             outro3FadeOutInterval,
                                             Func(self.request, "Menu"),
                                             name="OutroWonGame")

        #
        # Start with the menu after the intro has been played
        #
        self.introFadeInOutSequence.start()
        self.accept("intro_done", self.request, ["Menu"])

    def exitApp(self):
        if self.state == "Off":
            self.introFadeInOutSequence.finish()
        elif self.state == "Menu":
            self.userExit()
        elif self.state == "Intro":
            self.storySequence.finish()
        elif self.state == "Outro":
            if self.youWon:
                if self.currentLevel == 2:
                    self.outroWonSequence.finish()
                else:
                    self.outroWonGameSequence.finish()
            else:
                self.outroLostSequence.finish()
        else:
            self.request("Menu")

    def startMusic(self):
        self.lblNowPlaying.show()
        self.lastPlayed = None
        self.currentTrack = [None]
        self.playNextTrack()
        base.taskMgr.add(self.musicTask, "music playlist")

    def playNextTrack(self):
        if self.currentTrack[0] is not None:
            self.currentTrack[1].stop()
        while self.lastPlayed == self.currentTrack[0]:
            self.currentTrack = random.choice(self.musicList)
        self.lastPlayed = self.currentTrack[0]
        self.lblNowPlaying[
            "text"] = "Press 'N' for Next ~\nNOW PLAYING: {}".format(
                self.currentTrack[0])
        self.lblNowPlaying.resetFrameSize()
        self.currentTrack[1].play()

    def musicTask(self, task):
        if not base.AppHasAudioFocus: return task.cont
        track = self.currentTrack[1]
        if track.status() != track.PLAYING:
            self.playNextTrack()
        return task.cont

    def startGame(self):
        self.acceptWinLoose()
        self.currentLevel = 1
        self.request("Intro")

    def enterMenu(self):
        show_cursor()
        self.mainMenu.show()

    def exitMenu(self):
        self.mainMenu.hide()

    def enterOptions(self):
        self.optionsMenu.show()

    def exitOptions(self):
        self.optionsMenu.hide()

    def enterIntro(self):
        self.storySequence.start()

    def enterGame(self):
        self.youWon = False
        hide_cursor()
        ## Load/Start GameBase
        self.gamebase = GameBase()
        ## Load/Start Game
        self.game = Game()
        self.gamebase.start()
        self.game.setPhysicsWorld(self.gamebase.physics_world)
        if base.difficulty == 0:
            self.game.start(self.currentLevel, 25)
        elif base.difficulty == 1:
            self.game.start(self.currentLevel, 50)
        else:
            self.game.start(self.currentLevel, 100)
        self.acceptWinLoose()

        # Debug #
        #self.gamebase.enablePhysicsDebug()
        #print (render.ls())

    def exitGame(self):
        self.ignoreWinLoose()
        self.game.stop()
        self.gamebase.stop()
        del self.game
        del self.gamebase
        self.game = None
        self.gamebase = None

    def enterOutro(self):
        if self.youWon:
            if self.currentLevel == 1:
                self.outroWonSequence.start()
            else:
                self.outroWonGameSequence.start()
        else:
            self.outroLostSequence.start()

    def wonGame(self):
        self.ignoreWinLoose()
        self.youWon = True
        self.request("Outro")
        self.currentLevel += 1

    def lostGame(self):
        self.ignoreWinLoose()
        self.youWon = False
        self.request("Outro")

    def acceptWinLoose(self):
        self.accept("wonGame", self.wonGame)
        self.accept("lostGame", self.lostGame)

    def ignoreWinLoose(self):
        self.ignore("wonGame")
        self.ignore("lostGame")

    def __writeConfig(self):
        """Save current config in the prc file or if no prc file exists
        create one. The prc file is set in the prcFile variable"""
        page = None

        volume = str(round(base.musicManager.getVolume(), 2))
        volumeSfx = str(round(base.sfxManagerList[0].getVolume(), 2))
        mute = "#f" if base.AppHasAudioFocus else "#t"
        difficuty = str(base.difficulty)
        customConfigVariables = [
            "", "audio-mute", "audio-volume", "audio-volume-sfx", "difficulty"
        ]
        if os.path.exists(prcFile):
            # open the config file and change values according to current
            # application settings
            page = loadPrcFile(prcFile)
            removeDecls = []
            for dec in range(page.getNumDeclarations()):
                # Check if our variables are given.
                # NOTE: This check has to be done to not loose our base or other
                #       manual config changes by the user
                if page.getVariableName(dec) in customConfigVariables:
                    decl = page.modifyDeclaration(dec)
                    removeDecls.append(decl)
            for dec in removeDecls:
                page.deleteDeclaration(dec)
            # NOTE: audio-mute are custom variables and
            #       have to be loaded by hand at startup
            # audio
            page.makeDeclaration("audio-volume", volume)
            page.makeDeclaration("audio-volume-sfx", volumeSfx)
            page.makeDeclaration("audio-mute", mute)
            page.makeDeclaration("difficulty", difficuty)
        else:
            # Create a config file and set default values
            cpMgr = ConfigPageManager.getGlobalPtr()
            page = cpMgr.makeExplicitPage("App Pandaconfig")
            # set OpenGL to be the default
            page.makeDeclaration("load-display", "pandagl")
            # get the displays width and height
            w = self.pipe.getDisplayWidth()
            h = self.pipe.getDisplayHeight()
            # set the window size in the config file
            page.makeDeclaration("win-size", "%d %d" % (w, h))
            # set the default to fullscreen in the config file
            page.makeDeclaration("fullscreen", "1")
            # audio
            page.makeDeclaration("audio-volume", volume)
            page.makeDeclaration("audio-volume-sfx", volumeSfx)
            page.makeDeclaration("audio-mute", "#f")
            page.makeDeclaration("sync-video", "1")
            page.makeDeclaration("textures-auto-power-2", "1")
            page.makeDeclaration("framebuffer-multisample", "1")
            page.makeDeclaration("multisamples", "2")
            page.makeDeclaration("texture-anisotropic-degree", "0")
            page.makeDeclaration("difficulty", 0)
        # create a stream to the specified config file
        configfile = OFileStream(prcFile)
        # and now write it out
        page.write(configfile)
        # close the stream
        configfile.close()