1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
45
48
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
96 if self.song:
97 self.song.pause()
98
103
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
125
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
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
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
163 if self.song:
164
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
201 if not self.guitar.run(ticks, pos, self.controls):
202
203 self.endPick()
204
205
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
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
222
224 self.stage.render(self.visibility)
225
228
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
236 return self.lastSongPos + 4.0 * (1 - self.visibility) * self.song.period - self.delay
237 return 0.0
238
240 if not self.song:
241 return
242
243 pos = self.getSongPosition()
244
245 if self.guitar.playedNotes:
246
247
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
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
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
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
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
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
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
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
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
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
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
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
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
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