def __init__(self, engine, songName=None, libraryName=DEFAULT_LIBRARY): self.engine = engine self.time = 0.0 self.guitar = Guitar(self.engine, editorMode=True) self.controls = Player.Controls() self.camera = Camera() self.pos = 0.0 self.snapPos = 0.0 self.scrollPos = 0.0 self.scrollSpeed = 0.0 self.newNotes = None self.newNotePos = 0.0 self.song = None self.engine.loadSvgDrawing(self, "background", "editor.svg") self.modified = False self.songName = songName self.libraryName = libraryName self.heldFrets = set() self.spinnyDisabled = self.engine.config.get("game", "disable_spinny") mainMenu = [ (_("Save Song"), self.save), (_("Set Song Name"), self.setSongName), (_("Set Artist Name"), self.setArtistName), (_("Set Beats per Minute"), self.setBpm), (_("Estimate Beats per Minute"), self.estimateBpm), (_("Set A/V delay"), self.setAVDelay), (_("Set Cassette Color"), self.setCassetteColor), (_("Set Cassette Label"), self.setCassetteLabel), (_("Editing Help"), self.help), (_("Quit to Main Menu"), self.quit), ] self.menu = Menu(self.engine, mainMenu)
class Inventory(object): def __init__(self): self._guitars = staticList1 def addGuitar(self, serialNumber, price, spec): self.guitar = Guitar(serialNumber, price, spec) self._guitars.append(self.guitar) def getGuitar(self, serialNumber): index = 0 for i in self._guitars: self.guitar = self._guitars[index] if self.guitar.getSerialNumber() == serialNumber: return self.guitar index += 1 return None def search(self, searchSpec): self.matchingGuitars = staticList2 index = 0 for i in self._guitars: self.guitar = self._guitars[index] if self.guitar.GetSpec().matches(searchSpec): self.matchingGuitars.append(self.guitar) index += 1 return self.matchingGuitars
def __init__(self, engine, songName = None, libraryName = DEFAULT_LIBRARY): self.engine = engine self.time = 0.0 self.guitar = Guitar(self.engine, editorMode = True) self.controls = Player.Controls() self.camera = Camera() self.pos = 0.0 self.snapPos = 0.0 self.scrollPos = 0.0 self.scrollSpeed = 0.0 self.newNotes = None self.newNotePos = 0.0 self.song = None self.engine.loadSvgDrawing(self, "background", "editor.svg") self.modified = False self.songName = songName self.libraryName = libraryName self.heldFrets = set() mainMenu = [ (_("Save Song"), self.save), (_("Set Song Name"), self.setSongName), (_("Set Artist Name"), self.setArtistName), (_("Set Beats per Minute"), self.setBpm), (_("Estimate Beats per Minute"), self.estimateBpm), (_("Set A/V delay"), self.setAVDelay), (_("Set Cassette Color"), self.setCassetteColor), (_("Set Cassette Label"), self.setCassetteLabel), (_("Editing Help"), self.help), (_("Quit to Main Menu"), self.quit), ] self.menu = Menu(self.engine, mainMenu)
def establish_controller(index, logger, light_sender, gui): try: controller = Xbox360Controller(index=index) logger.info(controller.name) except Exception: logger.error( f'Encountered exception initiating controller at index {index}', exc_info=True) return if 'Drum' in controller.name: light_sender.add_controller( 'drums', Drums(controller, logger, 'drums', (0, 0, 0), light_sender, gui), 1) elif 'WingMan' in controller.name: controller.axis_threshold = 0.05 light_sender.add_controller( 'car', Car(controller, logger, 'car', (0, 0, 0), light_sender, gui), 2) elif 'Guitar' in controller.name: light_sender.add_controller( 'guitar1', Guitar(controller, logger, 'guitar1', (0, 0, 0), light_sender, gui), 3) else: logger.error( f'Unrecognized controller found with name {controller.name}. Skipping it.' ) controller.close()
def createClient(self, libraryName, songName): self.guitar = Guitar(self.engine) self.visibility = 0.0 self.libraryName = libraryName self.songName = songName self.done = False self.sfxChannel = self.engine.audio.getChannel(self.engine.audio.getChannelCount() - 1) self.lastMultTime = None self.cheatCodes = [ ([117, 112, 116, 111, 109, 121, 116, 101, 109, 112, 111], self.toggleAutoPlay), ([102, 97, 115, 116, 102, 111, 114, 119, 97, 114, 100], self.goToResults) ] self.enteredCode = [] self.song = None self.autoPlay = False self.lastPickPos = None self.lastSongPos = 0.0 self.keyBurstTimeout = None self.keyBurstPeriod = 30 self.camera.target = (0, 0, 4) self.camera.origin = (0, 3, -3) self.loadSettings() self.engine.resource.load(self, "song", lambda: loadSong(self.engine, songName, library = libraryName), onLoad = self.songLoaded) self.stage = Stage.Stage(self, self.engine.resource.fileName("stage.ini")) self.engine.loadSvgDrawing(self, "fx2x", "2x.svg", textureSize = (256, 256)) self.engine.loadSvgDrawing(self, "fx3x", "3x.svg", textureSize = (256, 256)) self.engine.loadSvgDrawing(self, "fx4x", "4x.svg", textureSize = (256, 256)) Dialogs.showLoadingScreen(self.engine, lambda: self.song, text = _("Tuning Guitar...")) settingsMenu = Settings.GameSettingsMenu(self.engine) settingsMenu.fadeScreen = True self.menu = Menu(self.engine, [ (_("Return to Song"), lambda: None), (_("Restart Song"), self.restartSong), (_("Change Song"), self.changeSong), (_("Settings"), settingsMenu), (_("Quit to Main Menu"), self.quit), ], fadeScreen = True, onClose = self.resumeGame) self.restartSong()
def main(): # Set up Rick's guitar inventory inventory = Inventory() initialize_inventory(inventory) what_erin_likes = Guitar("", 0, "fender", "Stratocastor", "electric", "Alder", "Alder") guitar = inventory.search(what_erin_likes) if guitar != None: print("Erin you might like this", guitar.builder, guitar.model, guitar.type, "guitar:\n", guitar.back_wood, "back and sides,\n", guitar.top_wood, "top.\n You can have it for only $", guitar.price, "!") else: print("Sorry, Erin, we have nothing for you.")
def __init__(self, engine, libraryName, songName): super(GuitarScene, self).__init__(engine) self.guitar = Guitar(self.engine) self.visibility = 0.0 self.libraryName = libraryName self.songName = songName self.done = False self.sfxChannel = self.engine.audio.getChannel(self.engine.audio.getChannelCount() - 1) self.lastMultTime = None self.cheatCodes = [ ([117, 112, 116, 111, 109, 121, 116, 101, 109, 112, 111], self.toggleAutoPlay), ([102, 97, 115, 116, 102, 111, 114, 119, 97, 114, 100], self.goToResults) ] self.enteredCode = [] self.song = None self.autoPlay = False self.lastPickPos = None self.lastSongPos = 0.0 self.keyBurstTimeout = None self.keyBurstPeriod = 30 self.camera.target = (0, 0, 4) self.camera.origin = (0, 3, -3) self.loadSettings() self.engine.resource.load(self, "song", lambda: loadSong(self.engine, songName, library = libraryName), onLoad = self.songLoaded) self.stage = Stage.Stage(self, self.engine.resource.fileName("stage.ini")) self.engine.loadImgDrawing(self, "fx2x", "2x.png", textureSize = (256, 256)) self.engine.loadImgDrawing(self, "fx3x", "3x.png", textureSize = (256, 256)) self.engine.loadImgDrawing(self, "fx4x", "4x.png", textureSize = (256, 256)) Dialogs.showLoadingScreen(self.engine, lambda: self.song, text = _("Tuning Guitar...")) settingsMenu = Settings.GameSettingsMenu(self.engine) settingsMenu.fadeScreen = True self.menu = Menu(self.engine, [ (_("Return to Song"), self.resumeHideMenu), (_("Restart Song"), self.restartSong), (_("Change Song"), self.changeSong), (_("Settings"), settingsMenu), (_("Quit to Main Menu"), self.quit), ], fadeScreen = True, onClose = self.resumeGame) self.restartSong()
class GuitarSceneClient(GuitarScene, SceneClient): def createClient(self, libraryName, songName): self.guitar = Guitar(self.engine) self.visibility = 0.0 self.libraryName = libraryName self.songName = songName self.done = False self.sfxChannel = self.engine.audio.getChannel(self.engine.audio.getChannelCount() - 1) self.lastMultTime = None self.cheatCodes = [ ([117, 112, 116, 111, 109, 121, 116, 101, 109, 112, 111], self.toggleAutoPlay), ([102, 97, 115, 116, 102, 111, 114, 119, 97, 114, 100], self.goToResults) ] self.enteredCode = [] self.song = None self.autoPlay = False self.lastPickPos = None self.lastSongPos = 0.0 self.keyBurstTimeout = None self.keyBurstPeriod = 30 self.camera.target = (0, 0, 4) self.camera.origin = (0, 3, -3) self.loadSettings() self.engine.resource.load(self, "song", lambda: loadSong(self.engine, songName, library = libraryName), onLoad = self.songLoaded) self.stage = Stage.Stage(self, self.engine.resource.fileName("stage.ini")) self.engine.loadImgDrawing(self, "fx2x", "2x.png", textureSize = (256, 256)) self.engine.loadImgDrawing(self, "fx3x", "3x.png", textureSize = (256, 256)) self.engine.loadImgDrawing(self, "fx4x", "4x.png", textureSize = (256, 256)) Dialogs.showLoadingScreen(self.engine, lambda: self.song, text = _("Tuning Guitar...")) settingsMenu = Settings.GameSettingsMenu(self.engine) settingsMenu.fadeScreen = True self.menu = Menu(self.engine, [ (_("Return to Song"), lambda: None), (_("Restart Song"), self.restartSong), (_("Change Song"), self.changeSong), (_("Settings"), settingsMenu), (_("Quit to Main Menu"), self.quit), ], fadeScreen = True, onClose = self.resumeGame) self.restartSong() def pauseGame(self): if self.song: self.song.pause() def resumeGame(self): self.loadSettings() if self.song: self.song.unpause() def loadSettings(self): self.delay = self.engine.config.get("audio", "delay") self.screwUpVolume = self.engine.config.get("audio", "screwupvol") self.guitarVolume = self.engine.config.get("audio", "guitarvol") self.songVolume = self.engine.config.get("audio", "songvol") self.rhythmVolume = self.engine.config.get("audio", "rhythmvol") self.guitar.leftyMode = self.engine.config.get("game", "leftymode") if self.song: self.song.setBackgroundVolume(self.songVolume) self.song.setRhythmVolume(self.rhythmVolume) def songLoaded(self, song): song.difficulty = self.player.difficulty self.delay += song.info.delay # If tapping is disabled, remove the tapping indicators if not self.engine.config.get("game", "tapping"): for time, event in self.song.track.getAllEvents(): if isinstance(event, Note): event.tappable = False def quit(self): if self.song: self.song.stop() self.song = None self.done = True self.engine.view.popLayer(self.menu) self.session.world.finishGame() def changeSong(self): if self.song: self.song.stop() self.song = None self.engine.view.popLayer(self.menu) self.session.world.deleteScene(self) self.session.world.createScene("SongChoosingScene") def restartSong(self): self.engine.data.startSound.play() self.engine.view.popLayer(self.menu) self.player.reset() self.stage.reset() self.enteredCode = [] self.autoPlay = False self.engine.collectGarbage() if not self.song: return self.countdown = 8.0 self.guitar.endPick(0) self.song.stop() def run(self, ticks): SceneClient.run(self, ticks) pos = self.getSongPosition() # update song if self.song: # update stage self.stage.run(pos, self.guitar.currentPeriod) if self.countdown <= 0 and not self.song.isPlaying() and not self.done: self.goToResults() return if self.autoPlay: notes = self.guitar.getRequiredNotes(self.song, pos) notes = [note.number for time, note in notes] changed = False held = 0 for n, k in enumerate(KEYS): if n in notes and not self.controls.getState(k): changed = True self.controls.toggle(k, True) elif not n in notes and self.controls.getState(k): changed = True self.controls.toggle(k, False) if self.controls.getState(k): held += 1 if changed and held: self.doPick() self.song.update(ticks) if self.countdown > 0: self.guitar.setBPM(self.song.bpm) self.countdown = max(self.countdown - ticks / self.song.period, 0) if not self.countdown: self.engine.collectGarbage() self.song.setGuitarVolume(self.guitarVolume) self.song.setBackgroundVolume(self.songVolume) self.song.setRhythmVolume(self.rhythmVolume) self.song.play() # update board if not self.guitar.run(ticks, pos, self.controls): # done playing the current notes self.endPick() # missed some notes? if self.guitar.getMissedNotes(self.song, pos) and not self.guitar.playedNotes: self.song.setGuitarVolume(0.0) self.player.streak = 0 # late pick if self.keyBurstTimeout is not None and self.engine.timer.time > self.keyBurstTimeout: self.keyBurstTimeout = None notes = self.guitar.getRequiredNotes(self.song, pos) if self.guitar.controlsMatchNotes(self.controls, notes): self.doPick() def endPick(self): score = self.getExtraScoreForCurrentlyPlayedNotes() if not self.guitar.endPick(self.song.getPosition()): self.song.setGuitarVolume(0.0) self.player.addScore(score) def render3D(self): self.stage.render(self.visibility) def renderGuitar(self): self.guitar.render(self.visibility, self.song, self.getSongPosition(), self.controls) def getSongPosition(self): if self.song: if not self.done: self.lastSongPos = self.song.getPosition() return self.lastSongPos - self.countdown * self.song.period - self.delay else: # Nice speeding up animation at the end of the song return self.lastSongPos + 4.0 * (1 - self.visibility) * self.song.period - self.delay return 0.0 def doPick(self): if not self.song: return pos = self.getSongPosition() if self.guitar.playedNotes: # If all the played notes are tappable, there are no required notes and # the last note was played recently enough, ignore this pick if self.guitar.areNotesTappable(self.guitar.playedNotes) and \ not self.guitar.getRequiredNotes(self.song, pos) and \ pos - self.lastPickPos <= self.song.period / 2: return self.endPick() self.lastPickPos = pos if self.guitar.startPick(self.song, pos, self.controls): self.song.setGuitarVolume(self.guitarVolume) self.player.streak += 1 self.player.notesHit += len(self.guitar.playedNotes) self.player.addScore(len(self.guitar.playedNotes) * 50) self.stage.triggerPick(pos, [n[1].number for n in self.guitar.playedNotes]) if self.player.streak % 10 == 0: self.lastMultTime = pos else: self.song.setGuitarVolume(0.0) self.player.streak = 0 self.stage.triggerMiss(pos) self.sfxChannel.play(self.engine.data.screwUpSound) self.sfxChannel.setVolume(self.screwUpVolume) def toggleAutoPlay(self): self.autoPlay = not self.autoPlay if self.autoPlay: Dialogs.showMessage(self.engine, _("Jurgen will show you how it is done.")) else: Dialogs.showMessage(self.engine, _("Jurgen has left the building.")) return self.autoPlay def goToResults(self): if self.song: self.song.stop() self.song = None self.done = True self.session.world.deleteScene(self) self.session.world.createScene("GameResultsScene", libraryName = self.libraryName, songName = self.songName) def keyPressed(self, key, unicode): control = self.controls.keyPressed(key) if control in (Player.ACTION1, Player.ACTION2): for k in KEYS: if self.controls.getState(k): self.keyBurstTimeout = None break else: self.keyBurstTimeout = self.engine.timer.time + self.keyBurstPeriod return True if control in (Player.ACTION1, Player.ACTION2) and self.song: self.doPick() elif control in KEYS and self.song: # Check whether we can tap the currently required notes pos = self.getSongPosition() notes = self.guitar.getRequiredNotes(self.song, pos) if self.player.streak > 0 and \ self.guitar.areNotesTappable(notes) and \ self.guitar.controlsMatchNotes(self.controls, notes): self.doPick() elif control == Player.CANCEL: self.pauseGame() self.engine.view.pushLayer(self.menu) return True elif key >= ord('a') and key <= ord('z'): # cheat codes n = len(self.enteredCode) for code, func in self.cheatCodes: if n < len(code): if key == code[n]: self.enteredCode.append(key) if self.enteredCode == code: self.enteredCode = [] self.player.cheating = True func() break else: self.enteredCode = [] def getExtraScoreForCurrentlyPlayedNotes(self): if not self.song: return 0 noteCount = len(self.guitar.playedNotes) pickLength = self.guitar.getPickLength(self.getSongPosition()) if pickLength > 1.1 * self.song.period / 4: return int(.1 * pickLength * noteCount) return 0 def keyReleased(self, key): if self.controls.keyReleased(key) in KEYS and self.song: # Check whether we can tap the currently required notes pos = self.getSongPosition() notes = self.guitar.getRequiredNotes(self.song, pos) if self.player.streak > 0 and \ self.guitar.areNotesTappable(notes) and \ self.guitar.controlsMatchNotes(self.controls, notes): self.doPick() # Otherwise we end the pick if the notes have been playing long enough elif self.lastPickPos is not None and pos - self.lastPickPos > self.song.period / 2: self.endPick() def render(self, visibility, topMost): SceneClient.render(self, visibility, topMost) font = self.engine.data.font bigFont = self.engine.data.bigFont self.visibility = v = 1.0 - ((1 - visibility) ** 2) self.engine.view.setOrthogonalProjection(normalize = True) try: # show countdown if self.countdown > 1: Theme.setBaseColor(min(1.0, 3.0 - abs(4.0 - self.countdown))) text = _("Get Ready to Rock") w, h = font.getStringSize(text) font.render(text, (.5 - w / 2, .3)) if self.countdown < 6: scale = 0.002 + 0.0005 * (self.countdown % 1) ** 3 text = "%d" % (self.countdown) w, h = bigFont.getStringSize(text, scale = scale) Theme.setSelectedColor() bigFont.render(text, (.5 - w / 2, .45 - h / 2), scale = scale) w, h = font.getStringSize(" ") y = .05 - h / 2 - (1.0 - v) * .2 # show song name if self.countdown and self.song: Theme.setBaseColor(min(1.0, 4.0 - abs(4.0 - self.countdown))) Dialogs.wrapText(font, (.05, .05 - h / 2), self.song.info.name + " \n " + self.song.info.artist, rightMargin = .6, scale = 0.0015) Theme.setSelectedColor() font.render("%d" % (self.player.score + self.getExtraScoreForCurrentlyPlayedNotes()), (.6, y)) font.render("%dx" % self.player.getScoreMultiplier(), (.6, y + h)) # show the streak counter and miss message if self.player.streak > 0 and self.song: text = _("%d hit") % self.player.streak factor = 0.0 if self.lastPickPos: diff = self.getSongPosition() - self.lastPickPos if diff > 0 and diff < self.song.period * 2: factor = .25 * (1.0 - (diff / (self.song.period * 2))) ** 2 factor = (1.0 + factor) * 0.002 tw, th = font.getStringSize(text, scale = factor) font.render(text, (.16 - tw / 2, y + h / 2 - th / 2), scale = factor) elif self.lastPickPos is not None and self.countdown <= 0: diff = self.getSongPosition() - self.lastPickPos alpha = 1.0 - diff * 0.005 if alpha > .1: Theme.setSelectedColor(alpha) glPushMatrix() glTranslate(.1, y + 0.000005 * diff ** 2, 0) glRotatef(math.sin(self.lastPickPos) * 25, 0, 0, 1) font.render(_("Missed!"), (0, 0)) glPopMatrix() # show the streak balls if self.player.streak >= 30: glColor3f(.5, .5, 1) elif self.player.streak >= 20: glColor3f(1, 1, .5) elif self.player.streak >= 10: glColor3f(1, .5, .5) else: glColor3f(.5, 1, .5) s = min(39, self.player.streak) % 10 + 1 font.render(Data.BALL2 * s + Data.BALL1 * (10 - s), (.67, y + h * 1.3), scale = 0.0011) # show multiplier changes if self.song and self.lastMultTime is not None: diff = self.getSongPosition() - self.lastMultTime if diff > 0 and diff < self.song.period * 2: m = self.player.getScoreMultiplier() c = (1, 1, 1) if self.player.streak >= 40: texture = None elif m == 1: texture = None elif m == 2: texture = self.fx2x.texture c = (1, .5, .5) elif m == 3: texture = self.fx3x.texture c = (1, 1, .5) elif m == 4: texture = self.fx4x.texture c = (.5, .5, 1) f = (1.0 - abs(self.song.period * 1 - diff) / (self.song.period * 1)) ** 2 # Flash the screen glBegin(GL_TRIANGLE_STRIP) glColor4f(c[0], c[1], c[2], (f - .5) * 1) glVertex2f(0, 0) glColor4f(c[0], c[1], c[2], (f - .5) * 1) glVertex2f(1, 0) glColor4f(c[0], c[1], c[2], (f - .5) * .25) glVertex2f(0, 1) glColor4f(c[0], c[1], c[2], (f - .5) * .25) glVertex2f(1, 1) glEnd() if texture: glPushMatrix() glEnable(GL_TEXTURE_2D) texture.bind() size = (texture.pixelSize[0] * .002, texture.pixelSize[1] * .002) glTranslatef(.5, .15, 0) glBlendFunc(GL_SRC_ALPHA, GL_ONE) f = .5 + .5 * (diff / self.song.period) ** 3 glColor4f(1, 1, 1, min(1, 2 - f)) glBegin(GL_TRIANGLE_STRIP) glTexCoord2f(0.0, 0.0) glVertex2f(-size[0] * f, -size[1] * f) glTexCoord2f(1.0, 0.0) glVertex2f( size[0] * f, -size[1] * f) glTexCoord2f(0.0, 1.0) glVertex2f(-size[0] * f, size[1] * f) glTexCoord2f(1.0, 1.0) glVertex2f( size[0] * f, size[1] * f) glEnd() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glPopMatrix() # show the comments if self.song and self.song.info.tutorial: glColor3f(1, 1, 1) pos = self.getSongPosition() for time, event in self.song.track.getEvents(pos - self.song.period * 2, pos + self.song.period * 4): if isinstance(event, PictureEvent): if pos < time or pos > time + event.length: continue try: picture = event.picture except: self.engine.loadImgDrawing(event, "picture", os.path.join(self.libraryName, self.songName, event.fileName)) picture = event.picture w, h, = self.engine.view.geometry[2:4] fadePeriod = 500.0 f = (1.0 - min(1.0, abs(pos - time) / fadePeriod) * min(1.0, abs(pos - time - event.length) / fadePeriod)) ** 2 picture.transform.reset() picture.transform.translate(w / 2, (f * -2 + 1) * h / 2) picture.transform.scale(1, -1) picture.draw() elif isinstance(event, TextEvent): if pos >= time and pos <= time + event.length: text = _(event.text) w, h = font.getStringSize(text) font.render(text, (.5 - w / 2, .67)) finally: self.engine.view.resetProjection()
class Editor(Layer, KeyListener): """Song editor layer.""" def __init__(self, engine, songName = None, libraryName = DEFAULT_LIBRARY): self.engine = engine self.time = 0.0 self.guitar = Guitar(self.engine, editorMode = True) self.controls = Player.Controls() self.camera = Camera() self.pos = 0.0 self.snapPos = 0.0 self.scrollPos = 0.0 self.scrollSpeed = 0.0 self.newNotes = None self.newNotePos = 0.0 self.song = None self.engine.loadSvgDrawing(self, "background", "editor.svg") self.modified = False self.songName = songName self.libraryName = libraryName self.heldFrets = set() mainMenu = [ (_("Save Song"), self.save), (_("Set Song Name"), self.setSongName), (_("Set Artist Name"), self.setArtistName), (_("Set Beats per Minute"), self.setBpm), (_("Estimate Beats per Minute"), self.estimateBpm), (_("Set A/V delay"), self.setAVDelay), (_("Set Cassette Color"), self.setCassetteColor), (_("Set Cassette Label"), self.setCassetteLabel), (_("Editing Help"), self.help), (_("Quit to Main Menu"), self.quit), ] self.menu = Menu(self.engine, mainMenu) def save(self): if not self.modified: Dialogs.showMessage(self.engine, _("There are no changes to save.")) return def save(): self.song.save() self.modified = False self.engine.resource.load(function = save) Dialogs.showLoadingScreen(self.engine, lambda: not self.modified, text = _("Saving...")) Dialogs.showMessage(self.engine, _("'%s' saved.") % self.song.info.name) def help(self): Dialogs.showMessage(self.engine, _("Editing keys: ") + _("Arrows - Move cursor, ") + _("Space - Play/pause song, ") + _("Enter - Make note (hold and move for long notes), ") + _("Delete - Delete note, ") + _("Page Up/Down - Change difficulty")) def setSongName(self): name = Dialogs.getText(self.engine, _("Enter Song Name"), self.song.info.name) if name: self.song.info.name = name self.modified = True def setArtistName(self): name = Dialogs.getText(self.engine, _("Enter Artist Name"), self.song.info.artist) if name: self.song.info.artist = name self.modified = True def setAVDelay(self): delay = Dialogs.getText(self.engine, _("Enter A/V delay in milliseconds"), unicode(self.song.info.delay)) if delay: try: self.song.info.delay = int(delay) self.modified = True except ValueError: Dialogs.showMessage(self.engine, _("That isn't a number.")) def setBpm(self): bpm = Dialogs.getText(self.engine, _("Enter Beats per Minute Value"), unicode(self.song.bpm)) if bpm: try: self.song.setBpm(float(bpm)) self.modified = True except ValueError: Dialogs.showMessage(self.engine, _("That isn't a number.")) def estimateBpm(self): bpm = Dialogs.estimateBpm(self.engine, self.song, _("Tap the Space bar to the beat of the song. Press Enter when done or Escape to cancel.")) if bpm is not None: self.song.setBpm(bpm) self.modified = True def setCassetteColor(self): if self.song.info.cassetteColor: color = Theme.colorToHex(self.song.info.cassetteColor) else: color = "" color = Dialogs.getText(self.engine, _("Enter cassette color in HTML (#RRGGBB) format."), color) if color: try: self.song.info.setCassetteColor(Theme.hexToColor(color)) self.modified = True except ValueError: Dialogs.showMessage(self.engine, _("That isn't a color.")) def setCassetteLabel(self): label = Dialogs.chooseFile(self.engine, masks = ["*.png"], prompt = _("Choose a 256x128 PNG format label image.")) if label: songPath = self.engine.resource.fileName("songs", self.songName, writable = True) shutil.copyfile(label, os.path.join(songPath, "label.png")) self.modified = True def shown(self): self.engine.input.addKeyListener(self) if not self.songName: self.libraryName, self.songName = Dialogs.chooseSong(self.engine) if not self.songName: self.engine.view.popLayer(self) return self.engine.resource.load(self, "song", lambda: loadSong(self.engine, self.songName, seekable = True, library = self.libraryName)) Dialogs.showLoadingScreen(self.engine, lambda: self.song, text = _("Loading song...")) def hidden(self): if self.song: self.song.stop() self.engine.input.removeKeyListener(self) self.engine.view.pushLayer(MainMenu.MainMenu(self.engine)) def controlPressed(self, control): if not self.song: return if control == Player.UP: self.guitar.selectPreviousString() elif control == Player.DOWN: self.guitar.selectNextString() elif control == Player.LEFT: self.pos = self.snapPos - self.song.period / 4 elif control == Player.RIGHT: self.pos = self.snapPos + self.song.period / 4 elif control in KEYS: self.heldFrets.add(KEYS.index(control)) elif control in [Player.ACTION1, Player.ACTION2]: self.newNotePos = self.snapPos # Add notes for the frets that are held down or for the selected string. if self.heldFrets: self.newNotes = [Note(f, self.song.period / 4) for f in self.heldFrets] else: self.newNotes = [Note(self.guitar.selectedString, self.song.period / 4)] self.modified = True def controlReleased(self, control): if not self.song: return if control in [Player.ACTION1, Player.ACTION2] and self.newNotes and not self.heldFrets: self.newNotes = [] elif control in KEYS: self.heldFrets.remove(KEYS.index(control)) if not self.heldFrets and self.newNotes: self.newNotes = [] def quit(self): self.engine.view.popLayer(self) self.engine.view.popLayer(self.menu) def keyPressed(self, key, unicode): c = self.engine.input.controls.getMapping(key) if c == Player.CANCEL: self.engine.view.pushLayer(self.menu) elif key == pygame.K_PAGEDOWN and self.song: d = self.song.difficulty v = difficulties.values() self.song.difficulty = v[(v.index(d) + 1) % len(v)] elif key == pygame.K_PAGEUP and self.song: d = self.song.difficulty v = difficulties.values() self.song.difficulty = v[(v.index(d) - 1) % len(v)] elif key == pygame.K_DELETE and self.song: # gather up all events that intersect the cursor and delete the ones on the selected string t1 = self.snapPos t2 = self.snapPos + self.song.period / 4 e = [(time, event) for time, event in self.song.track.getEvents(t1, t2) if isinstance(event, Note)] for time, event in e: if event.number == self.guitar.selectedString: self.song.track.removeEvent(time, event) self.modified = True elif key == pygame.K_SPACE and self.song: if self.song.isPlaying(): self.song.stop() else: self.song.play(start = self.pos) c = self.controls.keyPressed(key) if c: self.controlPressed(c) return True def keyReleased(self, key): c = self.controls.keyReleased(key) if c: self.controlReleased(c) return True def run(self, ticks): self.time += ticks / 50.0 if not self.song: return self.guitar.run(ticks, self.scrollPos, self.controls) if not self.song.isPlaying(): if self.controls.getState(Player.RIGHT) and not self.controls.getState(Player.LEFT): self.pos += self.song.period * self.scrollSpeed self.scrollSpeed += ticks / 4096.0 elif self.controls.getState(Player.LEFT) and not self.controls.getState(Player.RIGHT): self.pos -= self.song.period * self.scrollSpeed self.scrollSpeed += ticks / 4096.0 else: self.scrollSpeed = 0 else: self.pos = self.song.getPosition() - self.song.info.delay self.pos = max(0, self.pos) quarterBeat = int(self.pos / (self.song.period / 4) + .5) self.snapPos = quarterBeat * (self.song.period / 4) # note adding if self.newNotes: if self.snapPos < self.newNotePos: self.newNotes = [] for note in self.newNotes: self.song.track.removeEvent(self.newNotePos, note) note.length = max(self.song.period / 4, self.snapPos - self.newNotePos) # remove all notes under the this new note oldNotes = [(time, event) for time, event in self.song.track.getEvents(self.newNotePos, self.newNotePos + note.length) if isinstance(event, Note)] for time, event in oldNotes: if event.number == note.number: self.song.track.removeEvent(time, event) if time < self.newNotePos: event.length = self.newNotePos - time self.song.track.addEvent(time, event) self.song.track.addEvent(self.newNotePos, note) if self.song.isPlaying(): self.scrollPos = self.pos else: self.scrollPos = (self.scrollPos + self.snapPos) / 2.0 def render(self, visibility, topMost): if not self.song: return v = 1.0 - ((1 - visibility) ** 2) # render the background t = self.time / 100 + 34 w, h, = self.engine.view.geometry[2:4] r = .5 self.background.transform.reset() self.background.transform.translate(w / 2 + math.sin(t / 2) * w / 2 * r, h / 2 + math.cos(t) * h / 2 * r) self.background.transform.rotate(-t) self.background.transform.scale(math.sin(t / 8) + 2, math.sin(t / 8) + 2) self.background.draw() self.camera.target = ( 2, 0, 5.5) self.camera.origin = (-2, 9, 5.5) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(60, 4.0 / 3.0, 0.1, 1000) glMatrixMode(GL_MODELVIEW) glLoadIdentity() self.camera.apply() self.guitar.render(v, self.song, self.scrollPos, self.controls) self.engine.view.setOrthogonalProjection(normalize = True) font = self.engine.data.font try: Theme.setSelectedColor() w, h = font.getStringSize(" ") if self.song.isPlaying(): status = _("Playing") else: status = _("Stopped") t = "%d.%02d'%03d" % (self.pos / 60000, (self.pos % 60000) / 1000, self.pos % 1000) font.render(t, (.05, .05 - h / 2)) font.render(status, (.05, .05 + h / 2)) font.render(unicode(self.song.difficulty), (.05, .05 + 3 * h / 2)) Theme.setBaseColor() text = self.song.info.name + (self.modified and "*" or "") Dialogs.wrapText(font, (.5, .05 - h / 2), text) finally: self.engine.view.resetProjection()
class Editor(Layer, KeyListener): """Song editor layer.""" def __init__(self, engine, songName=None, libraryName=DEFAULT_LIBRARY): self.engine = engine self.time = 0.0 self.guitar = Guitar(self.engine, editorMode=True) self.controls = Player.Controls() self.camera = Camera() self.pos = 0.0 self.snapPos = 0.0 self.scrollPos = 0.0 self.scrollSpeed = 0.0 self.newNotes = None self.newNotePos = 0.0 self.song = None self.engine.loadSvgDrawing(self, "background", "editor.svg") self.modified = False self.songName = songName self.libraryName = libraryName self.heldFrets = set() self.spinnyDisabled = self.engine.config.get("game", "disable_spinny") mainMenu = [ (_("Save Song"), self.save), (_("Set Song Name"), self.setSongName), (_("Set Artist Name"), self.setArtistName), (_("Set Beats per Minute"), self.setBpm), (_("Estimate Beats per Minute"), self.estimateBpm), (_("Set A/V delay"), self.setAVDelay), (_("Set Cassette Color"), self.setCassetteColor), (_("Set Cassette Label"), self.setCassetteLabel), (_("Editing Help"), self.help), (_("Quit to Main Menu"), self.quit), ] self.menu = Menu(self.engine, mainMenu) def save(self): if not self.modified: Dialogs.showMessage(self.engine, _("There are no changes to save.")) return def save(): self.song.save() self.modified = False self.engine.resource.load(function=save) Dialogs.showLoadingScreen(self.engine, lambda: not self.modified, text=_("Saving...")) Dialogs.showMessage(self.engine, _("'%s' saved.") % self.song.info.name) def help(self): Dialogs.showMessage( self.engine, _("Editing keys: ") + _("Arrows - Move cursor, ") + _("Space - Play/pause song, ") + _("Enter - Make note (hold and move for long notes), ") + _("Delete - Delete note, ") + _("Page Up/Down - Change difficulty")) def setSongName(self): name = Dialogs.getText(self.engine, _("Enter Song Name"), self.song.info.name) if name: self.song.info.name = name self.modified = True def setArtistName(self): name = Dialogs.getText(self.engine, _("Enter Artist Name"), self.song.info.artist) if name: self.song.info.artist = name self.modified = True def setAVDelay(self): delay = Dialogs.getText(self.engine, _("Enter A/V delay in milliseconds"), unicode(self.song.info.delay)) if delay: try: self.song.info.delay = int(delay) self.modified = True except ValueError: Dialogs.showMessage(self.engine, _("That isn't a number.")) def setBpm(self): bpm = Dialogs.getText(self.engine, _("Enter Beats per Minute Value"), unicode(self.song.bpm)) if bpm: try: self.song.setBpm(float(bpm)) self.modified = True except ValueError: Dialogs.showMessage(self.engine, _("That isn't a number.")) def estimateBpm(self): bpm = Dialogs.estimateBpm( self.engine, self.song, _("Tap the Space bar to the beat of the song. Press Enter when done or Escape to cancel." )) if bpm is not None: self.song.setBpm(bpm) self.modified = True def setCassetteColor(self): if self.song.info.cassetteColor: color = Theme.colorToHex(self.song.info.cassetteColor) else: color = "" color = Dialogs.getText( self.engine, _("Enter cassette color in HTML (#RRGGBB) format."), color) if color: try: self.song.info.setCassetteColor(Theme.hexToColor(color)) self.modified = True except ValueError: Dialogs.showMessage(self.engine, _("That isn't a color.")) def setCassetteLabel(self): label = Dialogs.chooseFile( self.engine, masks=["*.png"], prompt=_("Choose a 256x128 PNG format label image.")) if label: songPath = self.engine.resource.fileName("songs", self.songName, writable=True) shutil.copyfile(label, os.path.join(songPath, "label.png")) self.modified = True def shown(self): self.engine.input.addKeyListener(self) if not self.songName: self.libraryName, self.songName = Dialogs.chooseSong(self.engine) if not self.songName: self.engine.view.popLayer(self) return self.engine.resource.load( self, "song", lambda: loadSong(self.engine, self.songName, seekable=True, library=self.libraryName)) Dialogs.showLoadingScreen(self.engine, lambda: self.song, text=_("Loading song...")) def hidden(self): if self.song: self.song.stop() self.engine.input.removeKeyListener(self) #self.engine.view.pushLayer(MainMenu.MainMenu(self.engine)) self.engine.view.pushLayer( self.engine.mainMenu ) #rchiav: use already-existing MainMenu instance def controlPressed(self, control): if not self.song: return if control in Player.UPS: self.guitar.selectPreviousString() elif control in Player.DOWNS: self.guitar.selectNextString() elif control in Player.LEFTS: self.pos = self.snapPos - self.song.period / 4 elif control in Player.RIGHTS: self.pos = self.snapPos + self.song.period / 4 elif control in KEYS: self.heldFrets.add(KEYS.index(control)) elif control in Player.ACTION1S + Player.ACTION2S: self.newNotePos = self.snapPos # Add notes for the frets that are held down or for the selected string. if self.heldFrets: self.newNotes = [ Note(f, self.song.period / 4) for f in self.heldFrets ] else: self.newNotes = [ Note(self.guitar.selectedString, self.song.period / 4) ] self.modified = True def controlReleased(self, control): if not self.song: return if control in Player.ACTION1S + Player.ACTION2S and self.newNotes and not self.heldFrets: self.newNotes = [] elif control in KEYS: self.heldFrets.remove(KEYS.index(control)) if not self.heldFrets and self.newNotes: self.newNotes = [] def quit(self): self.engine.view.popLayer(self) self.engine.view.popLayer(self.menu) def keyPressed(self, key, unicode): c = self.engine.input.controls.getMapping(key) if c in Player.CANCELS: self.engine.view.pushLayer(self.menu) elif key == pygame.K_PAGEDOWN and self.song: d = self.song.difficulty[0] v = difficulties.values() self.song.difficulty[0] = v[(v.index(d) + 1) % len(v)] elif key == pygame.K_PAGEUP and self.song: d = self.song.difficulty[0] v = difficulties.values() self.song.difficulty[0] = v[(v.index(d) - 1) % len(v)] elif key == pygame.K_DELETE and self.song: # gather up all events that intersect the cursor and delete the ones on the selected string t1 = self.snapPos t2 = self.snapPos + self.song.period / 4 e = [(time, event) for time, event in self.song.track[0].getEvents(t1, t2) if isinstance(event, Note)] for time, event in e: if event.number == self.guitar.selectedString: self.song.track[0].removeEvent(time, event) self.modified = True elif key == pygame.K_SPACE and self.song: if self.song.isPlaying(): self.song.stop() else: self.song.play(start=self.pos) c = self.controls.keyPressed(key) if c: self.controlPressed(c) return True def keyReleased(self, key): c = self.controls.keyReleased(key) if c: self.controlReleased(c) return True def run(self, ticks): self.time += ticks / 50.0 if not self.song: return self.guitar.run(ticks, self.scrollPos, self.controls) if not self.song.isPlaying(): if (self.controls.getState(Player.RIGHT) or self.controls.getState(Player.PLAYER_2_RIGHT) ) and not (self.controls.getState(Player.LEFT) or self.controls.getState(Player.PLAYER_2_LEFT)): self.pos += self.song.period * self.scrollSpeed self.scrollSpeed += ticks / 4096.0 elif (self.controls.getState(Player.LEFT) or self.controls.getState(Player.PLAYER_2_LEFT) ) and not (self.controls.getState(Player.RIGHT) or self.controls.getState(Player.PLAYER_2_RIGHT)): self.pos -= self.song.period * self.scrollSpeed self.scrollSpeed += ticks / 4096.0 else: self.scrollSpeed = 0 else: self.pos = self.song.getPosition() self.pos = max(0, self.pos) quarterBeat = int(self.pos / (self.song.period / 4) + .5) self.snapPos = quarterBeat * (self.song.period / 4) # note adding if self.newNotes: if self.snapPos < self.newNotePos: self.newNotes = [] for note in self.newNotes: self.song.track[0].removeEvent(self.newNotePos, note) note.length = max(self.song.period / 4, self.snapPos - self.newNotePos) # remove all notes under the this new note oldNotes = [(time, event) for time, event in self.song.track[0].getEvents( self.newNotePos, self.newNotePos + note.length) if isinstance(event, Note)] for time, event in oldNotes: if event.number == note.number: self.song.track[0].removeEvent(time, event) if time < self.newNotePos: event.length = self.newNotePos - time self.song.track[0].addEvent(time, event) self.song.track[0].addEvent(self.newNotePos, note) if self.song.isPlaying(): self.scrollPos = self.pos else: self.scrollPos = (self.scrollPos + self.snapPos) / 2.0 def render(self, visibility, topMost): if not self.song: return v = 1.0 - ((1 - visibility)**2) # render the background t = self.time / 100 + 34 w, h, = self.engine.view.geometry[2:4] r = .5 if self.spinnyDisabled != True and Theme.spinnyEditorDisabled: self.background.transform.reset() self.background.transform.translate( w / 2 + math.sin(t / 2) * w / 2 * r, h / 2 + math.cos(t) * h / 2 * r) self.background.transform.rotate(-t) self.background.transform.scale( math.sin(t / 8) + 2, math.sin(t / 8) + 2) self.background.draw() self.camera.target = (2, 0, 5.5) self.camera.origin = (-2, 9, 5.5) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(60, 4.0 / 3.0, 0.1, 1000) glMatrixMode(GL_MODELVIEW) glLoadIdentity() self.camera.apply() self.guitar.render(v, self.song, self.scrollPos, self.controls) self.engine.view.setOrthogonalProjection(normalize=True) font = self.engine.data.font try: Theme.setSelectedColor() w, h = font.getStringSize(" ") if self.song.isPlaying(): status = _("Playing") else: status = _("Stopped") t = "%d.%02d'%03d" % (self.pos / 60000, (self.pos % 60000) / 1000, self.pos % 1000) font.render(t, (.05, .05 - h / 2)) font.render(status, (.05, .05 + h / 2)) font.render(unicode(self.song.difficulty[0]), (.05, .05 + 3 * h / 2)) Theme.setBaseColor() text = self.song.info.name + (self.modified and "*" or "") Dialogs.wrapText(font, (.5, .05 - h / 2), text) finally: self.engine.view.resetProjection()
def __init__(self): QMainWindow.__init__(self) # Setup the ui form self.ui = Ui_MainWindow() self.ui.setupUi(self) # Create a new QGraphicsScene and show it on the ui self.scene = QGraphicsScene(self) self.scene.setBackgroundBrush(QColor(120, 120, 120)) self.ui.graphicsView.setScene(self.scene) self.ui.graphicsView.show() # Setup tuning self.tuning = [('Standard Tuning', [ Note("E", 2), Note("A", 2), Note("D", 3), Note("G", 3), Note("B", 3), Note("E", 4) ]), ('Dropped D', [ Note("D", 2), Note("A", 2), Note("D", 3), Note("G", 3), Note("B", 3), Note("E", 4) ]), ('Dropped C', [ Note("C", 2), Note("G", 2), Note("C", 3), Note("F", 3), Note("A", 3), Note("D", 4) ])] t1 = QGraphicsTextItem() t2 = QGraphicsTextItem() t3 = QGraphicsTextItem() t4 = QGraphicsTextItem() t5 = QGraphicsTextItem() t6 = QGraphicsTextItem() self.tuning_list = [t1, t2, t3, t4, t5, t6] # Define fret number self.FRETS = 22 # Create a new Guitar object self.GibsonGuitar = Guitar(self.FRETS, self.tuning[0][1]) # Create a new Guitar Fretboard object and show it on the scene self.GibsonGuitar_Fretboard = GraphicsFretboard(self.FRETS) self.GibsonGuitar_Fretboard.setPos(0, 0) self.scene.addItem(self.GibsonGuitar_Fretboard) # Draw the tuning self.draw_tuning(self.tuning[0][1]) # Populate the notes pool self.notes = [ 'A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#' ] # Populate the scales pool self.scales = [ Scale("Pentatonic Major", [2, 2, 3, 2, 3], 'box_pattern'), Scale("Pentatonic Minor", [3, 2, 2, 3, 2], 'box_pattern'), Scale("Pentatonic Blues", [3, 2, 1, 1, 3, 2], 'box_pattern'), Scale("Major", [2, 2, 1, 2, 2, 2, 1], 'three_notes'), Scale("Ionian", [2, 2, 1, 2, 2, 2, 1], 'three_notes'), Scale("Dorian", [2, 1, 2, 2, 2, 1, 2], 'three_notes'), Scale("Phrygian", [1, 2, 2, 2, 1, 2, 2], 'three_notes'), Scale("Lydian", [2, 2, 2, 1, 2, 2, 1], 'three_notes'), Scale("Mixolydian", [2, 2, 1, 2, 2, 1, 2], 'three_notes'), Scale("Aeolian", [2, 1, 2, 2, 1, 2, 2], 'three_notes'), Scale("Locrian", [1, 2, 2, 1, 2, 2, 2], 'three_notes'), Scale("Minor", [2, 1, 2, 2, 1, 2, 2], 'three_notes'), Scale("Harmonic Minor", [2, 1, 2, 2, 1, 3, 1], 'three_notes'), Scale("Melodic Minor - Ascending", [2, 1, 2, 2, 2, 2, 1], 'three_notes'), Scale("Melodic Minor - Descending", [2, 1, 2, 2, 1, 2, 2], 'three_notes'), Scale("Chromatic", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'three_notes'), Scale("Whole Tone", [2, 2, 2, 2, 2, 2], 'three_notes'), Scale("Diminished", [2, 1, 2, 1, 2, 1, 2], 'four_notes') ] # Add notes and scales to the comboboxes for note in self.notes: self.ui.rootComboBox.addItem(note) for scale in self.scales: self.ui.scaleComboBox.addItem(scale.name) for item in self.tuning: self.ui.tuningComboBox.addItem(item[0]) # Set the init value to the first item self.ui.rootComboBox.setCurrentIndex = 0 self.ui.scaleComboBox.setCurrentIndex = 0 # Setup the root note and the current scale to be the first item from the combobox self.root_note = self.notes[self.ui.rootComboBox.currentIndex()] self.currentScale = self.scales[self.ui.scaleComboBox.currentIndex()] self.scale = self.currentScale.get_scale_notes(self.root_note) self.update_repr() # Signaling if combobox has changed self.ui.rootComboBox.currentIndexChanged.connect(self.update_ui) self.ui.scaleComboBox.currentIndexChanged.connect(self.update_ui) self.ui.tuningComboBox.currentIndexChanged.connect(self.update_tuning) # Set the initial position for the scale draw self.newPos = 0 # Set the initial string number for the one sting per scale representation self.sting_num = 0 # Length of the scale self.intervalLength = len(self.currentScale.intervals) # QTimer object for animation self.timer = QTimer() self.is_animation_active = False # Variable to store the actual scale on the guitar for drawing # It based on the Radio Button selection self.notes_to_draw = []
class Main(QMainWindow): def __init__(self): QMainWindow.__init__(self) # Setup the ui form self.ui = Ui_MainWindow() self.ui.setupUi(self) # Create a new QGraphicsScene and show it on the ui self.scene = QGraphicsScene(self) self.scene.setBackgroundBrush(QColor(120, 120, 120)) self.ui.graphicsView.setScene(self.scene) self.ui.graphicsView.show() # Setup tuning self.tuning = [('Standard Tuning', [ Note("E", 2), Note("A", 2), Note("D", 3), Note("G", 3), Note("B", 3), Note("E", 4) ]), ('Dropped D', [ Note("D", 2), Note("A", 2), Note("D", 3), Note("G", 3), Note("B", 3), Note("E", 4) ]), ('Dropped C', [ Note("C", 2), Note("G", 2), Note("C", 3), Note("F", 3), Note("A", 3), Note("D", 4) ])] t1 = QGraphicsTextItem() t2 = QGraphicsTextItem() t3 = QGraphicsTextItem() t4 = QGraphicsTextItem() t5 = QGraphicsTextItem() t6 = QGraphicsTextItem() self.tuning_list = [t1, t2, t3, t4, t5, t6] # Define fret number self.FRETS = 22 # Create a new Guitar object self.GibsonGuitar = Guitar(self.FRETS, self.tuning[0][1]) # Create a new Guitar Fretboard object and show it on the scene self.GibsonGuitar_Fretboard = GraphicsFretboard(self.FRETS) self.GibsonGuitar_Fretboard.setPos(0, 0) self.scene.addItem(self.GibsonGuitar_Fretboard) # Draw the tuning self.draw_tuning(self.tuning[0][1]) # Populate the notes pool self.notes = [ 'A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#' ] # Populate the scales pool self.scales = [ Scale("Pentatonic Major", [2, 2, 3, 2, 3], 'box_pattern'), Scale("Pentatonic Minor", [3, 2, 2, 3, 2], 'box_pattern'), Scale("Pentatonic Blues", [3, 2, 1, 1, 3, 2], 'box_pattern'), Scale("Major", [2, 2, 1, 2, 2, 2, 1], 'three_notes'), Scale("Ionian", [2, 2, 1, 2, 2, 2, 1], 'three_notes'), Scale("Dorian", [2, 1, 2, 2, 2, 1, 2], 'three_notes'), Scale("Phrygian", [1, 2, 2, 2, 1, 2, 2], 'three_notes'), Scale("Lydian", [2, 2, 2, 1, 2, 2, 1], 'three_notes'), Scale("Mixolydian", [2, 2, 1, 2, 2, 1, 2], 'three_notes'), Scale("Aeolian", [2, 1, 2, 2, 1, 2, 2], 'three_notes'), Scale("Locrian", [1, 2, 2, 1, 2, 2, 2], 'three_notes'), Scale("Minor", [2, 1, 2, 2, 1, 2, 2], 'three_notes'), Scale("Harmonic Minor", [2, 1, 2, 2, 1, 3, 1], 'three_notes'), Scale("Melodic Minor - Ascending", [2, 1, 2, 2, 2, 2, 1], 'three_notes'), Scale("Melodic Minor - Descending", [2, 1, 2, 2, 1, 2, 2], 'three_notes'), Scale("Chromatic", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'three_notes'), Scale("Whole Tone", [2, 2, 2, 2, 2, 2], 'three_notes'), Scale("Diminished", [2, 1, 2, 1, 2, 1, 2], 'four_notes') ] # Add notes and scales to the comboboxes for note in self.notes: self.ui.rootComboBox.addItem(note) for scale in self.scales: self.ui.scaleComboBox.addItem(scale.name) for item in self.tuning: self.ui.tuningComboBox.addItem(item[0]) # Set the init value to the first item self.ui.rootComboBox.setCurrentIndex = 0 self.ui.scaleComboBox.setCurrentIndex = 0 # Setup the root note and the current scale to be the first item from the combobox self.root_note = self.notes[self.ui.rootComboBox.currentIndex()] self.currentScale = self.scales[self.ui.scaleComboBox.currentIndex()] self.scale = self.currentScale.get_scale_notes(self.root_note) self.update_repr() # Signaling if combobox has changed self.ui.rootComboBox.currentIndexChanged.connect(self.update_ui) self.ui.scaleComboBox.currentIndexChanged.connect(self.update_ui) self.ui.tuningComboBox.currentIndexChanged.connect(self.update_tuning) # Set the initial position for the scale draw self.newPos = 0 # Set the initial string number for the one sting per scale representation self.sting_num = 0 # Length of the scale self.intervalLength = len(self.currentScale.intervals) # QTimer object for animation self.timer = QTimer() self.is_animation_active = False # Variable to store the actual scale on the guitar for drawing # It based on the Radio Button selection self.notes_to_draw = [] def update_ui(self): """ Callback for ComboBox signaling Update the root note and the scale """ self.root_note = self.notes[self.ui.rootComboBox.currentIndex()] self.currentScale = self.scales[self.ui.scaleComboBox.currentIndex()] self.scale = self.currentScale.get_scale_notes(self.root_note) self.intervalLength = len(self.currentScale.intervals) self.update_repr() def update_tuning(self): """ Update the tuning based on the combo box selection""" # Get the current tuning from the combobox index current_tuning = self.tuning[self.ui.tuningComboBox.currentIndex()] # Update the Guitar object self.GibsonGuitar.tuning(current_tuning[1]) # Update the scene for note, text in zip(current_tuning[1], self.tuning_list): text.setPlainText(note.name) def update_repr(self): """ Update the scale representation """ if self.currentScale.get_scale_repr() == 'box_pattern': self.ui.boxPatternRadioButton.setChecked(True) if self.currentScale.get_scale_repr() == 'three_notes': self.ui.threeNotesRadioButton.setChecked(True) if self.currentScale.get_scale_repr() == 'four_notes': self.ui.fourNotesRadioButton.setChecked(True) def update_scale_pos(self, pos): """ Update the scale position """ self.newPos = pos if self.ui.boxPatternRadioButton.isChecked(): self.notes_to_draw = self.GibsonGuitar.get_box_pattern( self.scale, self.newPos) if self.ui.threeNotesRadioButton.isChecked(): self.notes_to_draw = self.GibsonGuitar.get_three_notes( self.scale, self.newPos) if self.ui.fourNotesRadioButton.isChecked(): self.notes_to_draw = self.GibsonGuitar.get_four_notes( self.scale, self.newPos) if self.ui.oneStringRadioButton.isChecked(): self.notes_to_draw = self.GibsonGuitar.get_one_string_pattern( self.scale, self.newPos) def clear_fretboard(self): """ Delete all GraphicsNoteItem from the scene """ for item in self.scene.items(): if item.type() == GraphicsNoteItem.UserType + 1: self.scene.removeItem(item) self.scene.update() def draw_tuning(self, tuning): """ Draw the tuning on the scene """ # Reverse the order tuning_notes = tuning[::-1] y = 150 # Iterate over the tuning_notes and the QGraphicsTextItem parallel # Set the tuning_notes content for the self.tuning_list QGraphicsTextItems for note, text in zip(tuning_notes, self.tuning_list): text.setPlainText(note.name) text.setFont(QFont("Verdana", 14)) text.setDefaultTextColor(QColor(Qt.white)) # Set the position and add it to the scene text.setPos(-30, y) self.scene.addItem(text) y -= 30 self.scene.update() def draw_notes(self, notes): """ Draw guitar notes on the scene from the notes list """ for s, string in enumerate(notes): for note in string: if note[0].name == self.root_note: note_color = QColor('red') else: note_color = QColor('green') # First item from the tuple is the Note object note_item = GraphicsNoteItem(note[0].name, note_color) # Second item from the tuple is the position on the string note_index = note[1] # Get the note coordinate from the fretboard note_item.setPos( self.GibsonGuitar_Fretboard.note_coordinates[s] [note_index]) self.scene.addItem(note_item) def draw_one_string_scale(self, notes, string_num): """ Draw guitar notes on the scene from the notes list """ for item in notes: for note in item: if note[0].name == self.root_note: note_color = QColor('red') else: note_color = QColor('green') # First item from the tuple is the Note object note_item = GraphicsNoteItem(note[0].name, note_color) # Second item from the tuple is the position on the string note_index = note[1] # Get the note coordinate from the fretboard note_item.setPos( self.GibsonGuitar_Fretboard.note_coordinates[string_num] [note_index]) self.scene.addItem(note_item) def drawFirstPos(self): """ Callback function for firstPushButton """ # If the animation still running stop it if self.is_animation_active: self.stopAnimation() # Clear the fretboard self.clear_fretboard() # Set the string number back to zero if oneStringRadioButton is checked # Get the updated scale notes and draw them if self.ui.oneStringRadioButton.isChecked(): self.sting_num = 0 self.update_scale_pos(self.sting_num) self.draw_one_string_scale(self.notes_to_draw, self.sting_num) else: # Set the position back to zero self.newPos = 0 # Update the pos and draw the notes on the fretboard self.update_scale_pos(self.newPos) self.draw_notes(self.notes_to_draw) def drawNextPos(self): """ Callback function for nextPushButton """ # If the animation still running stop it # if self.is_animation_active: # self.stopAnimation() # Clear the fretboard self.clear_fretboard() # Set the string number back to zero if oneStringRadioButton is checked # Get the updated scale notes and draw them if self.ui.oneStringRadioButton.isChecked(): # Increase the string_num by one if possible, if not set it back to zero if self.sting_num == 5: self.sting_num = 0 else: self.sting_num += 1 self.update_scale_pos(self.sting_num) self.draw_one_string_scale(self.notes_to_draw, self.sting_num) else: # Check the position, it cannot be larger than the available notes in the scale # Increase it by one if possible, if not set it back to zero if self.newPos == self.intervalLength: self.newPos = 0 else: self.newPos += 1 # Update the pos and draw the notes on the fretboard self.update_scale_pos(self.newPos) self.draw_notes(self.notes_to_draw) def drawPrevPos(self): """ Callback function for prevPushButton """ # If the animation still running stop it if self.is_animation_active: self.stopAnimation() # Clear the fretboard self.clear_fretboard() # Set the string number back to zero if oneStringRadioButton is checked # Get the updated scale notes and draw them if self.ui.oneStringRadioButton.isChecked(): # Increase the string_num by one if possible, if not set it back to zero if self.sting_num <= 0: self.sting_num = 5 else: self.sting_num -= 1 self.update_scale_pos(self.sting_num) self.draw_one_string_scale(self.notes_to_draw, self.sting_num) else: # Check the position, it cannot be larger than the available notes in the scale # Decrease it by one if possible if self.newPos <= 0: self.newPos = self.intervalLength else: self.newPos -= 1 # Update the pos and draw the notes on the fretboard self.update_scale_pos(self.newPos) self.draw_notes(self.notes_to_draw) def drawAllPos(self): """ Callback function for allPushButton """ # If the animation still running stop it if self.is_animation_active: self.stopAnimation() # Clear the fretboard self.clear_fretboard() # Draw all scale notes on the fretboard notes = self.GibsonGuitar.get_all_scale_notes(self.scale) self.draw_notes(notes) def __animate(self): """ Callback function for QTimer timeout signal """ self.is_animation_active = True self.drawNextPos() def startAnimation(self): """ Callback function for startAnimPushButton """ self.timer.timeout.connect(self.__animate) self.timer.start(1000) def stopAnimation(self): """ Callback function for stopAnimPushButton """ self.is_animation_active = False self.timer.stop()
def add_guitar(self, serial_number, price, builder, model, type, back_wood, top_wood): guitar = Guitar(serial_number, price, builder, model, type, back_wood, top_wood) self.__guitars.append(guitar)
class GuitarSceneClient(GuitarScene, SceneClient): def createClient(self, libraryName, songName): self.guitar = Guitar(self.engine) self.visibility = 0.0 self.libraryName = libraryName self.songName = songName self.done = False self.sfxChannel = self.engine.audio.getChannel( self.engine.audio.getChannelCount() - 1) self.lastMultTime = None self.cheatCodes = [ ([117, 112, 116, 111, 109, 121, 116, 101, 109, 112, 111], self.toggleAutoPlay), ([102, 97, 115, 116, 102, 111, 114, 119, 97, 114, 100], self.goToResults) ] self.enteredCode = [] self.song = None self.autoPlay = False self.lastPickPos = None self.lastSongPos = 0.0 self.keyBurstTimeout = None self.keyBurstPeriod = 30 self.camera.target = (0, 0, 4) self.camera.origin = (0, 3, -3) self.loadSettings() self.engine.resource.load( self, "song", lambda: loadSong(self.engine, songName, library=libraryName), onLoad=self.songLoaded) self.stage = Stage.Stage(self, self.engine.resource.fileName("stage.ini")) self.engine.loadSvgDrawing(self, "fx2x", "2x.svg", textureSize=(256, 256)) self.engine.loadSvgDrawing(self, "fx3x", "3x.svg", textureSize=(256, 256)) self.engine.loadSvgDrawing(self, "fx4x", "4x.svg", textureSize=(256, 256)) Dialogs.showLoadingScreen(self.engine, lambda: self.song, text=_("Tuning Guitar...")) settingsMenu = Settings.GameSettingsMenu(self.engine) settingsMenu.fadeScreen = True self.menu = Menu(self.engine, [ (_("Return to Song"), lambda: None), (_("Restart Song"), self.restartSong), (_("Change Song"), self.changeSong), (_("Settings"), settingsMenu), (_("Quit to Main Menu"), self.quit), ], fadeScreen=True, onClose=self.resumeGame) self.restartSong() def pauseGame(self): if self.song: self.song.pause() def resumeGame(self): self.loadSettings() if self.song: self.song.unpause() def loadSettings(self): self.delay = self.engine.config.get("audio", "delay") self.screwUpVolume = self.engine.config.get("audio", "screwupvol") self.guitarVolume = self.engine.config.get("audio", "guitarvol") self.songVolume = self.engine.config.get("audio", "songvol") self.rhythmVolume = self.engine.config.get("audio", "rhythmvol") self.guitar.leftyMode = self.engine.config.get("game", "leftymode") if self.song: self.song.setBackgroundVolume(self.songVolume) self.song.setRhythmVolume(self.rhythmVolume) def songLoaded(self, song): song.difficulty = self.player.difficulty self.delay += song.info.delay # If tapping is disabled, remove the tapping indicators if not self.engine.config.get("game", "tapping"): for time, event in self.song.track.getAllEvents(): if isinstance(event, Note): event.tappable = False def quit(self): if self.song: self.song.stop() self.song = None self.done = True self.engine.view.popLayer(self.menu) self.session.world.finishGame() def changeSong(self): if self.song: self.song.stop() self.song = None self.engine.view.popLayer(self.menu) self.session.world.deleteScene(self) self.session.world.createScene("SongChoosingScene") def restartSong(self): self.engine.data.startSound.play() self.engine.view.popLayer(self.menu) self.player.reset() self.stage.reset() self.enteredCode = [] self.autoPlay = False self.engine.collectGarbage() if not self.song: return self.countdown = 8.0 self.guitar.endPick(0) self.song.stop() def run(self, ticks): SceneClient.run(self, ticks) pos = self.getSongPosition() # update song if self.song: # update stage self.stage.run(pos, self.guitar.currentPeriod) if self.countdown <= 0 and not self.song.isPlaying( ) and not self.done: self.goToResults() return if self.autoPlay: notes = self.guitar.getRequiredNotes(self.song, pos) notes = [note.number for time, note in notes] changed = False held = 0 for n, k in enumerate(KEYS): if n in notes and not self.controls.getState(k): changed = True self.controls.toggle(k, True) elif not n in notes and self.controls.getState(k): changed = True self.controls.toggle(k, False) if self.controls.getState(k): held += 1 if changed and held: self.doPick() self.song.update(ticks) if self.countdown > 0: self.guitar.setBPM(self.song.bpm) self.countdown = max(self.countdown - ticks / self.song.period, 0) if not self.countdown: self.engine.collectGarbage() self.song.setGuitarVolume(self.guitarVolume) self.song.setBackgroundVolume(self.songVolume) self.song.setRhythmVolume(self.rhythmVolume) self.song.play() # update board if not self.guitar.run(ticks, pos, self.controls): # done playing the current notes self.endPick() # missed some notes? if self.guitar.getMissedNotes(self.song, pos) and not self.guitar.playedNotes: self.song.setGuitarVolume(0.0) self.player.streak = 0 # late pick if self.keyBurstTimeout is not None and self.engine.timer.time > self.keyBurstTimeout: self.keyBurstTimeout = None notes = self.guitar.getRequiredNotes(self.song, pos) if self.guitar.controlsMatchNotes(self.controls, notes): self.doPick() def endPick(self): score = self.getExtraScoreForCurrentlyPlayedNotes() if not self.guitar.endPick(self.song.getPosition()): self.song.setGuitarVolume(0.0) self.player.addScore(score) def render3D(self): self.stage.render(self.visibility) def renderGuitar(self): self.guitar.render(self.visibility, self.song, self.getSongPosition(), self.controls) def getSongPosition(self): if self.song: if not self.done: self.lastSongPos = self.song.getPosition() return self.lastSongPos - self.countdown * self.song.period - self.delay else: # Nice speeding up animation at the end of the song return self.lastSongPos + 4.0 * ( 1 - self.visibility) * self.song.period - self.delay return 0.0 def doPick(self): if not self.song: return pos = self.getSongPosition() if self.guitar.playedNotes: # If all the played notes are tappable, there are no required notes and # the last note was played recently enough, ignore this pick if self.guitar.areNotesTappable(self.guitar.playedNotes) and \ not self.guitar.getRequiredNotes(self.song, pos) and \ pos - self.lastPickPos <= self.song.period / 2: return self.endPick() self.lastPickPos = pos if self.guitar.startPick(self.song, pos, self.controls): self.song.setGuitarVolume(self.guitarVolume) self.player.streak += 1 self.player.notesHit += len(self.guitar.playedNotes) self.player.addScore(len(self.guitar.playedNotes) * 50) self.stage.triggerPick( pos, [n[1].number for n in self.guitar.playedNotes]) if self.player.streak % 10 == 0: self.lastMultTime = pos else: self.song.setGuitarVolume(0.0) self.player.streak = 0 self.stage.triggerMiss(pos) self.sfxChannel.play(self.engine.data.screwUpSound) self.sfxChannel.setVolume(self.screwUpVolume) def toggleAutoPlay(self): self.autoPlay = not self.autoPlay if self.autoPlay: Dialogs.showMessage(self.engine, _("Jurgen will show you how it is done.")) else: Dialogs.showMessage(self.engine, _("Jurgen has left the building.")) return self.autoPlay def goToResults(self): if self.song: self.song.stop() self.song = None self.done = True self.session.world.deleteScene(self) self.session.world.createScene("GameResultsScene", libraryName=self.libraryName, songName=self.songName) def keyPressed(self, key, unicode): control = self.controls.keyPressed(key) if control in (Player.ACTION1, Player.ACTION2): for k in KEYS: if self.controls.getState(k): self.keyBurstTimeout = None break else: self.keyBurstTimeout = self.engine.timer.time + self.keyBurstPeriod return True if control in (Player.ACTION1, Player.ACTION2) and self.song: self.doPick() elif control in KEYS and self.song: # Check whether we can tap the currently required notes pos = self.getSongPosition() notes = self.guitar.getRequiredNotes(self.song, pos) if self.player.streak > 0 and \ self.guitar.areNotesTappable(notes) and \ self.guitar.controlsMatchNotes(self.controls, notes): self.doPick() elif control == Player.CANCEL: self.pauseGame() self.engine.view.pushLayer(self.menu) return True elif key >= ord('a') and key <= ord('z'): # cheat codes n = len(self.enteredCode) for code, func in self.cheatCodes: if n < len(code): if key == code[n]: self.enteredCode.append(key) if self.enteredCode == code: self.enteredCode = [] self.player.cheating = True func() break else: self.enteredCode = [] def getExtraScoreForCurrentlyPlayedNotes(self): if not self.song: return 0 noteCount = len(self.guitar.playedNotes) pickLength = self.guitar.getPickLength(self.getSongPosition()) if pickLength > 1.1 * self.song.period / 4: return int(.1 * pickLength * noteCount) return 0 def keyReleased(self, key): if self.controls.keyReleased(key) in KEYS and self.song: # Check whether we can tap the currently required notes pos = self.getSongPosition() notes = self.guitar.getRequiredNotes(self.song, pos) if self.player.streak > 0 and \ self.guitar.areNotesTappable(notes) and \ self.guitar.controlsMatchNotes(self.controls, notes): self.doPick() # Otherwise we end the pick if the notes have been playing long enough elif self.lastPickPos is not None and pos - self.lastPickPos > self.song.period / 2: self.endPick() def render(self, visibility, topMost): SceneClient.render(self, visibility, topMost) font = self.engine.data.font bigFont = self.engine.data.bigFont self.visibility = v = 1.0 - ((1 - visibility)**2) self.engine.view.setOrthogonalProjection(normalize=True) try: # show countdown if self.countdown > 1: Theme.setBaseColor(min(1.0, 3.0 - abs(4.0 - self.countdown))) text = _("Get Ready to Rock") w, h = font.getStringSize(text) font.render(text, (.5 - w / 2, .3)) if self.countdown < 6: scale = 0.002 + 0.0005 * (self.countdown % 1)**3 text = "%d" % (self.countdown) w, h = bigFont.getStringSize(text, scale=scale) Theme.setSelectedColor() bigFont.render(text, (.5 - w / 2, .45 - h / 2), scale=scale) w, h = font.getStringSize(" ") y = .05 - h / 2 - (1.0 - v) * .2 # show song name if self.countdown and self.song: Theme.setBaseColor(min(1.0, 4.0 - abs(4.0 - self.countdown))) Dialogs.wrapText(font, (.05, .05 - h / 2), self.song.info.name + " \n " + self.song.info.artist, rightMargin=.6, scale=0.0015) Theme.setSelectedColor() font.render( "%d" % (self.player.score + self.getExtraScoreForCurrentlyPlayedNotes()), (.6, y)) font.render("%dx" % self.player.getScoreMultiplier(), (.6, y + h)) # show the streak counter and miss message if self.player.streak > 0 and self.song: text = _("%d hit") % self.player.streak factor = 0.0 if self.lastPickPos: diff = self.getSongPosition() - self.lastPickPos if diff > 0 and diff < self.song.period * 2: factor = .25 * (1.0 - (diff / (self.song.period * 2)))**2 factor = (1.0 + factor) * 0.002 tw, th = font.getStringSize(text, scale=factor) font.render(text, (.16 - tw / 2, y + h / 2 - th / 2), scale=factor) elif self.lastPickPos is not None and self.countdown <= 0: diff = self.getSongPosition() - self.lastPickPos alpha = 1.0 - diff * 0.005 if alpha > .1: Theme.setSelectedColor(alpha) glPushMatrix() glTranslate(.1, y + 0.000005 * diff**2, 0) glRotatef(math.sin(self.lastPickPos) * 25, 0, 0, 1) font.render(_("Missed!"), (0, 0)) glPopMatrix() # show the streak balls if self.player.streak >= 30: glColor3f(.5, .5, 1) elif self.player.streak >= 20: glColor3f(1, 1, .5) elif self.player.streak >= 10: glColor3f(1, .5, .5) else: glColor3f(.5, 1, .5) s = min(39, self.player.streak) % 10 + 1 font.render(Data.BALL2 * s + Data.BALL1 * (10 - s), (.67, y + h * 1.3), scale=0.0011) # show multiplier changes if self.song and self.lastMultTime is not None: diff = self.getSongPosition() - self.lastMultTime if diff > 0 and diff < self.song.period * 2: m = self.player.getScoreMultiplier() c = (1, 1, 1) if self.player.streak >= 40: texture = None elif m == 1: texture = None elif m == 2: texture = self.fx2x.texture c = (1, .5, .5) elif m == 3: texture = self.fx3x.texture c = (1, 1, .5) elif m == 4: texture = self.fx4x.texture c = (.5, .5, 1) f = (1.0 - abs(self.song.period * 1 - diff) / (self.song.period * 1))**2 # Flash the screen glBegin(GL_TRIANGLE_STRIP) glColor4f(c[0], c[1], c[2], (f - .5) * 1) glVertex2f(0, 0) glColor4f(c[0], c[1], c[2], (f - .5) * 1) glVertex2f(1, 0) glColor4f(c[0], c[1], c[2], (f - .5) * .25) glVertex2f(0, 1) glColor4f(c[0], c[1], c[2], (f - .5) * .25) glVertex2f(1, 1) glEnd() if texture: glPushMatrix() glEnable(GL_TEXTURE_2D) texture.bind() size = (texture.pixelSize[0] * .002, texture.pixelSize[1] * .002) glTranslatef(.5, .15, 0) glBlendFunc(GL_SRC_ALPHA, GL_ONE) f = .5 + .5 * (diff / self.song.period)**3 glColor4f(1, 1, 1, min(1, 2 - f)) glBegin(GL_TRIANGLE_STRIP) glTexCoord2f(0.0, 0.0) glVertex2f(-size[0] * f, -size[1] * f) glTexCoord2f(1.0, 0.0) glVertex2f(size[0] * f, -size[1] * f) glTexCoord2f(0.0, 1.0) glVertex2f(-size[0] * f, size[1] * f) glTexCoord2f(1.0, 1.0) glVertex2f(size[0] * f, size[1] * f) glEnd() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glPopMatrix() # show the comments if self.song and self.song.info.tutorial: glColor3f(1, 1, 1) pos = self.getSongPosition() for time, event in self.song.track.getEvents( pos - self.song.period * 2, pos + self.song.period * 4): if isinstance(event, PictureEvent): if pos < time or pos > time + event.length: continue try: picture = event.picture except: self.engine.loadSvgDrawing( event, "picture", os.path.join(self.libraryName, self.songName, event.fileName)) picture = event.picture w, h, = self.engine.view.geometry[2:4] fadePeriod = 500.0 f = (1.0 - min(1.0, abs(pos - time) / fadePeriod) * min(1.0, abs(pos - time - event.length) / fadePeriod) )**2 picture.transform.reset() picture.transform.translate(w / 2, (f * -2 + 1) * h / 2) picture.transform.scale(1, -1) picture.draw() elif isinstance(event, TextEvent): if pos >= time and pos <= time + event.length: text = _(event.text) w, h = font.getStringSize(text) font.render(text, (.5 - w / 2, .67)) finally: self.engine.view.resetProjection()
def addGuitar(self, serialNumber, price, spec): self.guitar = Guitar(serialNumber, price, spec) self._guitars.append(self.guitar)