# The course selection screen. import os import math import pygame import fontfx import colors import ui import records import grades import dance import random import util import options from constants import * from interface import * from courses import CRSFile from pygame.mixer import music NO_BANNER = os.path.join(image_path, "no-banner.png") SORTS = { "title": (lambda x, y: (cmp(x.name.lower(), y.name.lower()))), "mix": (lambda x, y: (cmp(str(x.mixname).lower(), str(y.mixname).lower()) or SORTS["title"](x, y))) } SORT_NAMES = ["mix", "title"] NUM_SORTS = len(SORT_NAMES) CS_HELP = [ "Up / Down: Change song selection", "Left / Right: Change difficulty setting", "Enter / Up Right: Open a folder or start a course", "Escape / Up Left: Close a folder or exit", "Tab / Select: Go to a random course", "Start: Switch to Options screen", "F11: Toggle fullscreen - S: Change the sort mode", ] class CourseDisplay(object): no_banner = pygame.image.load(NO_BANNER) no_banner.set_colorkey(no_banner.get_at([0, 0])) def __init__(self, course, recordkeys, game): # A CRSFile object self.banner_fn = course.banner self.course = course self.mixname = course.mixname self.name = course.name self.banner = None self.isfolder = False self.folder = {} self.banner = None self.clip = None self._songs = [] self.generate_songlist(recordkeys, game) # Note that we don't need to worry about player's best/worst/etc changing # while playing courses, since per-song records don't change. def generate_songlist(self, recordkeys, game): i = 1 for name, diff, mods in self.course.songs: if "*" in name: name, subtitle = "??????????", "" elif name[0] == "BEST": song = recordkeys.get(records.best(name[1], diff, game)) if song: subtitle = (song.info["subtitle"] or "") + (" (Best #%d)" % name[1]) name = song.info["title"] else: name = "Player's Best Unavailable" subtitle = "(You need to play more songs!)" elif name[0] == "WORST": song = recordkeys.get(records.worst(name[1], diff, game)) if song: subtitle = (song.info["subtitle"] or "") + (" (Worst #%d)" % name[1]) name = song.info["title"] else: name = "Player's Worst Unavailable" subtitle = "(You need to play more songs!)" elif name[0] == "LIKES": song = recordkeys.get(records.like(name[1], diff, game)) if song: subtitle = (song.info["subtitle"] or "") + (" (Likes #%d)" % name[1]) name = song.info["title"] else: name = "Player's Likes Unavailable" subtitle = "(You need to play more songs!)" elif name[0] == "DISLIKES": song = recordkeys.get(records.dislike(name[1], diff, game)) if song: subtitle = ((song.info["subtitle"] or "") + (" (Dislikes #%d)" % name[1])) name = song.info["title"] else: name = "Player's Dislikes Unavailable" subtitle = "(You need to play more songs!)" else: name, subtitle = util.find_subtitle(name.split("/")[-1]) if "." in diff: diff = "?" name = ("%d. " % i) + name self._songs.append([name, subtitle, diff]) i += 1 def render(self): if self.banner: return font = fontfx.WrapFont(28, 320) small_font = fontfx.WrapFont(18, 250) ls = font.get_linesize() y = ls * 3 for name, subtitle, diff in self._songs: y += font.lines(name, indent = " ") * ls y += font.lines(subtitle, indent = " ") * (ls / 4) self.image = pygame.Surface([340, y], SRCALPHA, 32) self.image.fill([0, 0, 0, 0]) y_off = 0 for i in range(len(self._songs)): t3 = None name, subtitle, diff = self._songs[i] t1 = font.render(name, color = colors.WHITE, indent = " ") if subtitle: t3 = small_font.render(subtitle, color = [190, 190, 190], indent = " ") if isinstance(diff, list): d = diff[0] else: d = diff t2 = fontfx.shadow(d[0], 28, DIFF_COLORS.get(d, colors.WHITE)) r1 = t1.get_rect() r2 = t2.get_rect() r1.top = r2.top = y_off r1.left = 0 r2.right = 340 y_off += t1.get_height() self.image.blit(t1, r1) self.image.blit(t2, r2) if t3: r3 = t3.get_rect() r3.left = 30 ls = small_font.get_linesize() r3.top = y_off - 3 * ls / 4 y_off += ls / 4 self.image.blit(t3, r3) if self.banner_fn and os.path.exists(self.banner_fn): self.banner, self.clip = load_banner(self.banner_fn, False) else: self.banner = CourseDisplay.no_banner class FolderDisplay(object): def __init__(self, name, type, count): self.name = name self.fullname = folder_name(name, type) self._type = type self.isfolder = True self.banner = None self.clip = None self.info = {} self.mixname = "%d songs" % count self.image = pygame.Surface([0, 0]) def render(self): if self.banner: return name = self.name.encode("ascii", "ignore") for path in [rc_path, pydance_path]: filename = os.path.join(path, "banners", self._type, name+".png") if os.path.exists(filename): self.banner, self.clip = load_banner(filename, False) break else: if self._type == "mix": for dir in mainconfig["coursedir"].split(os.pathsep): dir = os.path.expanduser(dir) fn = os.path.join(dir, self.name + ".png") if os.path.exists(fn): self.banner, self.clip = load_banner(fn, False) if self.banner == None: self.banner = CourseDisplay.no_banner class CourseSelector(InterfaceWindow): def __init__(self, songs, courses, screen, game): InterfaceWindow.__init__(self, screen, "courseselect-bg.png") recordkeys = dict([(k.info["recordkey"], k) for k in songs]) self._courses = [CourseDisplay(c, recordkeys, game) for c in courses] self._all_courses = self._courses self._index = 0 self._clock = pygame.time.Clock() self._game = game self._config = dict(game_config) self._configs = [] self._list = ListBox(pygame.font.Font(None, 28), [255, 255, 255], 32, 10, 256, [373, 150]) if len(self._courses) > 60 and mainconfig["folders"]: self._create_folders() self._create_folder_list() else: self._folders = None self._base_text = "All Courses" self._courses.sort(SORTS[SORT_NAMES[mainconfig["sortmode"] % NUM_SORTS]]) self._list.set_items([s.name for s in self._courses]) self._course = self._courses[self._index] self._course.render() players = games.GAMES[game].players for i in range(players): self._configs.append(dict(player_config)) ActiveIndicator([368, 306], height = 32, width = 266).add(self._sprites) HelpText(CS_HELP, [255, 255, 255], [0, 0, 0], pygame.font.Font(None, 20), [186, 20]).add(self._sprites) self._list_gfx = ScrollingImage(self._course.image, [15, 80], 390) self._coursetitle = TextDisplay(28, [345, 28], [20, 56]) self._title = TextDisplay(30, [240, 28], [377, 27]) self._banner = ImageDisplay(self._course.banner, [373, 56]) self._sprites.add([self._list, self._list_gfx, self._title, self._coursetitle, self._banner]) self._screen.blit(self._bg, [0, 0]) pygame.display.update() self.loop() music.fadeout(500) pygame.time.wait(500) # FIXME Does this belong in the menu code? Probably. music.load(os.path.join(sound_path, "menu.ogg")) music.set_volume(1.0) music.play(4, 0.0) player_config.update(self._configs[0]) # Save p1's settings def loop(self): pid, ev = ui.ui.poll() root_idx = 0 self._list.set_index(self._index) self._title.set_text(self._base_text + " - %d/%d" % (self._index + 1, len(self._courses))) while not (ev == ui.CANCEL and (not self._folders or self._course.isfolder)): # Inactive player. If the event isn't set to ui.PASS, we try to use # the pid later, which will be bad. if pid >= len(self._configs): ev = ui.PASS elif ev == ui.UP: self._index -= 1 elif ev == ui.DOWN: self._index += 1 elif ev == ui.PGUP: self._index -= 4 ev = ui.UP elif ev == ui.PGDN: self._index += 4 ev = ui.DOWN elif ev == ui.SELECT: if self._course.isfolder: self._course = random.choice(self._all_courses) root_idx = [fol.name for fol in self._courses].index(self._course.folder[SORT_NAMES[mainconfig["sortmode"]]]) fol = self._course.folder[SORT_NAMES[mainconfig["sortmode"]]] self._create_course_list(fol) self._index = self._courses.index(self._course) else: self._course = random.choice(self._courses) self._index = self._courses.index(self._course) elif ev == ui.SORT: s = self._courses[self._index] mainconfig["sortmode"] = (mainconfig["sortmode"] + 1) % NUM_SORTS if self._folders: if s.isfolder: self._create_folder_list() else: self._create_course_list(s.folder[SORT_NAMES[mainconfig["sortmode"]]]) self._index = self._courses.index(s) else: self._courses.sort(SORTS[SORT_NAMES[mainconfig["sortmode"]]]) self._index = self._courses.index(s) self._list.set_items([s.name for s in self._courses]) elif ev == ui.START: options.OptionScreen(self._configs, self._config, self._screen) self._screen.blit(self._bg, [0, 0]) self.update() pygame.display.update() elif ev == ui.CONFIRM: if self._course.isfolder: self._create_course_list(self._course.name) root_idx = self._index self._index = 0 else: music.fadeout(500) course = self._course.course course.setup(self._screen, self._configs, self._config, self._game) dance.play(self._screen, course, self._configs, self._config, self._game) course.done() music.fadeout(500) # The just-played song self._screen.blit(self._bg, [0, 0]) pygame.display.update() ui.ui.empty() ui.ui.clear() elif ev == ui.CANCEL: self._create_folder_list() self._index = root_idx elif ev == ui.FULLSCREEN: mainconfig["fullscreen"] ^= 1 pygame.display.toggle_fullscreen() self._index %= len(self._courses) self._course = self._courses[self._index] if ev in [ui.CANCEL, ui.UP, ui.DOWN, ui.SELECT, ui.CONFIRM, ui.SORT]: if ev == ui.UP: self._list.set_index(self._index, -1) elif ev == ui.DOWN: self._list.set_index(self._index, 1) else: self._list.set_index(self._index, 0) # don't animate self._course.render() self._coursetitle.set_text(self._course.name) self._banner.set_image(self._course.banner) self._list_gfx.set_image(self._course.image) self._title.set_text(self._base_text + " - %d/%d" % (self._index + 1, len(self._courses))) self.update() pid, ev = ui.ui.poll() def _create_folders(self): mixes = {} titles = {} for s in self._all_courses: if s.mixname not in mixes: mixes[s.mixname] = [] mixes[s.mixname].append(s) s.folder["mix"] = s.mixname label = s.name[0].capitalize() if label not in titles: titles[label] = [] titles[label].append(s) s.folder["title"] = label self._folders = { "mix": mixes, "title": titles } def _create_folder_list(self): sort = SORT_NAMES[mainconfig["sortmode"] % NUM_SORTS] lst = self._folders[sort].keys() lst.sort(lambda x, y: cmp(x.lower(), y.lower())) new_courses = [FolderDisplay(folder, sort, len(self._folders[sort][folder])) for folder in lst] self._courses = new_courses self._list.set_items([s.fullname for s in self._courses]) self._base_text = "Sort by %s" % sort.capitalize() def _create_course_list(self, folder): sort = SORT_NAMES[mainconfig["sortmode"] % NUM_SORTS] courselist = self._folders[sort][folder] courselist.sort(SORTS[sort]) self._courses = courselist self._list.set_items([s.name for s in self._courses]) self._base_text = folder_name(folder, sort)