예제 #1
0
 def startWorld(self,
                players,
                maxplayers=None,
                gameMode=0,
                multiMode=0,
                allowGuitar=True,
                allowDrum=True,
                allowMic=False,
                tutorial=False):
     self.world = World(self, players, maxplayers, gameMode, multiMode,
                        allowGuitar, allowDrum, allowMic, tutorial)
예제 #2
0
 def startWorld(self, players, maxplayers = None, gameMode = 0, multiMode = 0, allowGuitar = True, allowDrum = True, allowMic = False, tutorial = False):
     self.world = World(self, players, maxplayers, gameMode, multiMode, allowGuitar, allowDrum, allowMic, tutorial)
예제 #3
0
class GameEngine(object):
    """The main game engine."""
    def __init__(self, config=None):

        log.debug("GameEngine class init (GameEngine.py)...")
        self.mainMenu = None  #placeholder for main menu object - to prevent reinstantiation

        self.currentScene = None

        self.versionString = version  #stump: other version stuff moved to allow full version string to be retrieved without instantiating GameEngine
        self.uploadVersion = "%s-4.0" % Version.PROGRAM_NAME  #akedrou - the version passed to the upload site.

        self.dataPath = Version.dataPath()
        log.debug(self.versionString + " starting up...")
        log.debug("Python version: " + sys.version.split(' ')[0])
        log.debug("Pygame version: " + str(pygame.version.ver))
        log.debug("PyOpenGL version: " + OpenGL.__version__)
        log.debug("Numpy version: " + np.__version__)
        log.debug("PIL version: " + Image.VERSION)
        log.debug("sys.argv: " + repr(sys.argv))
        log.debug("os.name: " + os.name)
        log.debug("sys.platform: " + sys.platform)
        if os.name == 'nt':
            import win32api
            log.debug("win32api.GetVersionEx(1): " +
                      repr(win32api.GetVersionEx(1)))
        elif os.name == 'posix':
            log.debug("os.uname(): " + repr(os.uname()))
        """
        Constructor.
        @param config:  L{Config} instance for settings
        """

        self.tutorialFolder = "tutorials"

        if config is None:
            config = Config.load()

        self.config = config

        self.fps = self.config.get("video", "fps")

        self.running = True
        self.clock = FpsTimer()
        self.tickDelta = 0
        self.task = TaskEngine(self)

        # Compatiblity task management
        self.addTask = self.task.addTask
        self.removeTask = self.task.removeTask
        self.pauseTask = self.task.pauseTask
        self.resumeTask = self.task.resumeTask

        self.title = self.versionString
        self.restartRequested = False

        # Load window icon
        icon = os.path.join(Version.dataPath(), "fofix_icon.png")

        self.video = Video(self.title, icon)
        if self.config.get("video", "disable_screensaver"):
            self.video.disableScreensaver()

        self.audio = Audio()
        self.fpsEstimate = 0
        self.priority = self.config.get("engine", "highpriority")
        self.show_fps = self.config.get("video", "show_fps")
        self.advSettings = self.config.get("game", "adv_settings")
        self.restartRequired = False
        self.quicksetRestart = False
        self.quicksetPerf = self.config.get("quickset", "performance")
        self.scrollRate = self.config.get("game", "scroll_rate")
        self.scrollDelay = self.config.get("game", "scroll_delay")

        log.debug("Initializing audio.")
        frequency = self.config.get("audio", "frequency")
        bits = self.config.get("audio", "bits")
        stereo = self.config.get("audio", "stereo")
        bufferSize = self.config.get("audio", "buffersize")
        self.audio.open(frequency=frequency,
                        bits=bits,
                        stereo=stereo,
                        bufferSize=bufferSize)

        self.gameStarted = False
        self.world = None

        self.audioSpeedFactor = 1.0

        log.debug("Initializing video.")
        #myfingershurt: ensuring windowed mode starts up in center of the screen instead of cascading positions:
        os.environ['SDL_VIDEO_WINDOW_POS'] = 'center'

        width, height = [
            int(s) for s in self.config.get("video", "resolution").split("x")
        ]
        fullscreen = self.config.get("video", "fullscreen")
        multisamples = self.config.get("video", "multisamples")
        self.video.setMode((width, height),
                           fullscreen=fullscreen,
                           multisamples=multisamples)
        log.debug("OpenGL version: " + glGetString(GL_VERSION))
        log.debug("OpenGL vendor: " + glGetString(GL_VENDOR))
        log.debug("OpenGL renderer: " + glGetString(GL_RENDERER))
        log.debug("OpenGL extensions: " +
                  ' '.join(sorted(glGetString(GL_EXTENSIONS).split())))

        if self.video.default:
            self.config.set("video", "fullscreen", False)
            self.config.set("video", "resolution", "800x600")

        if self.config.get("video", "shader_use"):
            shaders.set(os.path.join(Version.dataPath(), "shaders"))

        # Enable the high priority timer if configured
        if self.priority:
            log.debug("Enabling high priority timer.")
            self.fps = 0  # High priority

        # evilynux - This was generating an error on the first pass (at least under
        #            GNU/Linux) as the Viewport was not set yet.
        try:
            viewport = glGetIntegerv(GL_VIEWPORT)
        except Exception:
            viewport = [0, 0, width, height]
        h = viewport[3] - viewport[1]
        w = viewport[2] - viewport[0]
        geometry = (0, 0, w, h)
        self.svg = SvgContext(geometry)
        glViewport(int(viewport[0]), int(viewport[1]), int(viewport[2]),
                   int(viewport[3]))

        self.startupMessages = self.video.error
        self.input = Input()
        self.view = View(self, geometry)
        self.resizeScreen(w, h)

        self.resource = Resource(Version.dataPath())
        self.mainloop = self.loading
        self.menuMusic = False

        self.setlistMsg = None

        # Init theme system
        themename = self.config.get("coffee", "themename")
        themepath = os.path.join(Version.dataPath(), "themes", themename)
        self._initTheme(themename, themepath)
        self._initStages()

        # Load game modifications
        Mod.init(self)

        self.task.addTask(self.input, synced=False)
        self.task.addTask(self.view, synced=False)
        self.task.addTask(self.resource, synced=False)

        self.data = Data(self.resource, self.svg)

        self.input.addKeyListener(FullScreenSwitcher(self), priority=True)
        self.input.addSystemEventListener(SystemEventHandler(self))

        self.debugLayer = None
        self.startupLayer = None
        self.loadingScreenShown = False
        self.graphicMenuShown = False

        log.debug("Ready.")

    # evilynux - This stops the crowd cheers if they're still playing (issue 317).
    def quit(self):
        # evilynux - self.audio.close() crashes when we attempt to restart
        if not self.restartRequested:
            self.audio.close()
        Player.savePlayers()
        for taskData in list(self.task.tasks):
            self.task.removeTask(taskData['task'])
        self.running = False

    def _initTheme(self, themename, themepath):
        """
        Select the source of graphics for the game.
        Note that currently this can only be called GameEngine on startup.

        :param themename: what to call this theme
        :type themename: str
        :param themepath: absolute path to theme folder
        :type themepath: str
        """

        log.info('Setting theme %s from "%s"' % (themename, themepath))

        self.theme = None
        try:
            # Look for "CustomTheme.py" inside theme dir
            fp, pathname, description = imp.find_module(
                "CustomTheme", [themepath])
            try:
                # Found it! Load it.
                theme = imp.load_module("CustomTheme", fp, pathname,
                                        description)
                self.theme = theme.CustomTheme(themepath, themename)
                log.info('Theme activated using custom class "%s"' % pathname)
            except ImportError as e:
                # Unable to load module; log it, but continue with default Theme.
                log.error('Failed to load CustomTheme.py from "%s"' % pathname)
            finally:
                fp.close()
        except ImportError:
            # CustomTheme.py file is optional, but notify developer anyway.
            log.info("No CustomTheme.py found in theme")
            pass

        if self.theme is None:
            self.theme = Theme(themepath, themename)
            log.info("Theme activated using built-in Theme class")
        self.task.addTask(self.theme)

    def _initStages(self):
        """
        Setup animated stages from already-setup theme.
        Only call this after _initTheme().

        # <themename>\Stages still contains the backgrounds for when stage rotation is off, and practice.png
        # subfolders under Stages\ will each be treated as a separate animated stage set
        """

        self.stageFolders = []
        currentTheme = self.theme.name
        themepath = self.theme.path

        stagespath = os.path.join(themepath, "backgrounds")
        if os.path.exists(stagespath):
            self.stageFolders = []
            allFolders = os.listdir(stagespath)
            # this also includes all the stage files - so check to see if there
            # is at least one .png file inside each folder to be sure it's an
            # animated stage folder
            for name in allFolders:
                aniStageFolderListing = []
                thisIsAnAnimatedStageFolder = False
                try:
                    aniStageFolderListing = os.listdir(
                        os.path.join(stagespath, name))
                except Exception:
                    thisIsAnAnimatedStageFolder = False
                for aniFile in aniStageFolderListing:
                    if os.path.splitext(aniFile)[1] in [
                            ".png", ".jpg", ".jpeg"
                    ]:
                        # we've found at least one .png file here, chances are this is a valid animated stage folder
                        thisIsAnAnimatedStageFolder = True
                if thisIsAnAnimatedStageFolder:
                    self.stageFolders.append(name)

            i = len(self.stageFolders)
            if i > 0:
                # only set default to first animated subfolder if one exists - otherwise use Normal!
                defaultAniStage = str(self.stageFolders[0])
            else:
                defaultAniStage = "Normal"
            log.debug("Default animated stage for " + currentTheme +
                      " theme = " + defaultAniStage)
            aniStageOptions = dict([(str(self.stageFolders[n]),
                                     self.stageFolders[n])
                                    for n in range(0, i)])
            aniStageOptions.update({"Normal": _("Slideshow")})
            if i > 1:
                # only add Random setting if more than one animated stage exists
                aniStageOptions.update({"Random": _("Random")})
            Config.define("game",
                          "animated_stage_folder",
                          str,
                          defaultAniStage,
                          text=_("Animated Stage"),
                          options=aniStageOptions)

            #MFH: here, need to track and check a new ini entry for last theme - so when theme changes we can re-default animated stage to first found
            lastTheme = self.config.get("game", "last_theme")
            if lastTheme == "" or lastTheme != currentTheme:
                #MFH - no last theme, and theme just changed:
                # force defaultAniStage
                self.config.set("game", "animated_stage_folder",
                                defaultAniStage)
            self.config.set("game", "last_theme", currentTheme)

            selectedAnimatedStage = self.config.get("game",
                                                    "animated_stage_folder")
            if selectedAnimatedStage != "Normal" and selectedAnimatedStage != "Random":
                if not os.path.exists(
                        os.path.join(stagespath, selectedAnimatedStage)):
                    log.warn("Selected animated stage folder " +
                             selectedAnimatedStage +
                             " does not exist, forcing Normal.")
                    self.config.set(
                        "game", "animated_stage_folder", "Normal"
                    )  #MFH: force "Standard" currently selected animated stage folder is invalid
        else:
            Config.define("game",
                          "animated_stage_folder",
                          str,
                          "None",
                          text=_("Animated Stage"),
                          options=["None", _("None")])
            log.warn(
                "No stages\ folder found, forcing None setting for Animated Stage."
            )
            self.config.set(
                "game", "animated_stage_folder",
                "None")  #MFH: force "None" when Stages folder can't be found

    def setStartupLayer(self, startupLayer):
        """
        Set the L{Layer} that will be shown when the all
        the resources have been loaded. See L{Data}

        @param startupLayer:    Startup L{Layer}
        """
        self.startupLayer = startupLayer

    def isDebugModeEnabled(self):
        return bool(self.debugLayer)

    def setDebugModeEnabled(self, enabled):
        """
        Show or hide the debug layer.

        @type enabled: bool
        """
        if enabled:
            self.debugLayer = DebugLayer(self)
        else:
            self.debugLayer = None

    def toggleFullscreen(self):
        """
        Toggle between fullscreen and windowed mode.

        @return: True on success
        """
        if not self.video.toggleFullscreen():
            # on windows, the fullscreen toggle kills our textures, se we must restart the whole game
            self.input.broadcastSystemEvent("restartRequested")
            self.config.set("video", "fullscreen", not self.video.fullscreen)
            return True
        self.config.set("video", "fullscreen", self.video.fullscreen)
        return True

    def restart(self):
        """Restart the game."""
        if not self.restartRequested:
            self.restartRequested = True
            self.input.broadcastSystemEvent("restartRequested")
        else:
            self.quit()

    def resizeScreen(self, width, height):
        """
        Resize the game screen.

        @param width:   New width in pixels
        @param height:  New height in pixels
        """
        self.view.setGeometry((0, 0, width, height))
        self.svg.setGeometry((0, 0, width, height))

    def startWorld(self,
                   players,
                   maxplayers=None,
                   gameMode=0,
                   multiMode=0,
                   allowGuitar=True,
                   allowDrum=True,
                   allowMic=False,
                   tutorial=False):
        self.world = World(self, players, maxplayers, gameMode, multiMode,
                           allowGuitar, allowDrum, allowMic, tutorial)

    def finishGame(self):
        if not self.world:
            log.info("GameEngine.finishGame called before World created.")
            return
        self.world.finishGame()
        self.world = None
        self.gameStarted = False
        self.view.pushLayer(self.mainMenu)

    def loadImgDrawing(self, target, name, fileName, textureSize=None):
        """
        Load an SVG drawing synchronously.

        @param target:      An object that will own the drawing
        @param name:        The name of the attribute the drawing will be assigned to
        @param fileName:    The name of the file in the data directory
        @param textureSize: Either None or (x, y), in which case the file will
                            be rendered to an x by y texture
        @return:            L{ImgDrawing} instance
        """
        return self.data.loadImgDrawing(target, name, fileName, textureSize)

    #volshebnyi
    def drawStarScore(self,
                      screenwidth,
                      screenheight,
                      xpos,
                      ypos,
                      stars,
                      scale=None,
                      horiz_spacing=1.2,
                      space=1.0,
                      hqStar=False,
                      align=LEFT):
        minScale = 0.02
        w = screenwidth
        h = screenheight
        if scale is None:
            scale = minScale
        elif scale < minScale:
            scale = minScale
        if self.data.fcStars and stars == 7:
            star = self.data.starFC
        else:
            star = self.data.starPerfect
        wide = scale * horiz_spacing
        if align == CENTER:  #center - akedrou (simplifying the alignment...)
            xpos -= (2 * wide)
        elif align == RIGHT:  #right
            xpos -= (4 * wide)
        if stars > 5:
            for j in range(5):

                if self.data.maskStars:
                    if self.data.theme == 2:
                        drawImage(star,
                                  scale=(scale, -scale),
                                  coord=(w * (xpos + wide * j) * space**4,
                                         h * ypos),
                                  color=(1, 1, 0, 1),
                                  stretched=KEEP_ASPECT | FIT_WIDTH)
                    else:
                        drawImage(star,
                                  scale=(scale, -scale),
                                  coord=(w * (xpos + wide * j) * space**4,
                                         h * ypos),
                                  color=(0, 1, 0, 1),
                                  stretched=KEEP_ASPECT | FIT_WIDTH)
                else:
                    drawImage(star,
                              scale=(scale, -scale),
                              coord=(w * (xpos + wide * j) * space**4,
                                     h * ypos),
                              stretched=KEEP_ASPECT | FIT_WIDTH)
        else:
            for j in range(5):
                if j < stars:
                    if hqStar:
                        star = self.data.star4
                    else:
                        star = self.data.star2
                else:
                    if hqStar:
                        star = self.data.star3
                    else:
                        star = self.data.star1
                drawImage(star,
                          scale=(scale, -scale),
                          coord=(w * (xpos + wide * j) * space**4, h * ypos),
                          stretched=KEEP_ASPECT | FIT_WIDTH)

    #glorandwarf: renamed to retrieve the path of the file
    def fileExists(self, fileName):
        return self.data.fileExists(fileName)

    def getPath(self, fileName):
        return self.data.getPath(fileName)

    def loading(self):
        """Loading state loop."""
        done = self.task.run()
        self.clearScreen()

        if self.data.essentialResourcesLoaded():
            if not self.loadingScreenShown:
                self.loadingScreenShown = True
                Dialogs.showLoadingScreen(self, self.data.resourcesLoaded)
                if self.startupLayer:
                    self.view.pushLayer(self.startupLayer)
                self.mainloop = self.main
            self.view.render()
        self.video.flip()
        return done

    def clearScreen(self):
        self.svg.clear(*self.theme.backgroundColor)

    def fadeScreen(self, v):
        """
        Fade the screen to a dark color to make whatever is on top easier to read.

        @param v: Visibility factor [0..1], 0 is fully visible
        """
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glEnable(GL_COLOR_MATERIAL)

        glBegin(GL_TRIANGLE_STRIP)
        glColor4f(0, 0, 0, .3 - v * .3)
        glVertex2f(0, 0)
        glColor4f(0, 0, 0, .3 - v * .3)
        glVertex2f(1, 0)
        glColor4f(0, 0, 0, .9 - v * .9)
        glVertex2f(0, 1)
        glColor4f(0, 0, 0, .9 - v * .9)
        glVertex2f(1, 1)
        glEnd()

    def enableGarbageCollection(self, enabled):
        """
        Enable or disable garbage collection whenever a random garbage
        collection run would be undesirable. Disabling the garbage collector
        has the unfortunate side-effect that your memory usage will skyrocket.
        """
        if enabled:
            gc.enable()
        else:
            gc.disable()

    def collectGarbage(self):
        """
        Run a garbage collection run.
        """
        gc.collect()

    def main(self):
        """Main state loop."""
        done = self.task.run()
        self.clearScreen()
        self.view.render()
        if self.debugLayer:
            self.debugLayer.render(1.0, True)
        self.video.flip()

        # Calculate FPS every 2 seconds
        if self.clock.fpsTime >= 2000:
            # evilynux - Printing on the console with a frozen binary may cause a crash.
            self.fpsEstimate = self.clock.get_fps()
            if self.show_fps and not Version.isWindowsExe():
                print("%.2f fps" % self.fpsEstimate)
        return done

    def run(self):
        # Move tick and fps limiting here, the old location did not work well.
        self.tickDelta = self.clock.tick()
        rtn = self.mainloop()
        self.clock.delay(self.fps)
        return rtn
