Ejemplo n.º 1
0
class Rockmeter(ConfigGetMixin):

    _layerLimit = 99    #limit to how many layers can be loaded
    _groupLimit = 50    #limit to how many groups can be loaded

    def __init__(self, guitarScene, configFileName, coOp = False):

        self.scene            = guitarScene
        self.engine           = guitarScene.engine
        self.layers = {}          #collection of all the layers in the rockmeter
        self.layersForRender = {} #collection of layers that are rendered separate from any group
        self.layerGroups = {}     #collection of layer groups
        self.sharedLayerGroups = {}
        self.sharedLayers = {}    #these layers are for coOp use only
        self.sharedLayersForRender = {}
        self.sharedGroups = {}

        self.coOp = coOp
        self.config = LinedConfigParser()
        self.config.read(configFileName)

        self.themename = self.engine.data.themeLabel

        try:
            themepath = os.path.join(Version.dataPath(), "themes", self.themename)
            fp, pathname, description = imp.find_module("CustomRMLayers",[themepath])
            self.customRMLayers = imp.load_module("CustomRMLayers", fp, pathname, description)
        except ImportError:
            self.customRMLayers = None
            Log.notice("Custom Rockmeter layers are not available")

        # Build the layers
        for i in range(Rockmeter._layerLimit):
            types = [
                     "Image",
                     "Text",
                     "Circle",
                     "Custom"
                    ]

            for t in types:
                self.section = "layer%d:%s" % (i, t)
                if not self.config.has_section(self.section):
                    continue
                else:
                    if t == types[1]:
                        self.createFont(self.section, i)
                    elif t == types[2]:
                        self.createCircle(self.section, i)
                    elif t == types[3]:
                        self.createCustom(self.section, i)
                    else:
                        self.createImage(self.section, i)
                    break

        for i in range(Rockmeter._groupLimit):
            self.section = "Group%d" % i
            if not self.config.has_section(self.section):
                continue
            else:
                self.createGroup(self.section, i)

        print self.layerGroups
        print self.sharedLayerGroups
        print self.layersForRender
        self.reset()

    def reset(self):
        self.lastMissPos        = None
        self.lastPickPos        = None
        self.playedNotes        = []
        self.averageNotes       = [0.0]

    #adds a layer to the rockmeter's list
    def addLayer(self, layer, number, shared = False):
        if shared:
            self.sharedLayers[number] = layer
            self.sharedLayersForRender[number] = layer
        else:
            if layer:
                self.layers[number] = layer
                self.layersForRender[number] = layer

    def loadLayerFX(self, layer, section):
        section = section.split(":")[0]
        types = {"Slide": "Slide(layer, fxsection)",
                 "Rotate": "Rotate(layer, fxsection)",
                 "Replace": "Replace(layer, fxsection)",
                 "Fade": "Fade(layer, fxsection)",
                 "Animate": "Animate(layer, fxsection)",
                 "Scale": "Scale(layer, fxsection)"}

        #makes sure groups don't load effects they can use
        if isinstance(layer, Group):
            types.pop("Animate"); types.pop("Replace")

        for i in range(5):  #maximum of 5 effects per layer
            for t in types.keys():
                fxsection = "%s:fx%d:%s" % (section, i, t)
                if not self.config.has_section(fxsection):
                    continue
                else:
                    layer.effects.append(eval(types[t]))
                    break


    def createCustom(self, section, number):

        if self.customRMLayers:
            classname = self.get("classname")

            layer = eval("self.customRMLayers."+classname+"(self, section)")

            if isinstance(layer, Group):
                self.addGroup(layer, number, layer.shared)
            else:
                self.addLayer(layer, number, layer.shared)

    def createFont(self, section, number):

        font  = self.get("font", str, "font")
        layer = FontLayer(self, section, font)

        layer.text      = self.getexpr("text")
        layer.shared    = self.get("shared",   bool, False)
        layer.condition = self.getexpr("condition", "True")
        layer.inPixels  = self.get("inPixels", str, "").split("|")

        self.loadLayerFX(layer, section)

        self.addLayer(layer, number, layer.shared)

    def createImage(self, section, number):

        texture   = self.get("texture")
        drawing   = os.path.join("themes", self.themename, "rockmeter", texture)
        layer     = ImageLayer(self, section, drawing)

        layer.shared    = self.get("shared", bool, False)
        layer.condition = self.getexpr("condition", "True")
        layer.inPixels  = self.get("inPixels", str, "").split("|")

        layer.rect      = self.getexpr("rect", "(0,1,0,1)")

        self.loadLayerFX(layer, section)

        self.addLayer(layer, number, layer.shared)

    def createCircle(self, section, number):

        texture   = self.get("texture")
        drawing   = os.path.join("themes", self.themename, "rockmeter", texture)
        layer     = CircleLayer(self, section, drawing)

        layer.shared    = self.get("shared", bool, False)
        layer.condition = self.getexpr("condition", "True")
        layer.inPixels  = self.get("inPixels", str, "").split("|")

        layer.rect      = self.getexpr("rect", "(0,1,0,1)")

        self.loadLayerFX(layer, section)

        self.addLayer(layer, number, layer.shared)

    def createGroup(self, section, number):
        group = Group(self, section)

        group.shared = self.get("shared", bool, False)

        self.loadLayerFX(group, section)

        self.addGroup(group, number, group.shared)

    def addGroup(self, group, number, shared):
        #remove the layers in the group from the layers to be rendered
        # independent of groups
        if shared:
            for layer in group.layers.keys():
                if layer in self.layersForRender.keys():
                    self.layersForRender.pop(layer)
                self.sharedLayerGroups[number] = group
        else:
            for layer in group.layers.keys():
                if layer in self.layersForRender.keys():
                    self.layersForRender.pop(layer)
                self.layerGroups[number] = group

    #because time is not player specific it's best to update it only once per cycle
    def updateTime(self):
        global songLength, minutesSongLength, secondsSongLength
        global minutesCountdown, secondsCountdown, minutes, seconds
        global position, countdownPosition, progress

        scene = self.scene

        songLength        = scene.lastEvent
        position          = scene.getSongPosition()
        countdownPosition = songLength - position
        progress          = float(position)/float(songLength)
        if progress < 0:
            progress = 0
        elif progress > 1:
            progress = 1

        minutesCountdown, secondsCountdown   = (countdownPosition / 60000, (countdownPosition % 60000) / 1000)
        minutes, seconds                     = (position / 60000, (position % 60000) / 1000)
        minutesSongLength, secondsSongLength = (songLength / 60000, (songLength % 60000) / 1000)

    #this updates all the usual global variables that are handled by the rockmeter
    #these are all player specific
    def updateVars(self, p):
        global score, rock, coop_rock, streak, streakMax, power, stars, partialStars, multiplier, bassgroove, boost, player, part, playerNum
        scene = self.scene
        playerNum = p
        player = scene.instruments[playerNum]
        playerName = self.scene.playerList[p].name
        part = player.__class__.__name__

        #this is here for when I finally get coOp worked in
        if self.coOp:
            score = scene.coOpScoreCard.score
            stars = scene.coOpScoreCard.stars
            partialStars = scene.coOpScoreCard.starRatio
            coop_rock  = scene.rock[scene.coOpPlayerMeter] / scene.rockMax
        else:
            score = scene.scoring[playerNum].score
            stars = scene.scoring[playerNum].stars
            partialStars = scene.scoring[playerNum].starRatio
        rock  = scene.rock[playerNum] / scene.rockMax

        streak = scene.scoring[playerNum].streak
        power  = player.starPower/100.0

        #allows for bassgroove
        if player.isBassGuitar:
            streakMax = 50
        else:
            streakMax = 30

        if streak >= streakMax:
            multiplier = int(streakMax*.1) + 1
        else:
            multiplier = int(streak*.1) + 1

        boost = player.starPowerActive

        #doubles the multiplier number when starpower is activated
        if boost:
            multiplier *= 2

        if player.isBassGuitar and streak >= 40:
            bassgroove = True
        else:
            bassgroove = False

        #force bassgroove to false if it's not enabled
        if not scene.bassGrooveEnabled:
            bassgroove = False

    def triggerPick(self, pos, notes):
        if notes:
            self.lastPickPos      = pos
            self.playedNotes      = self.playedNotes[-3:] + [sum(notes) / float(len(notes))]
            self.averageNotes[-1] = sum(self.playedNotes) / float(len(self.playedNotes))

    def triggerMiss(self, pos):
        self.lastMissPos = pos

    def render(self, visibility):
        self.updateTime()

        with self.engine.view.orthogonalProjection(normalize = True):
            for i,player in enumerate(self.scene.playerList):
                p = player.number
                self.updateVars(p)
                if p is not None:
                    self.engine.view.setViewportHalf(self.scene.numberOfGuitars,p)
                else:
                    self.engine.view.setViewportHalf(1,0)
                for group in self.layerGroups.values():
                    group.render(visibility, p)
                for layer in self.layersForRender.values():
                    layer.updateLayer(p)
                    for effect in layer.effects:
                        effect.update()
                    layer.render(visibility, p)


            self.engine.view.setViewportHalf(1,0)
            for layer in self.sharedLayersForRender.values():
                layer.render(visibility, 0)
            for group in self.sharedLayerGroups.values():
                group.render(visibility, 0)
