Module GuitarScene
[hide private]
[frames] | no frames]

Source Code for Module GuitarScene

  1  ##################################################################### 
  2  # -*- coding: iso-8859-1 -*-                                        # 
  3  #                                                                   # 
  4  # Frets on Fire                                                     # 
  5  # Copyright (C) 2006 Sami Kyöstilä                                  # 
  6  #                                                                   # 
  7  # This program is free software; you can redistribute it and/or     # 
  8  # modify it under the terms of the GNU General Public License       # 
  9  # as published by the Free Software Foundation; either version 2    # 
 10  # of the License, or (at your option) any later version.            # 
 11  #                                                                   # 
 12  # This program is distributed in the hope that it will be useful,   # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of    # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     # 
 15  # GNU General Public License for more details.                      # 
 16  #                                                                   # 
 17  # You should have received a copy of the GNU General Public License # 
 18  # along with this program; if not, write to the Free Software       # 
 19  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,        # 
 20  # MA  02110-1301, USA.                                              # 
 21  ##################################################################### 
 22   
 23  from Scene import SceneServer, SceneClient 
 24  from Song import Note, TextEvent, PictureEvent, loadSong 
 25  from Menu import Menu 
 26  from Guitar import Guitar, KEYS 
 27  from Language import _ 
 28  import Player 
 29  import Dialogs 
 30  import Data 
 31  import Theme 
 32  import View 
 33  import Audio 
 34  import Stage 
 35  import Settings 
 36   
 37  import math 
 38  import pygame 
 39  import random 
 40  import os 
 41  from OpenGL.GL import * 
 42   