예제 #4
0
class GameEngine(object):
    """The main game engine."""
    def __init__(self, config = None):

        Log.debug("GameEngine class init (GameEngine.py)...")
        self.mainMenu = None    #placeholder for main menu object - to prevent reinstantiation

        self.currentScene = None

        self.versionString = version  #stump: other version stuff moved to allow full version string to be retrieved without instantiating GameEngine
        self.uploadVersion = "%s-4.0" % Version.PROGRAM_NAME #akedrou - the version passed to the upload site.

        self.dataPath = Version.dataPath()
        Log.debug(self.versionString + " starting up...")
        Log.debug("Python version: " + sys.version.split(' ')[0])
        Log.debug("Pygame version: " + str(pygame.version.ver) )
        Log.debug("PyOpenGL version: " + OpenGL.__version__)
        Log.debug("Numpy version: " + np.__version__)
        Log.debug("PIL version: " + Image.VERSION)
        Log.debug("sys.argv: " + repr(sys.argv))
        Log.debug("os.name: " + os.name)
        Log.debug("sys.platform: " + sys.platform)
        if os.name == 'nt':
            import win32api
            Log.debug("win32api.GetVersionEx(1): " + repr(win32api.GetVersionEx(1)))
        elif os.name == 'posix':
            Log.debug("os.uname(): " + repr(os.uname()))

        """
        Constructor.
        @param config:  L{Config} instance for settings
        """

        self.tutorialFolder = "tutorials"

        if not config:
            config = Config.load()

        self.config  = config

        fps          = self.config.get("video", "fps")

        self.fps = fps
        self.running = True
        self.clock = FpsTimer()
        self.tickDelta = 0
        self.task = TaskEngine(self)
        
        # Compatiblity task management
        self.addTask = self.task.addTask
        self.removeTask = self.task.removeTask
        self.pauseTask = self.task.pauseTask
        self.resumeTask = self.task.resumeTask

        self.title             = self.versionString
        self.restartRequested  = False

        # evilynux - Check if theme icon exists first, then fallback on FoFiX icon.
        themename = self.config.get("coffee", "themename")
        themeicon = os.path.join(Version.dataPath(), "themes", themename, "icon.png")
        fofixicon = os.path.join(Version.dataPath(), "fofix_icon.png")
        icon = None
        if os.path.exists(themeicon):
            icon = themeicon
        elif os.path.exists(fofixicon):
            icon = fofixicon

        self.video             = Video(self.title, icon)
        if self.config.get("video", "disable_screensaver"):
            self.video.disableScreensaver()

        self.audio             = Audio()
        self.fpsEstimate       = 0
        self.priority          = self.config.get("engine", "highpriority")
        self.show_fps          = self.config.get("video", "show_fps")
        self.advSettings       = self.config.get("game", "adv_settings")
        self.restartRequired   = False
        self.quicksetRestart   = False
        self.quicksetPerf      = self.config.get("quickset", "performance")
        self.scrollRate        = self.config.get("game", "scroll_rate")
        self.scrollDelay       = self.config.get("game", "scroll_delay")

        Log.debug("Initializing audio.")
        frequency    = self.config.get("audio", "frequency")
        bits         = self.config.get("audio", "bits")
        stereo       = self.config.get("audio", "stereo")
        bufferSize   = self.config.get("audio", "buffersize")
        self.audio.open(frequency = frequency, bits = bits, stereo = stereo, bufferSize = bufferSize)

        self.cmdPlay           = 0
        self.cmdMode           = None
        self.cmdDiff           = None
        self.cmdPart           = None

        self.gameStarted       = False
        self.world             = None

        self.audioSpeedFactor  = 1.0

        Log.debug("Initializing video.")
        #myfingershurt: ensuring windowed mode starts up in center of the screen instead of cascading positions:
        os.environ['SDL_VIDEO_WINDOW_POS'] = 'center'

        width, height = [int(s) for s in self.config.get("video", "resolution").split("x")]
        fullscreen    = self.config.get("video", "fullscreen")
        multisamples  = self.config.get("video", "multisamples")
        self.video.setMode((width, height), fullscreen = fullscreen, multisamples = multisamples)
        Log.debug("OpenGL version: " + glGetString(GL_VERSION))
        Log.debug("OpenGL vendor: " + glGetString(GL_VENDOR))
        Log.debug("OpenGL renderer: " + glGetString(GL_RENDERER))
        Log.debug("OpenGL extensions: " + ' '.join(sorted(glGetString(GL_EXTENSIONS).split())))

        if self.video.default:
            self.config.set("video", "fullscreen", False)
            self.config.set("video", "resolution", "800x600")

        if self.config.get("video", "shader_use"):
            shaders.set(os.path.join(Version.dataPath(), "shaders"))

        # Enable the high priority timer if configured
        if self.priority:
            Log.debug("Enabling high priority timer.")
            self.fps = 0 # High priority

        # evilynux - This was generating an error on the first pass (at least under
        #            GNU/Linux) as the Viewport was not set yet.
        try:
            viewport = glGetIntegerv(GL_VIEWPORT)
        except:
            viewport = [0, 0, width, height]
        h = viewport[3] - viewport[1]
        w = viewport[2] - viewport[0]
        geometry = (0, 0, w, h)
        self.svg = SvgContext(geometry)
        glViewport(int(viewport[0]), int(viewport[1]), int(viewport[2]), int(viewport[3]))

        self.startupMessages   = self.video.error
        self.input     = Input()
        self.view      = View(self, geometry)
        self.resizeScreen(w, h)

        self.resource  = Resource(Version.dataPath())
        self.mainloop  = self.loading
        self.menuMusic = False

        self.setlistMsg = None


        # Load game modifications
        Mod.init(self)
        self.task.addTask(self.input, synced = False)

        self.task.addTask(self.view, synced = False)

        self.task.addTask(self.resource, synced = False)

        self.data = Data(self.resource, self.svg)

        ##MFH: Animated stage folder selection option
        #<themename>\Stages still contains the backgrounds for when stage rotation is off, and practice.png
        #subfolders under Stages\ will each be treated as a separate animated stage set

        self.stageFolders = []
        currentTheme = themename

        stagespath = os.path.join(Version.dataPath(), "themes", currentTheme, "backgrounds")
        themepath  = os.path.join(Version.dataPath(), "themes", currentTheme)
        if os.path.exists(stagespath):
            self.stageFolders = []
            allFolders = os.listdir(stagespath)   #this also includes all the stage files - so check to see if there is at least one .png file inside each folder to be sure it's an animated stage folder
            for name in allFolders:
                aniStageFolderListing = []
                thisIsAnAnimatedStageFolder = False
                try:
                    aniStageFolderListing = os.listdir(os.path.join(stagespath,name))
                except Exception:
                    thisIsAnAnimatedStageFolder = False
                for aniFile in aniStageFolderListing:
                    if os.path.splitext(aniFile)[1] in [".png", ".jpg", ".jpeg"]:
                        # we've found at least one .png file here, chances are this is a valid animated stage folder
                        thisIsAnAnimatedStageFolder = True
                if thisIsAnAnimatedStageFolder:
                    self.stageFolders.append(name)


            i = len(self.stageFolders)
            if i > 0: #only set default to first animated subfolder if one exists - otherwise use Normal!
                defaultAniStage = str(self.stageFolders[0])
            else:
                defaultAniStage = "Normal"
            Log.debug("Default animated stage for " + currentTheme + " theme = " + defaultAniStage)
            aniStageOptions = dict([(str(self.stageFolders[n]),self.stageFolders[n]) for n in range(0, i)])
            aniStageOptions.update({"Normal":_("Slideshow")})
            if i > 1:   #only add Random setting if more than one animated stage exists
                aniStageOptions.update({"Random":_("Random")})
            Config.define("game", "animated_stage_folder", str, defaultAniStage, text = _("Animated Stage"), options = aniStageOptions )

            #MFH: here, need to track and check a new ini entry for last theme - so when theme changes we can re-default animated stage to first found
            lastTheme = self.config.get("game","last_theme")
            if lastTheme == "" or lastTheme != currentTheme:   #MFH - no last theme, and theme just changed:
                self.config.set("game","animated_stage_folder",defaultAniStage)   #force defaultAniStage
            self.config.set("game","last_theme",currentTheme)

            selectedAnimatedStage = self.config.get("game", "animated_stage_folder")
            if selectedAnimatedStage != "Normal" and selectedAnimatedStage != "Random":
                if not os.path.exists(os.path.join(stagespath,selectedAnimatedStage)):
                    Log.warn("Selected animated stage folder " + selectedAnimatedStage + " does not exist, forcing Normal.")
                    self.config.set("game","animated_stage_folder","Normal") #MFH: force "Standard" currently selected animated stage folder is invalid
        else:
            Config.define("game", "animated_stage_folder", str, "None", text = _("Animated Stage"), options = ["None",_("None")])
            Log.warn("No stages\ folder found, forcing None setting for Animated Stage.")
            self.config.set("game","animated_stage_folder", "None") #MFH: force "None" when Stages folder can't be found



        try:
            fp, pathname, description = imp.find_module("CustomTheme",[themepath])
            theme = imp.load_module("CustomTheme", fp, pathname, description)
            self.theme = theme.CustomTheme(themepath, themename)
        except ImportError:
            self.theme = Theme(themepath, themename)

        self.task.addTask(self.theme)


        self.input.addKeyListener(FullScreenSwitcher(self), priority = True)
        self.input.addSystemEventListener(SystemEventHandler(self))

        self.debugLayer         = None
        self.startupLayer       = None
        self.loadingScreenShown = False
        self.graphicMenuShown   = False

        Log.debug("Ready.")


    # evilynux - This stops the crowd cheers if they're still playing (issue 317).
    def quit(self):
        # evilynux - self.audio.close() crashes when we attempt to restart
        if not self.restartRequested:
            self.audio.close()
        Player.savePlayers()
        for taskData in list(self.task.tasks):
            self.task.removeTask(taskData['task'])
        self.running = False

    def setStartupLayer(self, startupLayer):
        """
        Set the L{Layer} that will be shown when the all
        the resources have been loaded. See L{Data}

        @param startupLayer:    Startup L{Layer}
        """
        self.startupLayer = startupLayer

    def isDebugModeEnabled(self):
        return bool(self.debugLayer)

    def setDebugModeEnabled(self, enabled):
        """
        Show or hide the debug layer.

        @type enabled: bool
        """
        if enabled:
            self.debugLayer = DebugLayer(self)
        else:
            self.debugLayer = None

    def toggleFullscreen(self):
        """
        Toggle between fullscreen and windowed mode.

        @return: True on success
        """
        if not self.video.toggleFullscreen():
            # on windows, the fullscreen toggle kills our textures, se we must restart the whole game
            self.input.broadcastSystemEvent("restartRequested")
            self.config.set("video", "fullscreen", not self.video.fullscreen)
            return True
        self.config.set("video", "fullscreen", self.video.fullscreen)
        return True

    def restart(self):
        """Restart the game."""
        if not self.restartRequested:
            self.restartRequested = True
            self.input.broadcastSystemEvent("restartRequested")
        else:
            self.quit()

    def resizeScreen(self, width, height):
        """
        Resize the game screen.

        @param width:   New width in pixels
        @param height:  New height in pixels
        """
        self.view.setGeometry((0, 0, width, height))
        self.svg.setGeometry((0, 0, width, height))

    def startWorld(self, players, maxplayers = None, gameMode = 0, multiMode = 0, allowGuitar = True, allowDrum = True, allowMic = False, tutorial = False):
        self.world = World(self, players, maxplayers, gameMode, multiMode, allowGuitar, allowDrum, allowMic, tutorial)

    def finishGame(self):
        if not self.world:
            Log.notice("GameEngine.finishGame called before World created.")
            return
        self.world.finishGame()
        self.world = None
        self.gameStarted = False
        self.view.pushLayer(self.mainMenu)

    def loadImgDrawing(self, target, name, fileName, textureSize = None):
        """
        Load an SVG drawing synchronously.

        @param target:      An object that will own the drawing
        @param name:        The name of the attribute the drawing will be assigned to
        @param fileName:    The name of the file in the data directory
        @param textureSize: Either None or (x, y), in which case the file will
                            be rendered to an x by y texture
        @return:            L{ImgDrawing} instance
        """
        return self.data.loadImgDrawing(target, name, fileName, textureSize)

    #volshebnyi
    def drawStarScore(self, screenwidth, screenheight, xpos, ypos, stars, scale = None, horiz_spacing = 1.2, space = 1.0, hqStar = False, align = LEFT):
        minScale = 0.02
        w = screenwidth
        h = screenheight
        if not scale:
            scale = minScale
        elif scale < minScale:
            scale = minScale
        if self.data.fcStars and stars == 7:
            star = self.data.starFC
        else:
            star = self.data.starPerfect
        wide = scale * horiz_spacing
        if align == CENTER: #center - akedrou (simplifying the alignment...)
            xpos  -= (2 * wide)
        elif align == RIGHT: #right
            xpos  -= (4 * wide)
        if stars > 5:
            for j in range(5):

                if self.data.maskStars:
                    if self.data.theme == 2:
                        drawImage(star, scale = (scale,-scale), coord = (w*(xpos+wide*j)*space**4,h*ypos), color = (1, 1, 0, 1), stretched = KEEP_ASPECT | FIT_WIDTH)
                    else:
                        drawImage(star, scale = (scale,-scale), coord = (w*(xpos+wide*j)*space**4,h*ypos), color = (0, 1, 0, 1), stretched = KEEP_ASPECT | FIT_WIDTH)
                else:
                    drawImage(star, scale = (scale,-scale), coord = (w*(xpos+wide*j)*space**4,h*ypos), stretched = KEEP_ASPECT | FIT_WIDTH)
        else:
            for j in range(5):
                if j < stars:
                    if hqStar:
                        star = self.data.star4
                    else:
                        star = self.data.star2
                else:
                    if hqStar:
                        star = self.data.star3
                    else:
                        star = self.data.star1
                drawImage(star, scale = (scale,-scale), coord = (w*(xpos+wide*j)*space**4,h*ypos), stretched = KEEP_ASPECT | FIT_WIDTH)

    #glorandwarf: renamed to retrieve the path of the file
    def fileExists(self, fileName):
        return self.data.fileExists(fileName)

    def getPath(self, fileName):
        return self.data.getPath(fileName)

    def loading(self):
        """Loading state loop."""
        done = self.task.run()
        self.clearScreen()

        if self.data.essentialResourcesLoaded():
            if not self.loadingScreenShown:
                self.loadingScreenShown = True
                Dialogs.showLoadingScreen(self, self.data.resourcesLoaded)
                if self.startupLayer:
                    self.view.pushLayer(self.startupLayer)
                self.mainloop = self.main
            self.view.render()
        self.video.flip()
        return done

    def clearScreen(self):
        self.svg.clear(*self.theme.backgroundColor)

    def fadeScreen(self, v):
        """
        Fade the screen to a dark color to make whatever is on top easier to read.

        @param v: Visibility factor [0..1], 0 is fully visible
        """
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glEnable(GL_COLOR_MATERIAL)

        glBegin(GL_TRIANGLE_STRIP)
        glColor4f(0, 0, 0, .3 - v * .3)
        glVertex2f(0, 0)
        glColor4f(0, 0, 0, .3 - v * .3)
        glVertex2f(1, 0)
        glColor4f(0, 0, 0, .9 - v * .9)
        glVertex2f(0, 1)
        glColor4f(0, 0, 0, .9 - v * .9)
        glVertex2f(1, 1)
        glEnd()

    def enableGarbageCollection(self, enabled):
        """
        Enable or disable garbage collection whenever a random garbage
        collection run would be undesirable. Disabling the garbage collector
        has the unfortunate side-effect that your memory usage will skyrocket.
        """
        if enabled:
            gc.enable()
        else:
            gc.disable()

    def collectGarbage(self):
        """
        Run a garbage collection run.
        """
        gc.collect()

    def main(self):
        """Main state loop."""
        done = self.task.run()
        self.clearScreen()
        self.view.render()
        if self.debugLayer:
            self.debugLayer.render(1.0, True)
        self.video.flip()

        # Calculate FPS every 2 seconds
        if self.clock.fpsTime >= 2000:
            # evilynux - Printing on the console with a frozen binary may cause a crash.
            self.fpsEstimate = self.clock.get_fps()
            if self.show_fps and not Version.isWindowsExe():
                print("%.2f fps" % self.fpsEstimate)
        return done

    def run(self):
        # Move tick and fps limiting here, the old location did not work well.
        self.tickDelta = self.clock.tick()
        rtn = self.mainloop()
        self.clock.delay(self.fps)
        return rtn