Ejemplo n.º 2
0
class Stage(object):
    def __init__(self, guitarScene, configFileName):
        self.scene            = guitarScene
        self.engine           = guitarScene.engine
        self.config           = LinedConfigParser()
        self.backgroundLayers = []
        self.foregroundLayers = []
        self.textures         = {}
        self.reset()


        self.wFull = None   #MFH - needed for new stage background handling
        self.hFull = None

        # evilynux - imported myfingershurt stuff from GuitarScene
        self.mode = self.engine.config.get("game", "stage_mode")
        self.songStage = self.engine.config.get("game", "song_stage")
        self.animatedFolder = self.engine.config.get("game", "animated_stage_folder")

        # evilynux - imported myfingershurt stuff from GuitarScene w/ minor modifs
        #MFH TODO - alter logic to accommodate separated animation and slideshow
        #           settings based on selected animated stage folder
        animationMode = self.engine.config.get("game", "stage_animate")
        slideShowMode = self.engine.config.get("game", "rotate_stages")

        if self.animatedFolder == _("None"):
            self.rotationMode = 0   #MFH: if no animated stage folders are available, disable rotation.
        elif self.animatedFolder == "Normal":
            self.rotationMode = slideShowMode
        else:
            self.rotationMode = animationMode

        self.imgArr = [] #QQstarS:random
        self.imgArrScaleFactors = []  #MFH - for precalculated scale factors
        self.rotateDelay = self.engine.config.get("game",  "stage_rotate_delay") #myfingershurt - user defined stage rotate delay
        self.animateDelay = self.engine.config.get("game",  "stage_animate_delay") #myfingershurt - user defined stage rotate delay
        self.animation = False

        self.indexCount = 0 #QQstarS:random time counter
        self.arrNum = 0 #QQstarS:random the array num
        self.arrDir = 1 #forwards

        self.config.read(configFileName)

        # evilynux - Improved stage error handling
        self.themename = self.engine.data.themeLabel
        self.path = os.path.join("themes",self.themename,"backgrounds")
        self.pathfull = self.engine.getPath(self.path)
        if not os.path.exists(self.pathfull): # evilynux
            log.warn("Stage folder does not exist: %s" % self.pathfull)
            self.mode = 1 # Fallback to song-specific stage

        self.loadLayers(configFileName)

    def loadLayers(self, configFileName):
        self.config.read(configFileName)
        path = os.path.join("themes", self.themename, "stage")

        # Build the layers
        for i in range(32):
            section = "layer%d" % i
            if self.config.has_section(section):
                def get(value, type = str, default = None):
                    if self.config.has_option(section, value):
                        return type(self.config.get(section, value))
                    return default

                xres    = get("xres", int, 256)
                yres    = get("yres", int, 256)
                texture = get("texture")

                try:
                    drawing = self.textures[texture]
                except KeyError:
                    drawing = self.engine.loadImgDrawing(self, None, os.path.join(path, texture), textureSize = (xres, yres))
                    self.textures[texture] = drawing

                layer = Layer(self, drawing)

                layer.position    = (get("xpos",   float, 0.0), get("ypos",   float, 0.0))
                layer.scale       = (get("xscale", float, 1.0), get("yscale", float, 1.0))
                layer.angle       = math.pi * get("angle", float, 0.0) / 180.0
                layer.srcBlending = getattr(gl, "GL_%s" % get("src_blending", str, "src_alpha").upper())
                layer.dstBlending = getattr(gl, "GL_%s" % get("dst_blending", str, "one_minus_src_alpha").upper())
                layer.color       = (get("color_r", float, 1.0), get("color_g", float, 1.0), get("color_b", float, 1.0), get("color_a", float, 1.0))

                # Load any effects
                fxClasses = {
                  "light":          LightEffect,
                  "rotate":         RotateEffect,
                  "wiggle":         WiggleEffect,
                  "scale":          ScaleEffect,
                }

                for j in range(32):
                    fxSection = "layer%d:fx%d" % (i, j)
                    if self.config.has_section(fxSection):
                        type = self.config.get(fxSection, "type")

                        if not type in fxClasses:
                            continue

                        options = self.config.options(fxSection)
                        options = dict([(opt, self.config.get(fxSection, opt)) for opt in options])

                        fx = fxClasses[type](layer, options)
                        layer.effects.append(fx)

                if get("foreground", int):
                    self.foregroundLayers.append(layer)
                else:
                    self.backgroundLayers.append(layer)

    def loadVideo(self, libraryName, songName):
        vidSource = None

        if self.songStage == 1:
            songBackgroundVideoPath = os.path.join(libraryName, songName, "background.ogv")
            if os.path.isfile(songBackgroundVideoPath):
                vidSource = songBackgroundVideoPath
                loop = False
            else:
                log.warn("Video not found: %s" % songBackgroundVideoPath)

        if vidSource is None:
            vidSource = os.path.join(self.pathfull, "default.ogv")
            loop = True

        if not os.path.isfile(vidSource):
            log.warn("Video not found: %s" % vidSource)
            log.warn("Falling back to default stage mode.")
            self.mode = 1 # Fallback
            return

        try: # Catches invalid video files or unsupported formats
            log.debug("Attempting to load video: %s" % vidSource)
            self.vidPlayer = VideoLayer(self.engine, vidSource,
                                        mute = True, loop = loop)
            self.engine.view.pushLayer(self.vidPlayer)
        except (IOError, VideoPlayerError):
            self.mode = 1
            log.error("Failed to load song video (falling back to default stage mode):")

    def restartVideo(self):
        if not self.mode == 3:
            return
        self.vidPlayer.restart()

    def load(self, libraryName, songName, practiceMode = False):
        if self.scene.coOpType:
            rm = os.path.join("themes", self.themename, "rockmeter_coop.ini")
        elif self.scene.battle:
            rm = os.path.join("themes", self.themename, "rockmeter_profaceoff.ini")
        elif self.scene.gamePlayers > 1:
            rm = os.path.join("themes", self.themename, "rockmeter_faceoff.ini")
        else:
            rm = os.path.join("themes", self.themename, "rockmeter.ini")

        if os.path.exists(os.path.join("..", "data", rm)):
            rockmeter = self.engine.resource.fileName(rm)
        else:
            rockmeter = self.engine.resource.fileName(os.path.join("themes", self.themename, "rockmeter.ini"))

        self.rockmeter = Rockmeter.Rockmeter(self.scene, rockmeter, self.scene.coOpType)

        # evilynux - Fixes a self.background not defined crash
        self.background = None
        #MFH - new background stage logic:
        if self.mode == 2:   #blank / no stage
            self.songStage = 0
            self.rotationMode = 0
        elif practiceMode:   #check for existing practice stage; always disable stage rotation here
            self.songStage = 0
            self.rotationMode = 0
            self.mode = 1
            #separated practice stages for the instruments by k.i.d
            if self.scene.instruments[0].isDrum:
                background = "practicedrum"
            elif self.scene.instruments[0].isBassGuitar:
                background = "practicebass"
            else:
                background = "practice"
            if not self.engine.loadImgDrawing(self, "background", os.path.join("themes",self.themename,"backgrounds",background)):
                #MFH - must first fall back on the old practice.png before forcing blank stage mode!
                if not self.engine.loadImgDrawing(self, "background", os.path.join("themes",self.themename,"backgrounds","practice")):
                    log.warn("No practice stage, falling back on a forced Blank stage mode") # evilynux
                    self.mode = 2    #if no practice stage, just fall back on a forced Blank stage mode

        elif self.songStage == 1:    #check for song-specific background
            test = True
            if not self.engine.loadImgDrawing(self, "background", os.path.join(libraryName, songName, "background")):
                log.notice("No song-specific stage found") # evilynux
                test = False
            if test:  #does a song-specific background exist?
                self.rotationMode = 0
                self.mode = 1
            else:
                self.songStage = 0

        #MFH - now, after the above logic, we can run the normal stage mode logic
        #      only worrying about checking for Blank, song-specific and
        #      practice stage modes
        if self.mode != 2 and self.mode != 3 and self.songStage == 0 and not practiceMode: #still need to load stage(s)
            #myfingershurt: assign this first
            if self.mode == 1:   #just use Default.png
                if not self.engine.loadImgDrawing(self, "background", os.path.join(self.path, "default")):
                    log.warn("No default stage; falling back on a forced Blank stage mode") # evilynux
                    self.mode = 2    #if no practice stage, just fall back on a forced Blank stage mode

            ##This checks how many Stage-background we have to select from
            if self.mode == 0 and self.rotationMode == 0:  #MFH: just display a random stage
                files = []
                fileIndex = 0
                allfiles = os.listdir(self.pathfull)
                for name in allfiles:
                    if os.path.splitext(name)[0].lower() != "practice" and os.path.splitext(name)[0].lower() != "practicedrum" and os.path.splitext(name)[0].lower() != "practicebass" and name != ".git":
                        log.debug("Valid background found, index (" + str(fileIndex) + "): " + name)
                        files.append(name)
                        fileIndex += 1
                    else:
                        log.debug("Practice background filtered: " + name)

                # evilynux - improved error handling, fallback to blank background if no background are found
                if fileIndex == 0:
                    log.warn("No valid stage found!")
                    self.mode = 2;
                else:
                    i = random.randint(0,len(files)-1)
                    filename = files[i]
            ##End check number of Stage-backgrounds
                    if not self.engine.loadImgDrawing(self, "background", os.path.join(self.path, filename)):
                        self.mode = 2;

            elif self.rotationMode > 0 and self.mode != 2:
                files = []
                fileIndex = 0

                if self.animatedFolder == "Random": #Select one of the subfolders under stages\ to animate randomly
                    numAniStageFolders = len(self.engine.stageFolders)
                    if numAniStageFolders > 0:
                        self.animatedFolder = random.choice(self.engine.stageFolders)
                    else:
                        self.animatedFolder = "Normal"

                elif self.animatedFolder == "None":
                    self.mode = 2

                if self.animatedFolder != "Normal" and self.mode != 2: #just use the base Stages folder for rotation
                    self.path = os.path.join("themes",self.themename,"backgrounds",self.animatedFolder)
                    self.pathfull = self.engine.getPath(self.path)
                    self.animation = True

                allfiles = os.listdir(self.pathfull)
                for name in allfiles:

                    if os.path.splitext(name)[1].lower() == ".png" or os.path.splitext(name)[1].lower() == ".jpg" or os.path.splitext(name)[1].lower() == ".jpeg":
                        if os.path.splitext(name)[0].lower() != "practice" and os.path.splitext(name)[0].lower() != "practicedrum" and os.path.splitext(name)[0].lower() != "practicebass":
                            log.debug("Valid background found, index (" + str(fileIndex) + "): " + name)
                            files.append(name)
                            fileIndex += 1
                        else:
                            log.debug("Practice background filtered: " + name)
                    files.sort()

            if self.rotationMode > 0 and self.mode != 2:   #alarian: blank stage option is not selected
            #myfingershurt: just populate the image array in order, they are pulled in whatever order requested:
                for j in range(len(files)):
                    self.engine.loadImgDrawing(self, "backgroundA", os.path.join(self.path, files[j]))
                    self.imgArr.append(getattr(self, "backgroundA", os.path.join(self.path, files[j])))

        if self.rotationMode > 0 and len(self.imgArr) == 0:
            self.rotationMode = 0

    #stage rotation
    def rotate(self):
        if self.animation:
            whichDelay = self.animateDelay
        else:
            whichDelay = self.rotateDelay
        self.indexCount = self.indexCount + 1
        if self.indexCount > whichDelay:   #myfingershurt - adding user setting for stage rotate delay
            self.indexCount = 0
            if self.rotationMode == 1: #QQstarS:random
                self.arrNum = random.randint(0,len(self.imgArr)-1)
            elif self.rotationMode == 2: #myfingershurt: in order display mode
                self.arrNum += 1
                if self.arrNum > (len(self.imgArr)-1):
                    self.arrNum = 0
            elif self.rotationMode == 3: #myfingershurt: in order, back and forth display mode
                if self.arrDir == 1:  #forwards
                    self.arrNum += 1
                    if self.arrNum > (len(self.imgArr)-1):
                        self.arrNum -= 2
                        self.arrDir = 0
                else:
                    self.arrNum -= 1
                    if self.arrNum < 0:
                        self.arrNum += 2
                        self.arrDir = 1

    def renderBackground(self):
        #myfingershurt: multiple rotation modes
        if self.mode != 2:
            if self.rotationMode == 0:
                drawImage(self.background, scale = (1.0,-1.0),
                                      coord = (self.wFull/2,self.hFull/2), stretched = FULL_SCREEN)

            #myfingershurt:
            else:
                #MFH - use precalculated scale factors instead
                drawImage(self.imgArr[self.arrNum], scale = (1.0,-1.0),
                                      coord = (self.wFull/2,self.hFull/2), stretched = FULL_SCREEN)

    def updateDelays(self):
        self.rotateDelay = self.engine.config.get("game",  "stage_rotate_delay") #myfingershurt - user defined stage rotate delay
        self.animateDelay = self.engine.config.get("game",  "stage_animate_delay") #myfingershurt - user defined stage rotate delay

    def reset(self):
        self.lastBeatPos        = None
        self.lastQuarterBeatPos = None
        self.lastMissPos        = None
        self.lastPickPos        = None
        self.beat               = 0
        self.quarterBeat        = 0
        self.pos                = 0.0
        self.playedNotes        = []
        self.averageNotes       = [0.0]
        self.beatPeriod         = 0.0

    def triggerPick(self, pos, notes):
        if notes:
            self.lastPickPos      = pos
            self.playedNotes      = self.playedNotes[-3:] + [sum(notes) / float(len(notes))]
            self.averageNotes[-1] = sum(self.playedNotes) / float(len(self.playedNotes))
            self.rockmeter.triggerPick(pos, notes)

    def triggerMiss(self, pos):
        self.lastMissPos = pos
        self.rockmeter.triggerMiss(pos)

    def triggerQuarterBeat(self, pos, quarterBeat):
        self.lastQuarterBeatPos = pos
        self.quarterBeat        = quarterBeat

    def triggerBeat(self, pos, beat):
        self.lastBeatPos  = pos
        self.beat         = beat
        self.averageNotes = self.averageNotes[-4:] + self.averageNotes[-1:]

    def run(self, pos, period):
        self.pos        = pos
        self.beatPeriod = period
        quarterBeat = int(4 * pos / period)

        if quarterBeat > self.quarterBeat:
            self.triggerQuarterBeat(pos, quarterBeat)

        beat = quarterBeat / 4

        if beat > self.beat:
            self.triggerBeat(pos, beat)

    def renderLayers(self, layers, visibility):
        if self.mode != 3:
            with self.engine.view.orthogonalProjection(normalize = True):
                for layer in layers:
                    layer.render(visibility)

    def render(self, visibility):
        if self.mode != 3:
            self.renderBackground()
        self.renderLayers(self.backgroundLayers, visibility)
        if shaders.enable("stage"):
            height = 0.0
            for i in shaders.var["color"].keys():
                shaders.modVar("color",shaders.var["color"][i],0.05,10.0)
                height += shaders.var["color"][i][3]/3.0
            height=height**2
            shaders.setVar("height",2*height)
            shaders.setVar("ambientGlow",height/1.5)

            shaders.setVar("glowStrength",60+height*80.0)
            gl.glBegin(gl.GL_TRIANGLE_STRIP)
            gl.glVertex3f(-8.0, 1.0,7.0)
            gl.glVertex3f(8.0, 1.0,7.0)
            gl.glVertex3f(-8.0, 4.0,7.0)
            gl.glVertex3f(8.0, 4.0,7.0)
            gl.glEnd()
            shaders.disable()

        self.scene.renderGuitar()
        self.renderLayers(self.foregroundLayers, visibility)
        self.rockmeter.render(visibility)
