#################################################################### # -*- coding: iso-8859-1 -*- # # # # Frets on Fire # # Copyright (C) 2006 Sami Kyöstilä # # 2008 myfingershurt # # 2008 Blazingamer # # 2008 evilynux # # # # This program is free software; you can redistribute it and/or # # modify it under the terms of the GNU General Public License # # as published by the Free Software Foundation; either version 2 # # of the License, or (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the Free Software # # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # # MA 02110-1301, USA. # ##################################################################### from OpenGL.GL import * import math from FakeNetworking import socket from View import BackgroundLayer from Menu import Menu from Lobby import Lobby from Svg import ImgDrawing from Language import _ import Dialogs import Config import Audio import Settings import datetime import sys import Theme import Player import Version from Shader import shaders import sys import os #myfingershurt: needed for random menu music: import random import string import Log class MainMenu(BackgroundLayer): def __init__(self, engine): self.engine = engine self.logClassInits = Config.get("game", "log_class_inits") if self.logClassInits == 1: Log.debug("MainMenu class init (MainMenu.py)...") self.time = 0.0 self.nextLayer = None self.visibility = 0.0 self.active = False Player.practiceMode = False #myfingershurt: removing neck menu requirement: #self.neckMenuEnabled = False #self.neckMenuEnabled = Config.get("game", "neck_select_enabled") self.gfxVersionTag = Config.get("game", "gfx_version_tag") #self.tut = Config.get("game", "tut") self.chosenNeck = Config.get("game", "default_neck") exists = 0 #neck fallback to random if doesn't exist. try: # evilynux - first assume the chosenNeck contains the full filename engine.loadImgDrawing(self, "ok", os.path.join("necks",self.chosenNeck+".png")) except IOError: try: engine.loadImgDrawing(self, "ok", os.path.join("necks","Neck_"+self.chosenNeck+".png")) except IOError: pass else: exists = 1 else: exists = 1 #MFH - fallback logic now supports a couple valid default neck filenames #MFH - check for Neck_1 if exists == 0: try: engine.loadImgDrawing(self, "ok", os.path.join("necks","Neck_1.png")) except IOError: pass else: Config.set("game", "default_neck", "1") Log.warn("Default chosen neck not valid; fallback Neck_1.png forced.") exists = 1 #MFH - check for defaultneck if exists == 0: try: engine.loadImgDrawing(self, "ok", os.path.join("necks","defaultneck.png")) except IOError: #we don't really need to be accepting this except... ...yea, sorry. raise IOError, "Default chosen neck not valid; fallbacks Neck_1.png and defaultneck.png also not valid!" else: Log.warn("Default chosen neck not valid; fallback defaultneck.png forced.") Config.set("game", "default_neck", "defaultneck") exists = 1 dPlayerConfig = None #Get theme self.theme = self.engine.data.theme self.themeCoOp = self.engine.data.themeCoOp self.themename = self.engine.data.themeLabel self.useSoloMenu = Theme.use_solo_submenu allowMic = True if self.theme == 0: allowMic = False try: #blazingamer self.menux = Theme.menuX self.menuy = Theme.menuY except Exception, e: Log.warn("Unable to load Theme menuX / Y positions: %s" % e) self.menux = None self.menuy = None self.rbmenu = Theme.menuRB #MFH self.main_menu_scale = Theme.main_menu_scaleVar self.main_menu_vspacing = Theme.main_menu_vspacingVar if self.main_menu_scale == None: self.main_menu_scale = .5 if self.main_menu_vspacing == None: self.main_menu_vspacing = 0.09 try: self.engine.loadImgDrawing(self, "background", os.path.join("themes",self.themename,"menu","mainbg.png")) except IOError: self.background = None self.engine.loadImgDrawing(self, "BGText", os.path.join("themes",self.themename,"menu","maintext.png")) try: self.engine.loadImgDrawing(self, "optionsBG", os.path.join("themes",self.themename,"menu","optionsbg.png")) except IOError: self.optionsBG = None self.engine.loadImgDrawing(self, "optionsPanel", os.path.join("themes",self.themename,"menu","optionspanel.png")) #racer: added version tag if self.gfxVersionTag or Theme.versiontag == True: try: self.engine.loadImgDrawing(self, "version", os.path.join("themes",self.themename,"menu","versiontag.png")) except IOError: try: self.engine.loadImgDrawing(self, "version", "versiontag.png") #falls back on default versiontag.png in data\ folder except IOError: self.version = None else: self.version = None #myfingershurt: random main menu music function, menu.ogg and menuXX.ogg (any filename with "menu" as the first 4 letters) filepath = self.engine.getPath(os.path.join("themes",self.themename,"sounds")) self.files = [] allfiles = os.listdir(filepath) for name in allfiles: if os.path.splitext(name)[1] == ".ogg": if string.find(name,"menu") > -1: self.files.append(name) if self.files: i = random.randint(0,len(self.files)-1) filename = self.files[i] sound = os.path.join("themes",self.themename,"sounds",filename) self.menumusic = True engine.menuMusic = True self.song = Audio.Music(self.engine.resource.fileName(sound)) self.song.setVolume(self.engine.config.get("audio", "menu_volume")) self.song.play(0) #no loop else: self.menumusic = False #####======= Racer: New Main Menu ======#### self.opt_text_color = Theme.hexToColor(Theme.opt_text_colorVar) self.opt_selected_color = Theme.hexToColor(Theme.opt_selected_colorVar) if self.opt_text_color == None: self.opt_text_color = (1,1,1) if self.opt_selected_color == None: self.opt_selected_color = (1,0.75,0) newMultiplayerMenu = [ (_("Host Multiplayer Game"), self.hostMultiplayerGame), (_("Join Multiplayer Game"), self.joinMultiplayerGame), ] editorMenu = Menu(self.engine, [ (_("Edit Existing Song"), self.startEditor), (_("Import New Song"), self.startImporter), (_("Import GH(tm) Songs"), self.startGHImporter), ]) trainingMenu = [ (_("Tutorials"), self.showTutorial), (_("Practice"), lambda: self.newLocalGame(mode1p = 1)), ] self.opt_bkg_size = [float(i) for i in Theme.opt_bkg_size] strCareer = "" strQuickplay = "" strSolo = "" strMultiplayer = "" strTraining = "" strSettings = "" strQuit = "" if self.theme == 1 and self.themeCoOp: #Worldrave - Put GH Co-op ahead of FoFix co-op for GH based theme's. Made more sense. multPlayerMenu = [ (_("Face-Off"), lambda: self.newLocalGame(players = 2, maxplayers = -1)), (_("Pro Face-Off"), lambda: self.newLocalGame(players = 2, mode2p = 1, maxplayers = -1)), (_("GH Battle"), lambda: self.newLocalGame(players = 2, mode2p = 6, maxplayers = -1, allowDrum = False)), #akedrou- so you can block drums (_("Party Mode"), lambda: self.newLocalGame(mode2p = 2)), (_("Co-Op"), lambda: self.newLocalGame(players = 2, mode2p = 5)), (_("FoFiX Co-Op"), lambda: self.newLocalGame(players = 2, mode2p = 3, allowMic = allowMic)), #Worldrave - Re-added this option for now. ] elif self.theme == 1 and not self.themeCoOp: multPlayerMenu = [ (_("Face-Off"), lambda: self.newLocalGame(players = 2, maxplayers = -1)), (_("Pro Face-Off"), lambda: self.newLocalGame(players = 2, mode2p = 1, maxplayers = -1)), (_("Party Mode"), lambda: self.newLocalGame(mode2p = 2)), ] elif self.theme == 2: multPlayerMenu = [ (_("FoFiX Co-Op"), lambda: self.newLocalGame(players = 2, mode2p = 3, maxplayers = 4, allowMic = allowMic)), (_("RB Co-Op"), lambda: self.newLocalGame(players = 2, mode2p = 4, maxplayers = 4, allowMic = allowMic)), (_("GH Co-Op"), lambda: self.newLocalGame(players = 2, mode2p = 5, maxplayers = 4)), (_("Face-Off"), lambda: self.newLocalGame(players = 2, maxplayers = -1)), (_("Pro Face-Off"), lambda: self.newLocalGame(players = 2, mode2p = 1, maxplayers = -1)), (_("Party Mode"), lambda: self.newLocalGame(mode2p = 2)), ] else: multPlayerMenu = [ (_("FoFiX Co-Op"), lambda: self.newLocalGame(players = 2, mode2p = 3, allowMic = allowMic)), (_("Face-Off"), lambda: self.newLocalGame(players = 2, maxplayers = -1)), (_("Pro Face-Off"), lambda: self.newLocalGame(players = 2, mode2p = 1, maxplayers = -1)), (_("Party Mode"), lambda: self.newLocalGame(mode2p = 2)), ] if self.useSoloMenu is None: if self.theme == 0 or self.theme == 1: #GH themes = 6 main menu selections self.useSoloMenu = False else: #RB themes = 5 main menu selections self.useSoloMenu = True if not self.useSoloMenu: mainMenu = [ (strCareer, lambda: self.newLocalGame(mode1p = 2, allowMic = allowMic)), (strQuickplay, lambda: self.newLocalGame(allowMic = allowMic)), ((strMultiplayer,"multiplayer"), multPlayerMenu), ((strTraining,"training"), trainingMenu), ((strSettings,"settings"), self.settingsMenu), (strQuit, self.quit), ] else: soloMenu = [ (_("Solo Tour"), lambda: self.newLocalGame(mode1p = 2, allowMic = allowMic)), (_("Quickplay"), lambda: self.newLocalGame(allowMic = allowMic)), ] mainMenu = [ #( ( _(strSolo), 1, (0,0) ), soloMenu), ((strSolo,"solo"), soloMenu), ((strMultiplayer,"multiplayer"), multPlayerMenu), ((strTraining,"training"), trainingMenu), ((strSettings,"settings"), self.settingsMenu), (strQuit, self.quit), ] self.menu = Menu(self.engine, mainMenu, onClose = lambda: self.engine.view.popLayer(self), pos = (12,12), textColor = self.opt_text_color, selectedColor = self.opt_selected_color) engine.mainMenu = self #Points engine.mainMenu to the one and only MainMenu object instance ## whether the main menu has come into view at least once self.shownOnce = False def settingsMenu(self): if self.engine.advSettings: self.settingsMenuObject = Settings.SettingsMenu(self.engine) else: self.settingsMenuObject = Settings.BasicSettingsMenu(self.engine) return self.settingsMenuObject def shown(self): self.engine.view.pushLayer(self.menu) self.engine.stopServer() shaders.checkIfEnabled() if not self.shownOnce: self.shownOnce = True if hasattr(sys, 'frozen'): #stump: Check whether this is a non-svn binary being run from an svn working copy. if os.path.isdir(os.path.join('src', '.svn')) and 'development' not in Version.version(): Dialogs.showMessage(self.engine, _('This binary release is being run from a Subversion working copy. This is not the correct way to run FoFiX from Subversion. Please see one of the following web pages to set your Subversion working copy up correctly:') + '\n\nhttp://code.google.com/p/fofix/wiki/RunningUnderPython26' + '\nhttp://code.google.com/p/fofix/wiki/RequiredSourceModules') #stump: Check whether this is an svn binary not being run from an svn working copy elif not os.path.isdir(os.path.join('src', '.svn')) and 'development' in Version.version(): Dialogs.showMessage(self.engine, _('This binary was built from a Subversion working copy but is not running from one. The FoFiX Team will not provide any support whatsoever for this binary. Please see the following site for official binary releases:') + '\n\nhttp://code.google.com/p/fofix/') def runMusic(self): if not self.song.isPlaying(): #re-randomize if self.files: i = random.randint(0,len(self.files)-1) filename = self.files[i] sound = os.path.join("themes",self.themename,"sounds",filename) self.menumusic = True self.engine.menuMusic = True #self.song = Audio.Sound(self.engine.resource.fileName(sound)) self.song = Audio.Music(self.engine.resource.fileName(sound)) self.song.setVolume(self.engine.config.get("audio", "menu_volume")) #self.song.play(-1) self.song.play(0) #no loop else: self.menumusic = False self.engine.menuMusic = False def setMenuVolume(self): if self.menumusic and self.song.isPlaying(): self.song.setVolume(self.engine.config.get("audio", "menu_volume")) def cutMusic(self): if self.menumusic: if self.song and not self.engine.menuMusic: self.song.fadeout(1400) def hidden(self): self.engine.view.popLayer(self.menu) self.cutMusic() if self.nextLayer: self.engine.view.pushLayer(self.nextLayer()) self.nextLayer = None else: self.engine.quit() def quit(self): self.engine.view.popLayer(self.menu) def catchErrors(function): def harness(self, *args, **kwargs): try: try: function(self, *args, **kwargs) except: import traceback Log.error("Traceback:" + traceback.format_exc() ) traceback.print_exc() raise except socket.error, e: Dialogs.showMessage(self.engine, unicode(e[1])) except KeyboardInterrupt: pass except Exception, e: #MFH - enhancing error trapping and locating logic if e: Dialogs.showMessage(self.engine, unicode(e)) return harness def launchLayer(self, layerFunc): if not self.nextLayer: self.nextLayer = layerFunc self.engine.view.popAllLayers() #launchLayer = catchErrors(launchLayer) #MFH - trying to catch errors def showTutorial(self): # evilynux - Make sure tutorial exists before launching #tutorialpath = self.engine.getPath(os.path.join("songs","tutorial")) tutorialpath = self.engine.tutorialFolder if not os.path.isdir(self.engine.resource.fileName(tutorialpath)): Log.debug("No folder found: %s" % tutorialpath) Dialogs.showMessage(self.engine, _("No tutorials found!")) return if self.engine.isServerRunning(): return players = Dialogs.activateControllers(self.engine, 1) #akedrou if players == 0: return Config.set("game","game_mode", 0) #MFH - ensure tutorial can work with new logic that depends on this mode variable Config.set("game","multiplayer_mode", 0) #MFH - ensure tutorial can work with new logic that depends on this mode variable Config.set("game", "players", 1) Config.set("game", "tut", True) #Config.set("game","game_mode", 1) #MFH - don't force practice mode.... this is problematic. self.engine.startServer() self.engine.resource.load(self, "session", lambda: self.engine.connect("127.0.0.1"), synch = True) if Dialogs.showLoadingScreen(self.engine, lambda: self.session and self.session.isConnected): self.launchLayer(lambda: Lobby(self.engine, self.session, singlePlayer = True)) showTutorial = catchErrors(showTutorial) #MFH: adding deprecated support for EOF's method of quickstarting a song to test it def newSinglePlayerGame(self): self.newLocalGame() #just call start function with default settings = 1p quickplay def newLocalGame(self, players=1, mode1p=0, mode2p=0, maxplayers = None, allowGuitar = True, allowDrum = True, allowMic = False): #mode1p=0(quickplay),1(practice),2(career) / mode2p=0(faceoff),1(profaceoff) self.engine.data.acceptSound.play() players = Dialogs.activateControllers(self.engine, players, maxplayers, allowGuitar, allowDrum, allowMic) #akedrou if players == 0: if self.engine.cmdPlay == 2: self.engine.cmdPlay = 0 return Config.set("game", "players", players) Config.set("game","game_mode", mode1p) Config.set("game","multiplayer_mode", mode2p) if Config.get("game", "tut") == True: Config.set("game", "tut", False) #Config.set("game", "selected_library", "") #Config.set("game", "selected_song", "") #MFH - testing new traceback logging: #raise TypeError if self.engine.isServerRunning(): return self.engine.startServer() self.engine.resource.load(self, "session", lambda: self.engine.connect("127.0.0.1"), synch = True) if Dialogs.showLoadingScreen(self.engine, lambda: self.session and self.session.isConnected): self.launchLayer(lambda: Lobby(self.engine, self.session, singlePlayer = True)) newLocalGame = catchErrors(newLocalGame) def hostMultiplayerGame(self): self.engine.startServer() self.engine.resource.load(self, "session", lambda: self.engine.connect("127.0.0.1")) if Dialogs.showLoadingScreen(self.engine, lambda: self.session and self.session.isConnected): self.launchLayer(lambda: Lobby(self.engine, self.session)) hostMultiplayerGame = catchErrors(hostMultiplayerGame) def joinMultiplayerGame(self, address = None): if not address: address = Dialogs.getText(self.engine, _("Enter the server address:"), "127.0.0.1") if not address: return self.engine.resource.load(self, "session", lambda: self.engine.connect(address)) if Dialogs.showLoadingScreen(self.engine, lambda: self.session and self.session.isConnected, text = _("Connecting...")): self.launchLayer(lambda: Lobby(self.engine, self.session)) joinMultiplayerGame = catchErrors(joinMultiplayerGame) def startEditor(self): self.launchLayer(lambda: Editor(self.engine)) startEditor = catchErrors(startEditor) def startImporter(self): self.launchLayer(lambda: Importer(self.engine)) startImporter = catchErrors(startImporter) def startGHImporter(self): self.launchLayer(lambda: GHImporter(self.engine)) startGHImporter = catchErrors(startGHImporter) def run(self, ticks): self.time += ticks / 50.0 if self.engine.cmdPlay == 1: self.engine.cmdPlay = 4 elif self.engine.cmdPlay == 4: #this frame runs the engine an extra loop to allow the font to load... #evilynux - improve cmdline support self.engine.cmdPlay = 2 self.newLocalGame(players = Config.get("game", "players"), mode1p = Config.get("game","game_mode"), mode2p = Config.get("game","multiplayer_mode")) elif self.engine.cmdPlay == 3: self.quit() if self.menumusic: #MFH self.runMusic() def render(self, visibility, topMost): self.engine.view.setViewport(1,0) self.visibility = visibility if self.rbmenu: v = 1.0 - ((1 - visibility) ** 2) else: v = 1 if v == 1: self.engine.view.transitionTime = 1 if self.menu.active and not self.active: self.active = True t = self.time / 100 w, h, = self.engine.view.geometry[2:4] r = .5 if not self.useSoloMenu: if self.active: if self.engine.view.topLayer() is not None: if self.optionsBG != None: self.engine.drawImage(self.optionsBG, (self.opt_bkg_size[2],-self.opt_bkg_size[3]), (w*self.opt_bkg_size[0],h*self.opt_bkg_size[1]), stretched = 3) self.engine.drawImage(self.optionsPanel, (0.5,-0.5), (w/1.7, h/2)) else: self.engine.drawImage(self.engine.data.loadingImage, (1.0,-1.0), (w/2, h/2), stretched = 3) if self.menu.active and self.engine.cmdPlay == 0: if self.background != None: #MFH - auto-scaling self.engine.drawImage(self.background, (1.0,-1.0), (w/2, h/2), stretched = 3) for i in range(0,6): #Item selected if self.menu.currentIndex == i: xpos = (.5,1) #Item unselected else: xpos = (0,.5) #which item? ypos = 1/6.0*i #============blazingamer============ #if menux and/or menuy are not set it will use the default positions for the main text if self.menux == None or self.menuy == None: if self.theme == 0: textcoord = (w*0.5,h*0.45-(h*self.main_menu_vspacing)*i) elif self.theme == 1: textcoord = (w*0.7,h*0.8-(h*self.main_menu_vspacing)*i) #if menux and menuy are set it will use those else: try: textcoord = (w*self.menux,h*self.menuy-(h*self.main_menu_vspacing)*i) except Exception, e: Log.warn("Unable to translate BGText: %s" % e) #=================================== self.engine.drawImage(self.BGText, (.5*self.main_menu_scale,-1/6.0*self.main_menu_scale), textcoord, rect = (xpos[0],xpos[1],ypos,ypos+1/6.0)) else: if self.active: if self.engine.view.topLayer() is not None: if self.optionsBG != None: self.engine.drawImage(self.optionsBG, (self.opt_bkg_size[2],-self.opt_bkg_size[3]), (w*self.opt_bkg_size[0],h*self.opt_bkg_size[1]), stretched = 3) self.engine.drawImage(self.optionsPanel, (0.5,-0.5), (w*0.4, h/2)) else: self.engine.drawImage(self.engine.data.loadingImage, scale = (1.0,-1.0), coord = (w/2,h/2), stretched = 3) if self.menu.active and self.engine.cmdPlay == 0: if self.background != None: self.engine.drawImage(self.background, (1.0,-1.0), (w/2, h/2), stretched = 3) for i in range(0,5): #Item selected if self.menu.currentIndex == i: xpos = (.5,1) #Item unselected else: xpos = (0,.5) #which item? ypos = 1/5.0*i #============blazingamer============ #if menux and/or menuy are not set it will use the default positions for the main text if self.menux == None or self.menuy == None: textcoord = (w*0.2,(h*0.8-(h*self.main_menu_vspacing)*i)*v) #if menux and menuy are set it will use those else: try: textcoord = (w*self.menux,(h*self.menuy-(h*self.main_menu_vspacing)*i)*v) except Exception, e: Log.warn("Unable to translate BGText: %s" % e) #=================================== self.engine.drawImage(self.BGText, (.5*self.main_menu_scale,(-1/5.0*self.main_menu_scale)), textcoord, rect = (xpos[0],xpos[1],ypos,ypos+1/5.0)) #racer: added version tag to main menu: if self.version != None: wfactor = self.version.widthf(pixelw = 640.000) self.engine.drawImage(self.version, (0.5,-0.5),(w*Theme.versiontagposX, h*Theme.versiontagposY)) #worldrave - Added theme settings to control X+Y positions of versiontag.