43 -class GuitarScene:
44 pass
45
46 -class GuitarSceneServer(GuitarScene, SceneServer):
47 pass
48
49 -class GuitarSceneClient(GuitarScene, SceneClient):
50 - def createClient(self, libraryName, songName):
51 self.guitar = Guitar(self.engine) 52 self.visibility = 0.0 53 self.libraryName = libraryName 54 self.songName = songName 55 self.done = False 56 self.sfxChannel = self.engine.audio.getChannel(self.engine.audio.getChannelCount() - 1) 57 self.lastMultTime = None 58 self.cheatCodes = [ 59 ([117, 112, 116, 111, 109, 121, 116, 101, 109, 112, 111], self.toggleAutoPlay), 60 ([102, 97, 115, 116, 102, 111, 114, 119, 97, 114, 100], self.goToResults) 61 ] 62 self.enteredCode = [] 63 self.song = None 64 self.autoPlay = False 65 self.lastPickPos = None 66 self.lastSongPos = 0.0 67 self.keyBurstTimeout = None 68 self.keyBurstPeriod = 30 69 self.camera.target = (0, 0, 4) 70 self.camera.origin = (0, 3, -3) 71 72 self.loadSettings() 73 self.engine.resource.load(self, "song", lambda: loadSong(self.engine, songName, library = libraryName), onLoad = self.songLoaded) 74 75 self.stage = Stage.Stage(self, self.engine.resource.fileName("stage.ini")) 76 77 self.engine.loadSvgDrawing(self, "fx2x", "2x.svg", textureSize = (256, 256)) 78 self.engine.loadSvgDrawing(self, "fx3x", "3x.svg", textureSize = (256, 256)) 79 self.engine.loadSvgDrawing(self, "fx4x", "4x.svg", textureSize = (256, 256)) 80 81 Dialogs.showLoadingScreen(self.engine, lambda: self.song, text = _("Tuning Guitar...")) 82 83 settingsMenu = Settings.GameSettingsMenu(self.engine) 84 settingsMenu.fadeScreen = True 85 86 self.menu = Menu(self.engine, [ 87 (_("Restart Song"), self.restartSong), 88 (_("Change Song"), self.changeSong), 89 (_("Settings"), settingsMenu), 90 (_("Quit to Main Menu"), self.quit), 91 ], fadeScreen = True, onClose = self.resumeGame) 92 93 self.restartSong()
94
95 - def pauseGame(self):
96 if self.song: 97 self.song.pause()
98
99 - def resumeGame(self):
100 self.loadSettings() 101 if self.song: 102 self.song.unpause()
103
104 - def loadSettings(self):
105 self.delay = self.engine.config.get("audio", "delay") 106 self.screwUpVolume = self.engine.config.get("audio", "screwupvol") 107 self.guitarVolume = self.engine.config.get("audio", "guitarvol") 108 self.songVolume = self.engine.config.get("audio", "songvol") 109 self.rhythmVolume = self.engine.config.get("audio", "rhythmvol") 110 self.guitar.leftyMode = self.engine.config.get("game", "leftymode") 111 112 if self.song: 113 self.song.setBackgroundVolume(self.songVolume) 114 self.song.setRhythmVolume(self.rhythmVolume)
115
116 - def songLoaded(self, song):
117 song.difficulty = self.player.difficulty 118 self.delay += song.info.delay 119 120 # If tapping is disabled, remove the tapping indicators 121 if not self.engine.config.get("game", "tapping"): 122 for time, event in self.song.track.getAllEvents(): 123 if isinstance(event, Note): 124 event.tappable = False
125
126 - def quit(self):
127 if self.song: 128 self.song.stop() 129 self.song = None 130 self.done = True 131 self.engine.view.popLayer(self.menu) 132 self.session.world.finishGame()
133
134 - def changeSong(self):
135 if self.song: 136 self.song.stop() 137 self.song = None 138 self.engine.view.popLayer(self.menu) 139 self.session.world.deleteScene(self) 140 self.session.world.createScene("SongChoosingScene")
141
142 - def restartSong(self):
143 self.engine.data.startSound.play() 144 self.engine.view.popLayer(self.menu) 145 self.player.reset() 146 self.stage.reset() 147 self.enteredCode = [] 148 self.autoPlay = False 149 self.engine.collectGarbage() 150 151 if not self.song: 152 return 153 154 self.countdown = 8.0 155 self.guitar.endPick(0) 156 self.song.stop()
157
158 - def run(self, ticks):
159 SceneClient.run(self, ticks) 160 pos = self.getSongPosition() 161 162 # update song 163 if self.song: 164 # update stage 165 self.stage.run(pos, self.guitar.currentPeriod) 166 167 if self.countdown <= 0 and not self.song.isPlaying() and not self.done: 168 self.goToResults() 169 return 170 171 if self.autoPlay: 172 notes = self.guitar.getRequiredNotes(self.song, pos) 173 notes = [note.number for time, note in notes] 174 175 changed = False 176 held = 0 177 for n, k in enumerate(KEYS): 178 if n in notes and not self.controls.getState(k): 179 changed = True 180 self.controls.toggle(k, True) 181 elif not n in notes and self.controls.getState(k): 182 changed = True 183 self.controls.toggle(k, False) 184 if self.controls.getState(k): 185 held += 1 186 if changed and held: 187 self.doPick() 188 189 self.song.update(ticks) 190 if self.countdown > 0: 191 self.guitar.setBPM(self.song.bpm) 192 self.countdown = max(self.countdown - ticks / self.song.period, 0) 193 if not self.countdown: 194 self.engine.collectGarbage() 195 self.song.setGuitarVolume(self.guitarVolume) 196 self.song.setBackgroundVolume(self.songVolume) 197 self.song.setRhythmVolume(self.rhythmVolume) 198 self.song.play() 199 200 # update board 201 if not self.guitar.run(ticks, pos, self.controls): 202 # done playing the current notes 203 self.endPick() 204 205 # missed some notes? 206 if self.guitar.getMissedNotes(self.song, pos) and not self.guitar.playedNotes: 207 self.song.setGuitarVolume(0.0) 208 self.player.streak = 0 209 210 # late pick 211 if self.keyBurstTimeout is not None and self.engine.timer.time > self.keyBurstTimeout: 212 self.keyBurstTimeout = None 213 notes = self.guitar.getRequiredNotes(self.song, pos) 214 if self.guitar.controlsMatchNotes(self.controls, notes): 215 self.doPick()
216
217 - def endPick(self):
218 score = self.getExtraScoreForCurrentlyPlayedNotes() 219 if not self.guitar.endPick(self.song.getPosition()): 220 self.song.setGuitarVolume(0.0) 221 self.player.addScore(score)
222
223 - def render3D(self):
224 self.stage.render(self.visibility)
225
226 - def renderGuitar(self):
227 self.guitar.render(self.visibility, self.song, self.getSongPosition(), self.controls)
228
229 - def getSongPosition(self):
230 if self.song: 231 if not self.done: 232 self.lastSongPos = self.song.getPosition() 233 return self.lastSongPos - self.countdown * self.song.period - self.delay 234 else: 235 # Nice speeding up animation at the end of the song 236 return self.lastSongPos + 4.0 * (1 - self.visibility) * self.song.period - self.delay 237 return 0.0
238
239 - def doPick(self):
240 if not self.song: 241 return 242 243 pos = self.getSongPosition() 244 245 if self.guitar.playedNotes: 246 # If all the played notes are tappable, there are no required notes and 247 # the last note was played recently enough, ignore this pick 248 if self.guitar.areNotesTappable(self.guitar.playedNotes) and \ 249 not self.guitar.getRequiredNotes(self.song, pos) and \ 250 pos - self.lastPickPos <= self.song.period / 2: 251 return 252 self.endPick() 253 254 self.lastPickPos = pos 255 256 if self.guitar.startPick(self.song, pos, self.controls): 257 self.song.setGuitarVolume(self.guitarVolume) 258 self.player.streak += 1 259 self.player.notesHit += len(self.guitar.playedNotes) 260 self.player.addScore(len(self.guitar.playedNotes) * 50) 261 self.stage.triggerPick(pos, [n[1].number for n in self.guitar.playedNotes]) 262 if self.player.streak % 10 == 0: 263 self.lastMultTime = pos 264 else: 265 self.song.setGuitarVolume(0.0) 266 self.player.streak = 0 267 self.stage.triggerMiss(pos) 268 self.sfxChannel.play(self.engine.data.screwUpSound) 269 self.sfxChannel.setVolume(self.screwUpVolume)
270
271 - def toggleAutoPlay(self):
272 self.autoPlay = not self.autoPlay 273 if self.autoPlay: 274 Dialogs.showMessage(self.engine, _("Jurgen will show you how it is done.")) 275 else: 276 Dialogs.showMessage(self.engine, _("Jurgen has left the building.")) 277 return self.autoPlay
278
279 - def goToResults(self):
280 if self.song: 281 self.song.stop() 282 self.song = None 283 self.done = True 284 self.session.world.deleteScene(self) 285 self.session.world.createScene("GameResultsScene", libraryName = self.libraryName, songName = self.songName)
286
287 - def keyPressed(self, key, unicode):
288 control = self.controls.keyPressed(key) 289 290 if control in (Player.ACTION1, Player.ACTION2): 291 for k in KEYS: 292 if self.controls.getState(k): 293 self.keyBurstTimeout = None 294 break 295 else: 296 self.keyBurstTimeout = self.engine.timer.time + self.keyBurstPeriod 297 return True 298 299 if control in (Player.ACTION1, Player.ACTION2) and self.song: 300 self.doPick() 301 elif control in KEYS and self.song: 302 # Check whether we can tap the currently required notes 303 pos = self.getSongPosition() 304 notes = self.guitar.getRequiredNotes(self.song, pos) 305 306 if self.player.streak > 0 and \ 307 self.guitar.areNotesTappable(notes) and \ 308 self.guitar.controlsMatchNotes(self.controls, notes): 309 self.doPick() 310 elif control == Player.CANCEL: 311 self.pauseGame() 312 self.engine.view.pushLayer(self.menu) 313 return True 314 elif key >= ord('a') and key <= ord('z'): 315 # cheat codes 316 n = len(self.enteredCode) 317 for code, func in self.cheatCodes: 318 if n < len(code): 319 if key == code[n]: 320 self.enteredCode.append(key) 321 if self.enteredCode == code: 322 self.enteredCode = [] 323 self.player.cheating = True 324 func() 325 break 326 else: 327 self.enteredCode = []
328
330 if not self.song: 331 return 0 332 333 noteCount = len(self.guitar.playedNotes) 334 pickLength = self.guitar.getPickLength(self.getSongPosition()) 335 if pickLength > 1.1 * self.song.period / 4: 336 return int(.1 * pickLength * noteCount) 337 return 0
338
339 - def keyReleased(self, key):
340 if self.controls.keyReleased(key) in KEYS and self.song: 341 # Check whether we can tap the currently required notes 342 pos = self.getSongPosition() 343 notes = self.guitar.getRequiredNotes(self.song, pos) 344 if self.player.streak > 0 and \ 345 self.guitar.areNotesTappable(notes) and \ 346 self.guitar.controlsMatchNotes(self.controls, notes): 347 self.doPick() 348 # Otherwise we end the pick if the notes have been playing long enough 349 elif self.lastPickPos is not None and pos - self.lastPickPos > self.song.period / 2: 350 self.endPick()
351 - def render(self, visibility, topMost):
352 SceneClient.render(self, visibility, topMost) 353 354 font = self.engine.data.font 355 bigFont = self.engine.data.bigFont 356 357 self.visibility = v = 1.0 - ((1 - visibility) ** 2) 358 359 self.engine.view.setOrthogonalProjection(normalize = True) 360 try: 361 # show countdown 362 if self.countdown > 1: 363 Theme.setBaseColor(min(1.0, 3.0 - abs(4.0 - self.countdown))) 364 text = _("Get Ready to Rock") 365 w, h = font.getStringSize(text) 366 font.render(text, (.5 - w / 2, .3)) 367 if self.countdown < 6: 368 scale = 0.002 + 0.0005 * (self.countdown % 1) ** 3 369 text = "%d" % (self.countdown) 370 w, h = bigFont.getStringSize(text, scale = scale) 371 Theme.setSelectedColor() 372 bigFont.render(text, (.5 - w / 2, .45 - h / 2), scale = scale) 373 374 w, h = font.getStringSize(" ") 375 y = .05 - h / 2 - (1.0 - v) * .2 376 377 # show song name 378 if self.countdown and self.song: 379 Theme.setBaseColor(min(1.0, 4.0 - abs(4.0 - self.countdown))) 380 Dialogs.wrapText(font, (.05, .05 - h / 2), self.song.info.name + " \n " + self.song.info.artist, rightMargin = .6, scale = 0.0015) 381 382 Theme.setSelectedColor() 383 384 font.render("%d" % (self.player.score + self.getExtraScoreForCurrentlyPlayedNotes()), (.6, y)) 385 font.render("%dx" % self.player.getScoreMultiplier(), (.6, y + h)) 386 387 # show the streak counter and miss message 388 if self.player.streak > 0 and self.song: 389 text = _("%d hit") % self.player.streak 390 factor = 0.0 391 if self.lastPickPos: 392 diff = self.getSongPosition() - self.lastPickPos 393 if diff > 0 and diff < self.song.period * 2: 394 factor = .25 * (1.0 - (diff / (self.song.period * 2))) ** 2 395 factor = (1.0 + factor) * 0.002 396 tw, th = font.getStringSize(text, scale = factor) 397 font.render(text, (.16 - tw / 2, y + h / 2 - th / 2), scale = factor) 398 elif self.lastPickPos is not None and self.countdown <= 0: 399 diff = self.getSongPosition() - self.lastPickPos 400 alpha = 1.0 - diff * 0.005 401 if alpha > .1: 402 Theme.setSelectedColor(alpha) 403 glPushMatrix() 404 glTranslate(.1, y + 0.000005 * diff ** 2, 0) 405 glRotatef(math.sin(self.lastPickPos) * 25, 0, 0, 1) 406 font.render(_("Missed!"), (0, 0)) 407 glPopMatrix() 408 409 # show the streak balls 410 if self.player.streak >= 30: 411 glColor3f(.5, .5, 1) 412 elif self.player.streak >= 20: 413 glColor3f(1, 1, .5) 414 elif self.player.streak >= 10: 415 glColor3f(1, .5, .5) 416 else: 417 glColor3f(.5, 1, .5) 418 419 s = min(39, self.player.streak) % 10 + 1 420 font.render(Data.BALL2 * s + Data.BALL1 * (10 - s), (.67, y + h * 1.3), scale = 0.0011) 421 422 # show multiplier changes 423 if self.song and self.lastMultTime is not None: 424 diff = self.getSongPosition() - self.lastMultTime 425 if diff > 0 and diff < self.song.period * 2: 426 m = self.player.getScoreMultiplier() 427 c = (1, 1, 1) 428 if self.player.streak >= 40: 429 texture = None 430 elif m == 1: 431 texture = None 432 elif m == 2: 433 texture = self.fx2x.texture 434 c = (1, .5, .5) 435 elif m == 3: 436 texture = self.fx3x.texture 437 c = (1, 1, .5) 438 elif m == 4: 439 texture = self.fx4x.texture 440 c = (.5, .5, 1) 441 442 f = (1.0 - abs(self.song.period * 1 - diff) / (self.song.period * 1)) ** 2 443 444 # Flash the screen 445 glBegin(GL_TRIANGLE_STRIP) 446 glColor4f(c[0], c[1], c[2], (f - .5) * 1) 447 glVertex2f(0, 0) 448 glColor4f(c[0], c[1], c[2], (f - .5) * 1) 449 glVertex2f(1, 0) 450 glColor4f(c[0], c[1], c[2], (f - .5) * .25) 451 glVertex2f(0, 1) 452 glColor4f(c[0], c[1], c[2], (f - .5) * .25) 453 glVertex2f(1, 1) 454 glEnd() 455 456 if texture: 457 glPushMatrix() 458 glEnable(GL_TEXTURE_2D) 459 texture.bind() 460 size = (texture.pixelSize[0] * .002, texture.pixelSize[1] * .002) 461 462 glTranslatef(.5, .15, 0) 463 glBlendFunc(GL_SRC_ALPHA, GL_ONE) 464 465 f = .5 + .5 * (diff / self.song.period) ** 3 466 glColor4f(1, 1, 1, min(1, 2 - f)) 467 glBegin(GL_TRIANGLE_STRIP) 468 glTexCoord2f(0.0, 0.0) 469 glVertex2f(-size[0] * f, -size[1] * f) 470 glTexCoord2f(1.0, 0.0) 471 glVertex2f( size[0] * f, -size[1] * f) 472 glTexCoord2f(0.0, 1.0) 473 glVertex2f(-size[0] * f, size[1] * f) 474 glTexCoord2f(1.0, 1.0) 475 glVertex2f( size[0] * f, size[1] * f) 476 glEnd() 477 478 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 479 glPopMatrix() 480 481 # show the comments 482 if self.song and self.song.info.tutorial: 483 glColor3f(1, 1, 1) 484 pos = self.getSongPosition() 485 for time, event in self.song.track.getEvents(pos - self.song.period * 2, pos + self.song.period * 4): 486 if isinstance(event, PictureEvent): 487 if pos < time or pos > time + event.length: 488 continue 489 490 try: 491 picture = event.picture 492 except: 493 self.engine.loadSvgDrawing(event, "picture", os.path.join(self.libraryName, self.songName, event.fileName)) 494 picture = event.picture 495 496 w, h, = self.engine.view.geometry[2:4] 497 fadePeriod = 500.0 498 f = (1.0 - min(1.0, abs(pos - time) / fadePeriod) * min(1.0, abs(pos - time - event.length) / fadePeriod)) ** 2 499 picture.transform.reset() 500 picture.transform.translate(w / 2, (f * -2 + 1) * h / 2) 501 picture.transform.scale(1, -1) 502 picture.draw() 503 elif isinstance(event, TextEvent): 504 if pos >= time and pos <= time + event.length: 505 text = _(event.text) 506 w, h = font.getStringSize(text) 507 font.render(text, (.5 - w / 2, .67)) 508 finally: 509 self.engine.view.resetProjection()
510