Ejemplo n.º 3
0
class Stage(object):
    def __init__(self, guitarScene, configFileName):
        self.scene            = guitarScene
        self.engine           = guitarScene.engine
        self.config           = LinedConfigParser()
        self.backgroundLayers = []
        self.foregroundLayers = []
        self.textures         = {}
        self.reset()


        self.wFull = None   #MFH - needed for new stage background handling
        self.hFull = None

        # evilynux - imported myfingershurt stuff from GuitarScene
        self.mode = self.engine.config.get("game", "stage_mode")
        self.songStage = self.engine.config.get("game", "song_stage")
        self.animatedFolder = self.engine.config.get("game", "animated_stage_folder")

        # evilynux - imported myfingershurt stuff from GuitarScene w/ minor modifs
        #MFH TODO - alter logic to accommodate separated animation and slideshow
        #           settings based on selected animated stage folder
        animationMode = self.engine.config.get("game", "stage_animate")
        slideShowMode = self.engine.config.get("game", "rotate_stages")

        if self.animatedFolder == _("None"):
            self.rotationMode = 0   #MFH: if no animated stage folders are available, disable rotation.
        elif self.animatedFolder == "Normal":
            self.rotationMode = slideShowMode
        else:
            self.rotationMode = animationMode

        self.imgArr = [] #QQstarS:random
        self.imgArrScaleFactors = []  #MFH - for precalculated scale factors
        self.rotateDelay = self.engine.config.get("game",  "stage_rotate_delay") #myfingershurt - user defined stage rotate delay
        self.animateDelay = self.engine.config.get("game",  "stage_animate_delay") #myfingershurt - user defined stage rotate delay
        self.animation = False

        self.indexCount = 0 #QQstarS:random time counter
        self.arrNum = 0 #QQstarS:random the array num
        self.arrDir = 1 #forwards

        self.config.read(configFileName)

        # evilynux - Improved stage error handling
        self.themename = self.engine.data.themeLabel
        self.path = os.path.join("themes",self.themename,"backgrounds")
        self.pathfull = self.engine.getPath(self.path)
        if not os.path.exists(self.pathfull): # evilynux
            log.warn("Stage folder does not exist: %s" % self.pathfull)
            self.mode = 1 # Fallback to song-specific stage

        self.loadLayers(configFileName)

    def loadLayers(self, configFileName):
        self.config.read(configFileName)
        path = os.path.join("themes", self.themename, "stage")

        # Build the layers
        for i in range(32):
            section = "layer%d" % i
            if self.config.has_section(section):
                def get(value, type = str, default = None):
                    if self.config.has_option(section, value):
                        return type(self.config.get(section, value))
                    return default

                xres    = get("xres", int, 256)
                yres    = get("yres", int, 256)
                texture = get("texture")

                try:
                    drawing = self.textures[texture]
                except KeyError:
                    drawing = self.engine.loadImgDrawing(self, None, os.path.join(path, texture), textureSize = (xres, yres))
                    self.textures[texture] = drawing

                layer = Layer(self, drawing)

                layer.position    = (get("xpos",   float, 0.0), get("ypos",   float, 0.0))
                layer.scale       = (get("xscale", float, 1.0), get("yscale", float, 1.0))
                layer.angle       = math.pi * get("angle", float, 0.0) / 180.0
                layer.srcBlending = getattr(gl, "GL_%s" % get("src_blending", str, "src_alpha").upper())
                layer.dstBlending = getattr(gl, "GL_%s" % get("dst_blending", str, "one_minus_src_alpha").upper())
                layer.color       = (get("color_r", float, 1.0), get("color_g", float, 1.0), get("color_b", float, 1.0), get("color_a", float, 1.0))

                # Load any effects
                fxClasses = {
                  "light":          LightEffect,
                  "rotate":         RotateEffect,
                  "wiggle":         WiggleEffect,
                  "scale":          ScaleEffect,
                }

                for j in range(32):
                    fxSection = "layer%d:fx%d" % (i, j)
                    if self.config.has_section(fxSection):
                        type = self.config.get(fxSection, "type")

                        if not type in fxClasses:
                            continue

                        options = self.config.options(fxSection)
                        options = dict([(opt, self.config.get(fxSection, opt)) for opt in options])

                        fx = fxClasses[type](layer, options)
                        layer.effects.append(fx)

                if get("foreground", int):
                    self.foregroundLayers.append(layer)
                else:
                    self.backgroundLayers.append(layer)

    def loadVideo(self, libraryName, songName):
        vidSource = None

        if self.songStage == 1:
            songBackgroundVideoPath = os.path.join(libraryName, songName, "background.ogv")
            if os.path.isfile(songBackgroundVideoPath):
                vidSource = songBackgroundVideoPath
                loop = False
            else:
                log.warn("Video not found: %s" % songBackgroundVideoPath)

        if vidSource is None:
            vidSource = os.path.join(self.pathfull, "default.ogv")
            loop = True

        if not os.path.isfile(vidSource):
            log.warn("Video not found: %s" % vidSource)
            log.warn("Falling back to default stage mode.")
            self.mode = 1 # Fallback
            return

        try: # Catches invalid video files or unsupported formats
            log.debug("Attempting to load video: %s" % vidSource)
            self.vidPlayer = VideoLayer(self.engine, vidSource,
                                        mute = True, loop = loop)
            self.engine.view.pushLayer(self.vidPlayer)
        except (IOError, VideoPlayerError):
            self.mode = 1
            log.error("Failed to load song video (falling back to default stage mode):")

    def restartVideo(self):
        if not self.mode == 3:
            return
        self.vidPlayer.restart()

    def load(self, libraryName, songName, practiceMode = False):
        if self.scene.coOpType:
            rm = os.path.join("themes", self.themename, "rockmeter_coop.ini")
        elif self.scene.battle:
            rm = os.path.join("themes", self.themename, "rockmeter_profaceoff.ini")
        elif self.scene.gamePlayers > 1:
            rm = os.path.join("themes", self.themename, "rockmeter_faceoff.ini")
        else:
            rm = os.path.join("themes", self.themename, "rockmeter.ini")

        if os.path.exists(os.path.join("..", "data", rm)):
            rockmeter = self.engine.resource.fileName(rm)
        else:
            rockmeter = self.engine.resource.fileName(os.path.join("themes", self.themename, "rockmeter.ini"))

        self.rockmeter = Rockmeter.Rockmeter(self.scene, rockmeter, self.scene.coOpType)

        # evilynux - Fixes a self.background not defined crash
        self.background = None
        #MFH - new background stage logic:
        if self.mode == 2:   #blank / no stage
            self.songStage = 0
            self.rotationMode = 0
        elif practiceMode:   #check for existing practice stage; always disable stage rotation here
            self.songStage = 0
            self.rotationMode = 0
            self.mode = 1
            #separated practice stages for the instruments by k.i.d
            if self.scene.instruments[0].isDrum:
                background = "practicedrum"
            elif self.scene.instruments[0].isBassGuitar:
                background = "practicebass"
            else:
                background = "practice"
            if not self.engine.loadImgDrawing(self, "background", os.path.join("themes",self.themename,"backgrounds",background)):
                #MFH - must first fall back on the old practice.png before forcing blank stage mode!
                if not self.engine.loadImgDrawing(self, "background", os.path.join("themes",self.themename,"backgrounds","practice")):
                    log.warn("No practice stage, falling back on a forced Blank stage mode") # evilynux
                    self.mode = 2    #if no practice stage, just fall back on a forced Blank stage mode

        elif self.songStage == 1:    #check for song-specific background
            test = True
            if not self.engine.loadImgDrawing(self, "background", os.path.join(libraryName, songName, "background")):
                log.notice("No song-specific stage found") # evilynux
                test = False
            if test:  #does a song-specific background exist?
                self.rotationMode = 0
                self.mode = 1
            else:
                self.songStage = 0

        #MFH - now, after the above logic, we can run the normal stage mode logic
        #      only worrying about checking for Blank, song-specific and
        #      practice stage modes
        if self.mode != 2 and self.mode != 3 and self.songStage == 0 and not practiceMode: #still need to load stage(s)
            #myfingershurt: assign this first
            if self.mode == 1:   #just use Default.png
                if not self.engine.loadImgDrawing(self, "background", os.path.join(self.path, "default")):
                    log.warn("No default stage; falling back on a forced Blank stage mode") # evilynux
                    self.mode = 2    #if no practice stage, just fall back on a forced Blank stage mode

            ##This checks how many Stage-background we have to select from
            if self.mode == 0 and self.rotationMode == 0:  #MFH: just display a random stage
                files = []
                fileIndex = 0
                allfiles = os.listdir(self.pathfull)
                for name in allfiles:
                    if os.path.splitext(name)[0].lower() != "practice" and os.path.splitext(name)[0].lower() != "practicedrum" and os.path.splitext(name)[0].lower() != "practicebass" and name != ".svn":
                        log.debug("Valid background found, index (" + str(fileIndex) + "): " + name)
                        files.append(name)
                        fileIndex += 1
                    else:
                        log.debug("Practice background filtered: " + name)

                # evilynux - improved error handling, fallback to blank background if no background are found
                if fileIndex == 0:
                    log.warn("No valid stage found!")
                    self.mode = 2;
                else:
                    i = random.randint(0,len(files)-1)
                    filename = files[i]
            ##End check number of Stage-backgrounds
                    if not self.engine.loadImgDrawing(self, "background", os.path.join(self.path, filename)):
                        self.mode = 2;

            elif self.rotationMode > 0 and self.mode != 2:
                files = []
                fileIndex = 0

                if self.animatedFolder == "Random": #Select one of the subfolders under stages\ to animate randomly
                    numAniStageFolders = len(self.engine.stageFolders)
                    if numAniStageFolders > 0:
                        self.animatedFolder = random.choice(self.engine.stageFolders)
                    else:
                        self.animatedFolder = "Normal"

                elif self.animatedFolder == "None":
                    self.mode = 2

                if self.animatedFolder != "Normal" and self.mode != 2: #just use the base Stages folder for rotation
                    self.path = os.path.join("themes",self.themename,"backgrounds",self.animatedFolder)
                    self.pathfull = self.engine.getPath(self.path)
                    self.animation = True

                allfiles = os.listdir(self.pathfull)
                for name in allfiles:

                    if os.path.splitext(name)[1].lower() == ".png" or os.path.splitext(name)[1].lower() == ".jpg" or os.path.splitext(name)[1].lower() == ".jpeg":
                        if os.path.splitext(name)[0].lower() != "practice" and os.path.splitext(name)[0].lower() != "practicedrum" and os.path.splitext(name)[0].lower() != "practicebass":
                            log.debug("Valid background found, index (" + str(fileIndex) + "): " + name)
                            files.append(name)
                            fileIndex += 1
                        else:
                            log.debug("Practice background filtered: " + name)
                    files.sort()

            if self.rotationMode > 0 and self.mode != 2:   #alarian: blank stage option is not selected
            #myfingershurt: just populate the image array in order, they are pulled in whatever order requested:
                for j in range(len(files)):
                    self.engine.loadImgDrawing(self, "backgroundA", os.path.join(self.path, files[j]))
                    self.imgArr.append(getattr(self, "backgroundA", os.path.join(self.path, files[j])))

        if self.rotationMode > 0 and len(self.imgArr) == 0:
            self.rotationMode = 0

    #stage rotation
    def rotate(self):
        if self.animation:
            whichDelay = self.animateDelay
        else:
            whichDelay = self.rotateDelay
        self.indexCount = self.indexCount + 1
        if self.indexCount > whichDelay:   #myfingershurt - adding user setting for stage rotate delay
            self.indexCount = 0
            if self.rotationMode == 1: #QQstarS:random
                self.arrNum = random.randint(0,len(self.imgArr)-1)
            elif self.rotationMode == 2: #myfingershurt: in order display mode
                self.arrNum += 1
                if self.arrNum > (len(self.imgArr)-1):
                    self.arrNum = 0
            elif self.rotationMode == 3: #myfingershurt: in order, back and forth display mode
                if self.arrDir == 1:  #forwards
                    self.arrNum += 1
                    if self.arrNum > (len(self.imgArr)-1):
                        self.arrNum -= 2
                        self.arrDir = 0
                else:
                    self.arrNum -= 1
                    if self.arrNum < 0:
                        self.arrNum += 2
                        self.arrDir = 1

    def renderBackground(self):
        #myfingershurt: multiple rotation modes
        if self.mode != 2:
            if self.rotationMode == 0:
                drawImage(self.background, scale = (1.0,-1.0),
                                      coord = (self.wFull/2,self.hFull/2), stretched = FULL_SCREEN)

            #myfingershurt:
            else:
                #MFH - use precalculated scale factors instead
                drawImage(self.imgArr[self.arrNum], scale = (1.0,-1.0),
                                      coord = (self.wFull/2,self.hFull/2), stretched = FULL_SCREEN)

    def updateDelays(self):
        self.rotateDelay = self.engine.config.get("game",  "stage_rotate_delay") #myfingershurt - user defined stage rotate delay
        self.animateDelay = self.engine.config.get("game",  "stage_animate_delay") #myfingershurt - user defined stage rotate delay

    def reset(self):
        self.lastBeatPos        = None
        self.lastQuarterBeatPos = None
        self.lastMissPos        = None
        self.lastPickPos        = None
        self.beat               = 0
        self.quarterBeat        = 0
        self.pos                = 0.0
        self.playedNotes        = []
        self.averageNotes       = [0.0]
        self.beatPeriod         = 0.0

    def triggerPick(self, pos, notes):
        if notes:
            self.lastPickPos      = pos
            self.playedNotes      = self.playedNotes[-3:] + [sum(notes) / float(len(notes))]
            self.averageNotes[-1] = sum(self.playedNotes) / float(len(self.playedNotes))
            self.rockmeter.triggerPick(pos, notes)

    def triggerMiss(self, pos):
        self.lastMissPos = pos
        self.rockmeter.triggerMiss(pos)

    def triggerQuarterBeat(self, pos, quarterBeat):
        self.lastQuarterBeatPos = pos
        self.quarterBeat        = quarterBeat

    def triggerBeat(self, pos, beat):
        self.lastBeatPos  = pos
        self.beat         = beat
        self.averageNotes = self.averageNotes[-4:] + self.averageNotes[-1:]

    def run(self, pos, period):
        self.pos        = pos
        self.beatPeriod = period
        quarterBeat = int(4 * pos / period)

        if quarterBeat > self.quarterBeat:
            self.triggerQuarterBeat(pos, quarterBeat)

        beat = quarterBeat / 4

        if beat > self.beat:
            self.triggerBeat(pos, beat)

    def renderLayers(self, layers, visibility):
        if self.mode != 3:
            with self.engine.view.orthogonalProjection(normalize = True):
                for layer in layers:
                    layer.render(visibility)

    def render(self, visibility):
        if self.mode != 3:
            self.renderBackground()
        self.renderLayers(self.backgroundLayers, visibility)
        if shaders.enable("stage"):
            height = 0.0
            for i in shaders.var["color"].keys():
                shaders.modVar("color",shaders.var["color"][i],0.05,10.0)
                height += shaders.var["color"][i][3]/3.0
            height=height**2
            shaders.setVar("height",2*height)
            shaders.setVar("ambientGlow",height/1.5)

            shaders.setVar("glowStrength",60+height*80.0)
            gl.glBegin(gl.GL_TRIANGLE_STRIP)
            gl.glVertex3f(-8.0, 1.0,7.0)
            gl.glVertex3f(8.0, 1.0,7.0)
            gl.glVertex3f(-8.0, 4.0,7.0)
            gl.glVertex3f(8.0, 4.0,7.0)
            gl.glEnd()
            shaders.disable()

        self.scene.renderGuitar()
        self.renderLayers(self.foregroundLayers, visibility)
        self.rockmeter.render(visibility)