no channel
[lake] / lake_of_heavenly_wind / LakeOfHeavenlyWind.py
1 # -*- coding: utf-8 -*-
2
3 from os import listdir
4 from os.path import join
5 from math import sin, cos, radians, sqrt
6 from random import randint, randrange, choice, random
7 import codecs
8
9 from pygame import Surface, Color, PixelArray
10 from pygame.image import load, save
11 from pygame.draw import circle, arc, aaline, line, ellipse
12 from pygame.font import Font
13 from pygame.mixer import Sound
14 from pygame.event import clear
15 from pygame.locals import *
16
17 from lake_of_heavenly_wind.pgfw.Game import Game
18 from lake_of_heavenly_wind.pgfw.GameChild import GameChild
19 from lake_of_heavenly_wind.pgfw.Sprite import Sprite
20 from lake_of_heavenly_wind.pgfw.Animation import Animation
21
22 class LakeOfHeavenlyWind(Game):
23
24 def __init__(self):
25 Game.__init__(self)
26 self.title.activate()
27
28 def set_children(self):
29 Game.set_children(self)
30 self.high_scores = HighScores(self)
31 self.title = Title(self)
32 self.game_screen = GameScreen(self)
33 self.book = Book(self)
34
35 def update(self):
36 self.title.update()
37 self.game_screen.update()
38 self.book.update()
39
40
41 class Book(GameChild, list):
42
43 def __init__(self, parent):
44 GameChild.__init__(self, parent)
45 path = self.get_resource("text", "book")
46 for block in codecs.open(path, "r", "utf-8").read().split("\n\n"):
47 self.append(Hexagram(self, block))
48
49 def hide_indicators(self):
50 for hexagram in self:
51 hexagram.indicator.hide()
52
53 def hide_explanations(self):
54 for hexagram in self:
55 hexagram.explanation.hide()
56
57 def are_indicators_hidden(self):
58 return all(hexagram.indicator.is_hidden() for hexagram in self)
59
60 def update(self):
61 for hexagram in self:
62 hexagram.update()
63
64
65 class Hexagram(GameChild):
66
67 TRIGRAM_NAMES = {0b111: "heaven", 0b110: "marsh", 0b101: "fire",
68 0b100: "thunder", 0b11: "wind", 0b10: "water",
69 1: "mountain", 0: "earth"}
70
71 def __init__(self, parent, block):
72 GameChild.__init__(self, parent)
73 lines = self.lines = block.split("\n")
74 self.index = int(lines[0], 2)
75 pair = ""
76 for ii in xrange(2):
77 pair += self.TRIGRAM_NAMES[(self.index & \
78 (0b111 << ii * 3)) >> (ii * 3)]. \
79 upper()
80 pair += " ABOVE ~ " if not ii else " BELOW"
81 lines.insert(2, pair)
82 font_path = self.get_resource("display", "font")
83 font = Font(font_path, 10)
84 explanation = self.explanation = Sprite(self, 4000)
85 for line in map(unicode, lines[1:]):
86 if line:
87 frame = Surface((640, 16), SRCALPHA)
88 spaced = line[0]
89 for ch in line[1:]:
90 spaced += " " + ch
91 caption = font.render(spaced, True, (102, 102, 102))
92 rect = caption.get_rect()
93 rect.center = frame.get_rect().center
94 frame.blit(caption, rect)
95 explanation.add_frame(frame)
96 explanation.location.center = self.parent.parent.game_screen.link. \
97 background.location.center
98 explanation.hide(),
99 indicator = self.indicator = Sprite(self, 500)
100 font = Font(font_path, 36)
101 font.set_italic(True)
102 indicator.add_frame(font.render(lines[1][0], True, (31, 31, 31)))
103 blank = Surface(indicator.location.size)
104 blank.set_colorkey((0, 0, 0))
105 indicator.add_frame(blank)
106 rect = self.parent.parent.game_screen.oracle.screens[0].get_rect()
107 indicator.location.center = 32 + rect.centerx, 16 + rect.centery
108 indicator.add_location(offset=(self.get_display_surface(). \
109 get_width() - rect.centerx * 2 - 64, 0))
110 indicator.hide()
111
112 def show_indicator(self):
113 self.indicator.unhide()
114
115 def show_explanation(self):
116 self.explanation.get_current_frameset().reset()
117 self.explanation.unhide()
118
119 def update(self):
120 self.explanation.update()
121 self.indicator.update()
122
123
124 class HighScores(GameChild):
125
126 def __init__(self, parent):
127 GameChild.__init__(self, parent)
128
129 def read(self):
130 scores = []
131 for line in file(self.get_resource("text", "scores")):
132 scores.append(int(line))
133 return scores
134
135 def add(self, score):
136 file(self.get_resource("text", "scores"), "a").write(str(score) + "\n")
137
138
139 class Title(GameChild):
140
141 def __init__(self, parent):
142 GameChild.__init__(self, parent)
143 self.time_filter = self.get_game().time_filter
144 self.display_surface = self.get_display_surface()
145 self.delegate = self.get_game().delegate
146 self.music = Sound(self.get_resource("audio", "outer"))
147 self.start_fx = SoundEffect(self, "start", .4)
148 self.deactivate()
149 background = self.background = Sprite(self)
150 tile_size = self.tile_size = 8
151 for _ in xrange(9):
152 tile = Surface((tile_size, tile_size))
153 frame = Surface(self.display_surface.get_size())
154 for x in xrange(tile.get_width()):
155 for y in xrange(tile.get_height()):
156 tile.set_at((x, y), choice([(128, 128, 128), (95, 95, 95)]))
157 for x in xrange(0, frame.get_width(), tile.get_width()):
158 for y in xrange(0, frame.get_height(), tile.get_height()):
159 frame.blit(tile, (x, y))
160 background.add_frame(frame)
161 layers = self.layers = []
162 key = (255, 0, 255)
163 for ii in xrange(3):
164 tiles = []
165 for _ in xrange(8):
166 tile = Surface((tile_size, tile_size))
167 tile.fill(key)
168 for x in xrange(tile.get_width()):
169 for y in xrange(tile.get_height()):
170 if random() > .885:
171 tile.set_at((x, y), [(255, 255, 80), (80, 255, 255),
172 (22, 22, 22)][ii])
173 tiles.append(tile)
174 layer = Sprite(self)
175 frame = Surface((background.location.w + tile_size * (16 + ii),
176 background.location.h + tile_size * (16 + ii)))
177 frame.set_colorkey(key)
178 for x in xrange(0, frame.get_width(), tile.get_width()):
179 for y in xrange(0, frame.get_height(), tile.get_height()):
180 frame.blit(choice(tiles), (x, y))
181 layer.add_frame(frame)
182 layers.append(layer)
183 self.subscribe(self.respond)
184
185 def set_caption(self):
186 caption = self.caption = Sprite(self, 5000)
187 font = Font(self.get_resource("display", "font"), 12)
188 color = Color(0, 0, 0)
189 if random() < .92:
190 texts = ["Wind Off of Heaven's Lake", u"澤 天 風"]
191 else:
192 texts = ["Pleasure in Strong Penetration", u"兌 乾 巽"]
193 for text in texts:
194 spaced = text[0]
195 for ch in text[1:]:
196 spaced += " " + ch
197 frame = Surface((640, 100), SRCALPHA)
198 plate = font.render(spaced.upper(), True, (255, 255, 255))
199 rect = plate.get_rect()
200 rect.center = frame.get_rect().center
201 frame.blit(plate, rect)
202 caption.add_frame(frame)
203 caption.location.center = self.display_surface.get_rect().centerx, 400
204 caption.get_current_frameset().current_index = 1
205
206 def deactivate(self):
207 self.active = False
208 self.music.fadeout(500)
209
210 def respond(self, event):
211 if self.active:
212 if self.delegate.compare(event, "any"):
213 self.deactivate()
214 self.parent.game_screen.activate()
215 self.start_fx.play()
216
217 def activate(self, incoming=None):
218 self.active = True
219 self.music.play(-1, 0, 500)
220 scores = self.scores = []
221 count = 5
222 for ii, score in enumerate(sorted(self.parent. \
223 high_scores.read())[-count:]):
224 font = Font(self.get_resource("display", "font"), 11 + (ii * 1))
225 sprite = Sprite(self, 380)
226 sprite.add_frame(font.render(str(score), True, (32, 200, 32),
227 (112, 240, 240)))
228 if score == incoming:
229 surface = Surface(sprite.location.size)
230 key = (255, 0, 255)
231 surface.fill(key)
232 surface.set_colorkey(key)
233 sprite.add_frame(surface)
234 sprite.location.centerx = self.display_surface.get_rect(). \
235 centerx + 100 * ((count - ii - 1) - \
236 count / 2.0 + .5)
237 scores.append(sprite)
238 self.set_caption()
239 clear()
240
241 def update(self):
242 if self.active:
243 self.background.update()
244 ds = self.display_surface
245 for ii, layer in enumerate(reversed(self.layers)):
246 if ii == 0:
247 layer.move(-.25, -.25)
248 if layer.location.right < ds.get_width():
249 layer.move(self.tile_size * 16, self.tile_size * 16)
250 elif ii == 1:
251 layer.move(-.5)
252 if layer.location.right < ds.get_width():
253 layer.move(self.tile_size * 16)
254 elif ii == 2:
255 layer.move(.5, .5)
256 if layer.location.left > 0:
257 layer.move(-self.tile_size * 16, -self.tile_size * 16)
258 layer.update()
259 for score in self.scores:
260 score.update()
261 self.caption.update()
262
263
264 class SoundEffect(GameChild, Sound):
265
266 def __init__(self, parent, name, volume=1.0):
267 GameChild.__init__(self, parent)
268 Sound.__init__(self, self.get_resource("audio", name))
269 self.name = name
270 self.set_volume(volume)
271
272 def play(self, position=.5):
273 channel = Sound.play(self)
274 right = 1 + min(0, ((position - .5) * 2))
275 left = 1 - max(0, ((position - .5) * 2))
276 if channel is not None:
277 channel.set_volume(left, right)
278
279
280 class GameScreen(GameChild):
281
282 PALETTE = [[(191, 191, 220), (191, 255, 191)],
283 [(191, 191, 220), (255, 191, 191)],
284 [(191, 191, 225), (191, 255, 191)],
285 [(191, 191, 255), (255, 191, 191)],
286 [(191, 220, 191), (191, 191, 255)],
287 [(191, 220, 191), (255, 191, 191)],
288 [(191, 255, 191), (255, 191, 191)],
289 [(220, 191, 191), (191, 191, 255)],
290 [(220, 191, 191), (191, 255, 191)]]
291
292 def __init__(self, parent):
293 GameChild.__init__(self, parent)
294 self.delegate = self.get_game().delegate
295 self.time_filter = self.get_game().time_filter
296 self.display_surface = self.get_display_surface()
297 self.music = Sound(self.get_resource("audio", "inner"))
298 self.music.set_volume(.8)
299 self.previous_palette = None
300 self.wave = 0
301 self.deactivate()
302 self.set_background()
303 self.pulp = Pulp(self)
304 self.link = Link(self)
305 self.paddles = Paddles(self)
306 self.rails = Rails(self)
307 self.oracle = Oracle(self)
308 self.subscribe(self.respond)
309
310 def set_background(self):
311 w, h = 8, 8
312 tile = Surface((w, h))
313 while True:
314 palette = choice(self.PALETTE)
315 if self.previous_palette != palette:
316 self.previous_palette = palette
317 break
318 for x in xrange(w):
319 for y in xrange(h):
320 tile.set_at((x, y), palette[(x + y) % 2])
321 background = self.background = Surface(self.display_surface.get_size())
322 for x in xrange(0, background.get_width(), w):
323 for y in xrange(0, background.get_height(), h):
324 background.blit(tile, (x, y))
325
326 def activate(self):
327 self.active = True
328 self.ending = False
329 self.game_over_elapsed = 0
330 self.music.play(-1)
331 self.wave = 0
332 self.pulp.reset()
333 self.rails.reset()
334 self.oracle.clear()
335 self.introduce()
336 self.paddles.set_background()
337 self.paddles.reset_position()
338 self.paddles.arrange_graticules(0)
339 self.set_background()
340
341 def introduce(self):
342 self.freeze()
343 self.introducing = True
344 self.introducing_elapsed = 0
345
346 def increase_wave(self):
347 self.wave += 1
348 self.paddles.explode_mines()
349 self.paddles.set_background()
350 self.paddles.reset_position()
351 self.paddles.arrange_graticules(0)
352 self.rails.increase_spawn_rate()
353 self.introduce()
354 self.rails.clear_phages()
355 self.rails.set_deviation()
356 self.set_background()
357 self.pulp.health += 1.0 / 6
358 if self.pulp.health > .9999:
359 self.pulp.health = .9999
360 self.parent.book.hide_indicators()
361
362 def freeze(self):
363 self.frozen = True
364
365 def unfreeze(self):
366 self.frozen = False
367
368 def is_frozen(self):
369 return self.frozen
370
371 def deactivate(self):
372 self.active = False
373 self.music.stop()
374
375 def end(self):
376 self.freeze()
377 self.ending = True
378 self.parent.high_scores.add(int(self.pulp.score))
379 self.paddles.explode_mines()
380 self.paddles.reset_position()
381 self.oracle.animals.hide()
382 for hexagram in self.parent.book:
383 if not hexagram.explanation.is_hidden():
384 hexagram.explanation.halt()
385
386 def respond(self, event):
387 if self.active and self.ending and self.game_over_elapsed > 3000 and \
388 self.delegate.compare(event, "any"):
389 self.parent.book.hide_explanations()
390 self.parent.book.hide_indicators()
391 self.deactivate()
392 self.parent.title.activate(int(self.pulp.score))
393
394 def update(self):
395 if self.active:
396 self.display_surface.blit(self.background, (0, 0))
397 if self.introducing:
398 font = Font(self.get_resource("display", "font"), 10)
399 text = "WAVE " + str(self.wave)
400 spaced = text[0]
401 for ch in text[1:]:
402 spaced += " " + ch
403 text = font.render(spaced, True, (31, 31, 31))
404 rect = text.get_rect()
405 rect.center = self.display_surface.get_rect().centerx, 260
406 self.display_surface.blit(text, rect)
407 if self.introducing_elapsed > 2000:
408 self.introducing = False
409 self.unfreeze()
410 else:
411 self.introducing_elapsed += self.time_filter. \
412 get_last_frame_duration()
413 elif self.ending:
414 font = Font(self.get_resource("display", "font"), 10)
415 text = font.render("G A M E O V E R", True,
416 (31, 31, 31))
417 rect = text.get_rect()
418 rect.center = self.display_surface.get_rect().centerx, 260
419 self.display_surface.blit(text, rect)
420 self.game_over_elapsed += self.time_filter. \
421 get_last_frame_duration()
422 self.oracle.animals.update()
423 self.pulp.update()
424 if not self.is_frozen():
425 self.rails.update()
426 self.paddles.update()
427 self.link.update()
428 self.oracle.update()
429
430
431 class Pulp(GameChild, Surface):
432
433 def __init__(self, parent):
434 GameChild.__init__(self, parent)
435 self.display_surface = self.get_display_surface()
436 self.font = Font(self.get_resource("display", "font"), 24)
437 self.explosions = [Explosion(self) for _ in xrange(32)]
438 self.lose_health_fx = SoundEffect(self, "lose-health")
439 self.blow_up_fx = SoundEffect(self, "blow-up", .8)
440 self.reset()
441 Surface.__init__(self, (self.display_surface.get_width(), 96))
442 self.rect = self.get_rect()
443 indicators = self.indicators = []
444 root = self.get_resource("image", "pulp")
445 for path in sorted(listdir(root)):
446 indicator = Sprite(self, 5500)
447 image = load(join(root, path))
448 image_r = self.image_r = image.get_rect()
449 width = self.get_width()
450 height = (self.rect.h / image_r.h + 2) * image_r.h
451 color = Color(0, 0, 0)
452 for hue in [-150, -128, 149]:
453 pixels = PixelArray(image.copy())
454 for x in xrange(len(pixels)):
455 for y in xrange(len(pixels[0])):
456 color = Color(*image.unmap_rgb(pixels[x][y]))
457 h, s, l, a = color.hsla
458 color.hsla = round((h + hue) % 360), round(s), \
459 round(l), round(a)
460 pixels[x][y] = color
461 plate = pixels.make_surface()
462 frame = Surface((width, height), SRCALPHA)
463 for x in xrange(0, width, image_r.w):
464 for y in xrange(0, height, image_r.h):
465 frame.blit(plate, (x, y))
466 indicator.add_frame(frame)
467 indicator.display_surface = self
468 indicators.append(indicator)
469 self.score_backgrounds = []
470 tile = Surface((2, 2))
471 for x in xrange(tile.get_width()):
472 for y in xrange(tile.get_height()):
473 tile.set_at((x, y), [(220, 220, 220),
474 (128, 128, 128)][(x + y) % 2])
475 for _ in xrange(8):
476 surface = Surface((22, 26))
477 for x in xrange(0, surface.get_width(), tile.get_width()):
478 for y in xrange(0, surface.get_height(), tile.get_height()):
479 surface.blit(tile, (x, y))
480 self.score_backgrounds.append(surface)
481
482 def reset(self):
483 self.health = .9999
484 self.score = 0
485 for explosion in self.explosions:
486 explosion.reset()
487
488 def update(self):
489 for indicator in self.indicators:
490 indicator.move(dy=-1)
491 if indicator.location.top < -self.image_r.h:
492 indicator.move(dy=self.image_r.h)
493 self.indicators[int(self.health * len(self.indicators))].update()
494 self.display_surface.blit(self, self.rect)
495 # if not self.parent.is_frozen():
496 # self.score += self.health * .1
497 text = str(int(self.score))
498 color = randint(0, 120), randint(0, 120), randint(0, 120)
499 for ii, digit in enumerate(text):
500 background = self.score_backgrounds[ii]
501 br = background.get_rect()
502 br.centerx = 40 * (ii - len(text) / 2.0 + .5) + \
503 self.display_surface.get_rect().centerx
504 self.display_surface.blit(self.score_backgrounds[ii], br)
505 glyph = self.font.render(digit, True, color)
506 gr = glyph.get_rect()
507 gr.centerx = br.centerx
508 self.display_surface.blit(glyph, gr)
509 outgoing = []
510 for phage in self.parent.rails.phages:
511 if phage.get_center()[1] <= self.rect.centery:
512 outgoing.append(phage)
513 self.health -= .05
514 for explosion in self.explosions:
515 if explosion.is_hidden():
516 explosion.get_current_frameset().reset()
517 explosion.location.center = phage.get_center()
518 explosion.unhide()
519 break
520 if self.health < 0:
521 self.parent.end()
522 break
523 if outgoing:
524 total = 0
525 for phage in outgoing:
526 total += phage.get_center()[0]
527 self.parent.rails.phages.remove(phage)
528 position = float(total) / len(outgoing) / \
529 self.get_display_surface().get_width()
530 self.lose_health_fx.play(position)
531 self.blow_up_fx.play(position)
532 for explosion in self.explosions:
533 explosion.update()
534
535
536 class Explosion(Sprite):
537
538 def __init__(self, parent):
539 Sprite.__init__(self, parent)
540 count = 32
541 color = Color(0, 0, 0)
542 for ii in xrange(count):
543 frame = Surface((64, 64), SRCALPHA)
544 ratio = float(ii) / count
545 color.hsla = 60 - ratio * 60, 100, 50, 100 - ratio * 100
546 fr = frame.get_rect()
547 circle(frame, color, fr.center, 6 + int(ratio * (fr.w / 2 - 6)), 5)
548 self.add_frame(frame)
549 self.reset()
550
551 def reset(self):
552 self.hide()
553
554 def shift_frame(self):
555 Sprite.shift_frame(self)
556 frameset = self.get_current_frameset()
557 if frameset.current_index == frameset.length() - 1:
558 self.hide()
559
560
561 class Link(GameChild):
562
563 def __init__(self, parent):
564 GameChild.__init__(self, parent)
565 self.display_surface = self.get_display_surface()
566 self.background = Sprite(self, 500)
567 tile = Surface((2, 2))
568 for x in xrange(tile.get_width()):
569 for y in xrange(tile.get_height()):
570 tile.set_at((x, y), [(255, 255, 0), (228, 228, 228)][(x + y) % 2])
571 for jj in xrange(3):
572 frame = Surface((self.display_surface.get_width(), 24))
573 for x in xrange(0, frame.get_width(), tile.get_width()):
574 for y in xrange(0, frame.get_height(), tile.get_height()):
575 frame.blit(tile, (x, y))
576 for ii, x in enumerate(xrange(-4, frame.get_width(), 16)):
577 colors = [(63, 255, 63), (240, 240, 63), (191, 31, 220)]
578 primary_color = colors[(ii - jj) % 3]
579 secondary_color = colors[(ii - jj + 1) % 3]
580 frame.fill(primary_color, (x, 0, 8, 3))
581 frame.fill(secondary_color, (x + 2, 1, 4, 2))
582 frame.fill(primary_color, (x, frame.get_height() - 3, 8, 3))
583 frame.fill(secondary_color, (x + 2, frame.get_height() - 3, 4,
584 2))
585 self.background.add_frame(frame)
586 self.background.location.top = self.parent.pulp.get_rect().bottom
587
588 def update(self):
589 self.background.update()
590
591
592 class Paddles(GameChild):
593
594 LEFT, RIGHT = range(2)
595 GRATICULES = (((0, 0),),
596 ((.5, 270), (.5, 90)),
597 ((.5, 0), (.5, 180)),
598 ((.5, 45), (.5, 225)),
599 ((.5, 135), (.5, 315)),
600 ((1.0, 0), (0, 0), (1.0, 180)),
601 ((1.0, 45), (0, 0), (1.0, 225)),
602 ((1.0, 135), (0, 0), (1.0, 315)),
603 ((.5774, 0), (.5774, 120), (.5774, 240)))
604
605 def __init__(self, parent):
606 GameChild.__init__(self, parent)
607 self.time_filter = self.get_game().time_filter
608 self.display_surface = self.get_display_surface()
609 self.delegate = self.get_game().delegate
610 self.detonate_mine_fx = SoundEffect(self, "detonate", .8)
611 self.drop_mine_fx = SoundEffect(self, "drop", .3)
612 self.eliminate_phage_fx = SoundEffect(self, "eliminate", 1)
613 self.graticule_y = 0
614 self.set_background()
615 self.set_lattice()
616 self.set_paddles()
617 self.graticule = Graticule(self)
618 self.arrange_graticules(0)
619 self.mines = [Mine(self) for _ in xrange(16)]
620 self.active_mines = [[], []]
621 self.subscribe(self.respond)
622
623 def set_background(self):
624 background = self.background = Sprite(self, 120)
625 mask = Surface((8, 8), SRCALPHA)
626 for x in xrange(mask.get_width()):
627 for y in xrange(mask.get_height()):
628 mask.set_at((x, y), [(255, 255, 255, 255),
629 (255, 255, 255, 0)][(x + y) % 2])
630 color = Color(0, 0, 0)
631 count = 16
632 h = 32
633 hue_ii = 0
634 hue_base = randrange(0, 360)
635 for ii in xrange(count):
636 frame = Surface((self.display_surface.get_width(), h), SRCALPHA)
637 for y in xrange(h):
638 hue = 30 * (float(hue_ii) / (count / 2)) + hue_base
639 if hue >= 360:
640 hue -= 360
641 color.hsla = hue, 100, 50, 100 * (float(y) / h)
642 frame.fill(color, (0, y, frame.get_width(), 1))
643 hue_ii += 1 if ii < count / 2 else -1
644 for x in xrange(0, frame.get_width(), mask.get_width()):
645 for y in xrange(0, h, mask.get_height()):
646 frame.blit(mask, (x, y), None, BLEND_RGBA_MIN)
647 background.add_frame(frame)
648 background.location.bottom = self.get_display_surface().get_rect(). \
649 bottom
650
651 def set_lattice(self):
652 mask = load(self.get_resource("image", "plateau")).convert_alpha()
653 tile = Surface((9, 9))
654 tw, th = tile.get_size()
655 for x in xrange(tw):
656 for y in xrange(th):
657 tile.set_at((x, y), [(0, 255, 255), (255, 0, 255),
658 (255, 255, 0)][(x + y) % 3])
659 w, h = self.background.location.size
660 w += mask.get_width()
661 base = Surface((w, h), SRCALPHA)
662 for x in xrange(0, w, tw):
663 for y in xrange(0, h, th):
664 base.blit(tile, (x, y))
665 for x in xrange(0, w, mask.get_width()):
666 base.blit(mask, (x, 0), None, BLEND_RGBA_MIN)
667 lattice = self.lattice = Sprite(self)
668 lattice.add_frame(base)
669 lattice.location.bottom = self.get_display_surface().get_rect().bottom + 2
670
671 def set_paddles(self):
672 image = load(self.get_resource("image", "paddle")).convert_alpha()
673 self.paddle_length = image.get_width()
674 margin = self.margin = (self.get_display_surface().get_width() / 2) - \
675 self.paddle_length
676 surface = Surface((image.get_width() * 2 + margin, image.get_height()))
677 key = (255, 0, 255)
678 surface.fill(key)
679 surface.set_colorkey(key)
680 surface.blit(image, (0, 0))
681 surface.blit(image, (image.get_width() + margin, 0))
682 paddles = self.paddles = Sprite(self, 60)
683 count = 8
684 color = Color(0, 0, 0)
685 for ii in xrange(count):
686 color.hsla = randrange(0, 360), 100, \
687 60 + 40 * (float(ii) / (count - 1)), 100
688 pixels = PixelArray(surface.copy())
689 pixels.replace((255, 255, 255), color)
690 frame = pixels.make_surface()
691 frame.set_colorkey(key)
692 paddles.add_frame(frame)
693 ds = self.get_display_surface()
694 paddles.add_location()
695 self.reset_position()
696 self.reset_throttle()
697
698 def reset_position(self):
699 self.moving = [False, False]
700 ds = self.get_display_surface()
701 for ii, location in enumerate(self.paddles.locations):
702 location.midbottom = ds.get_rect().centerx, \
703 ds.get_height() - 15
704 if ii == 1:
705 location.left += ds.get_width()
706
707 def reset_throttle(self):
708 self.throttle = [0, 0]
709
710 def arrange_graticules(self, index=None):
711 if index is None:
712 index = randrange(1, len(self.GRATICULES))
713 if index < 5:
714 if index == 3:
715 states = self.GRATICULES[index], self.GRATICULES[index + 1]
716 elif index == 4:
717 states = self.GRATICULES[index], self.GRATICULES[index - 1]
718 else:
719 states = self.GRATICULES[index], self.GRATICULES[index]
720 else:
721 if randint(0, 1):
722 states = self.GRATICULES[index], self.GRATICULES[0]
723 else:
724 states = self.GRATICULES[0], self.GRATICULES[index]
725 graticule = self.graticule
726 graticule.remove_locations()
727 initialized = False
728 y = self.graticule.location.centery
729 for ii, state in enumerate(states):
730 if not ii:
731 x = self.paddles.location.left + self.paddle_length / 2
732 else:
733 x = self.paddles.location.right - self.paddle_length / 2
734 for offset in state:
735 margin = 60
736 dx = margin * offset[0] * sin(radians(offset[1]))
737 dy = margin * offset[0] * - cos(radians(offset[1]))
738 center = int(float(x + dx)), int(float(y + dy))
739 if not initialized:
740 graticule.location.center = center
741 graticule.location.side = self.LEFT
742 initialized = True
743 else:
744 location = graticule.add_location((0, 0))
745 location.side = ii
746 location.center = center
747
748 def explode_mines(self):
749 count = 0
750 total_x = 0
751 for side in self.active_mines:
752 for mine in side:
753 count += 1
754 total_x += mine.location.centerx
755 mine.set_frameset("explode")
756 for location in self.graticule.locations:
757 location.unhide()
758 if count:
759 self.detonate_mine_fx.play(float(total_x) / count / \
760 self.get_display_surface().get_width())
761
762 def respond(self, event):
763 if self.parent.active and not self.parent.ending:
764 compare = self.delegate.compare
765 if compare(event, "left") or compare(event, "left", True):
766 self.moving[self.LEFT] = not event.cancel
767 elif compare(event, "right") or compare(event, "right", True):
768 self.moving[self.RIGHT] = not event.cancel
769 elif not self.parent.is_frozen() and \
770 (compare(event, "release-left") or \
771 compare(event, "release-right")):
772 side = self.LEFT if event.command == "release-left" else self.RIGHT
773 if self.active_mines[side]:
774 outgoing = []
775 count = len(self.active_mines[side])
776 mine_total_x = 0
777 hit = []
778 while self.active_mines[side]:
779 mine = self.active_mines[side][0]
780 mine_total_x += mine.location.centerx
781 center = mine.location.center
782 mine.set_frameset("explode")
783 mine.location.center = center
784 mine.get_current_frameset().reset()
785 for phage in self.parent.rails.phages:
786 px, py = phage.get_center()
787 d = sqrt((px - mine.location.centerx) ** 2 + \
788 (py - mine.location.centery) ** 2)
789 reach = 100
790 if d < reach:
791 start = phage.health
792 phage.health -= 1.5 * (reach - float(d)) / reach
793 if phage.health < 0:
794 if phage not in (r[0] for r in outgoing):
795 for record in hit:
796 if record[0] == phage:
797 start = record[1]
798 hit.remove(record)
799 break
800 outgoing.append((phage, start))
801 else:
802 if phage not in (r[0] for r in hit):
803 hit.append((phage, start))
804 self.active_mines[side].remove(mine)
805 self.detonate_mine_fx.play(float(mine_total_x) / count / \
806 self.get_display_surface(). \
807 get_width())
808 increase = 0
809 phage_total_x = 0
810 for record in outgoing:
811 increase += record[1]
812 phage_total_x += record[0].get_center()[0]
813 record[0].play(record[0].die)
814 # self.parent.rails.phages.remove(record[0])
815 self.parent.pulp.score += increase * len(outgoing) * 10
816 if outgoing:
817 self.eliminate_phage_fx.play(float(phage_total_x) / \
818 len(outgoing) / \
819 self. \
820 display_surface. \
821 get_width())
822 for location in self.graticule.locations:
823 if location.side == side:
824 location.unhide()
825 self.throttle[side] = 0
826 elif self.throttle[side] > 500:
827 total_x = 0
828 for location in self.graticule.locations:
829 if location.side == side:
830 while True:
831 mine = choice(self.mines)
832 if mine.is_hidden():
833 mine.unhide()
834 mine.location.center = location.center
835 mine.get_current_frameset().reset()
836 break
837 self.active_mines[side].append(mine)
838 total_x += mine.location.centerx
839 location.hide()
840 self.drop_mine_fx.play(float(total_x) / \
841 len(self.active_mines[side]) / \
842 self.get_display_surface(). \
843 get_width())
844
845 def update(self):
846 for ii in xrange(len(self.throttle)):
847 self.throttle[ii] += self.time_filter.get_last_frame_duration()
848 speed = 8
849 if self.moving[self.LEFT]:
850 self.paddles.move(-speed)
851 self.graticule.move(-speed)
852 elif self.moving[self.RIGHT]:
853 self.paddles.move(speed)
854 self.graticule.move(speed)
855 ds = self.get_display_surface()
856 if self.paddles.location.right < 0:
857 self.paddles.move(ds.get_width())
858 elif self.paddles.location.right > ds.get_width():
859 self.paddles.move(-ds.get_width())
860 y = self.graticule.location.centery
861 start = self.parent.link.background.rect.bottom
862 end = ds.get_height() - 32
863 position = (end - y) / float(end - start)
864 dy = self.get_game().interpolator.get_nodeset("shoot").get_y(position)
865 self.graticule.move(dy=-dy)
866 for location in self.graticule.locations:
867 if location.right < 0:
868 location.move_ip(ds.get_width(), 0)
869 elif location.right > ds.get_width():
870 location.move_ip(-ds.get_width(), 0)
871 if location.top < self.parent.link.background.rect.bottom:
872 location.move_ip(0, ds.get_height() - \
873 self.parent.link.background.rect.bottom - 32)
874 self.lattice.move(-.5)
875 if self.lattice.location.right < ds.get_rect().right:
876 self.lattice.move(ds.get_rect().right - \
877 self.lattice.location.right + 16)
878 self.lattice.update()
879 if not self.parent.is_frozen():
880 self.graticule.update()
881 for mine in self.mines:
882 mine.update()
883 self.paddles.update()
884 self.background.update()
885
886
887 class Graticule(Sprite):
888
889 def __init__(self, parent):
890 Sprite.__init__(self, parent, 120)
891 surface = Surface((14, 14), SRCALPHA)
892 rect = surface.get_rect()
893 count = 8
894 color_ii = 0
895 inner_color, outer_color, bg_color = Color(0, 0, 0), Color(0, 0, 0), \
896 Color(0, 0, 0)
897 mid = count / 2
898 for ii in xrange(count):
899 inner_color.hsla = 80, 50, 40 + 10 * (float(color_ii) / mid), 100
900 hue = randrange(0, 360)
901 outer_color.hsla = hue, 100, 50, 100
902 bg_color.hsla = randrange(0, 360), 100, 50, 16
903 frame = surface.copy()
904 circle(frame, bg_color, rect.center, 7)
905 circle(frame, inner_color, (rect.centerx, rect.centery + 1), 5)
906 circle(frame, outer_color, rect.center, 5)
907 circle(frame, inner_color, rect.center, 3)
908 circle(frame, (0, 0, 0, 0), (rect.centerx, rect.centery + 1), 2)
909 self.add_frame(frame)
910 color_ii += -1 if ii >= count / 2 else 1
911
912
913 class Mine(Sprite):
914
915 def __init__(self, parent):
916 Sprite.__init__(self, parent, 120)
917 surface = Surface((20, 20), SRCALPHA)
918 particles = []
919 distances = [4, 2, 2, 1, 0, 1, 2, 2, 4]
920 radii = [2, 2, 2, 2, 2, 2, 2, 2, 2]
921 particle_count = 48
922 for ii in xrange(particle_count):
923 angle = 360 * float(ii) / particle_count
924 distance_ii = ii % len(distances)
925 particles.append(Particle(ii, angle, distance_ii))
926 frame_count = 18
927 cx, cy = 10, 10
928 self.add_frameset(name="wait", switch=True)
929 for frame_ii in xrange(frame_count):
930 frame = surface.copy()
931 circle(frame, (255, 255, randint(0, 63), randint(100, 160)),
932 (10, 10), 10)
933 for particle in sorted(particles, key=lambda p: p.layer):
934 distance = distances[particle.distance_ii]
935 x = int(round(cx + distance * sin(radians(particle.angle))))
936 y = int(round(cy + distance * - cos(radians(particle.angle))))
937 circle(frame, particle.color, (x, y),
938 radii[particle.distance_ii])
939 particle.distance_ii += 1
940 if particle.distance_ii >= len(distances):
941 particle.distance_ii = 0
942 particle.layer += ((len(particles) / 2 - particle.layer) * \
943 2 - 1)
944 if distance == 0:
945 particle.angle = (particle.angle + 180) % 360
946 self.add_frame(frame)
947 self.add_frameset(name="explode", switch=True)
948 surface = Surface((100, 100), SRCALPHA)
949 thickness = 6
950 color = Color(0, 0, 0)
951 for radius in xrange(6, 50, 2):
952 frame = surface.copy()
953 ratio = float(radius - 6) / (50 - 6)
954 color.hsla = 60 * (1 - ratio), 80 + 10 * (1 - ratio), \
955 75 + 18 * (1 - ratio), thickness * 30 * (100 / 255.0)
956 circle(frame, color, (50, 50), radius, max(1, int(thickness)))
957 thickness -= .2
958 self.add_frame(frame)
959 self.set_frameset("wait")
960 self.hide()
961
962 def shift_frame(self):
963 Sprite.shift_frame(self)
964 frameset = self.get_current_frameset()
965 if frameset.name == "explode":
966 if frameset.current_index == frameset.length() - 1:
967 self.set_frameset("wait")
968 self.hide()
969
970
971 class Particle:
972
973 def __init__(self, ii, angle, distance_ii):
974 self.color = Color(0, 0, 0)
975 self.color.hsla = 0, 0, randint(0, 100), 100
976 self.color = choice([(27, 27, 27), (255, 63, 63), (63, 63, 255),
977 (255, 255, 255)])
978 self.angle = angle
979 self.layer = ii
980 self.distance_ii = distance_ii
981
982
983 class Rails(GameChild, list):
984
985 def __init__(self, parent):
986 GameChild.__init__(self, parent)
987 self.display_surface = self.get_display_surface()
988 self.depth = self.display_surface.get_height() + 4
989 self.stray = 128
990 self.view_y = 0
991 interpolator = self.get_game().interpolator
992 self.spawn_nodeset = interpolator.get_nodeset("spawn")
993 self.step_nodeset = interpolator.get_nodeset("step")
994 margin = 32
995 limit = self.display_surface.get_width() - margin
996 step = (limit - margin) / 16
997 for x in xrange(48, limit, step):
998 self.append(Rail(self, x,
999 x > self.display_surface.get_rect().centerx))
1000 deviations = self.deviations = []
1001 ii = 1
1002 while True:
1003 nodeset = self.get_game().interpolator.get_nodeset("deviation-" + \
1004 str(ii))
1005 ii += 1
1006 if nodeset:
1007 deviations.append(nodeset)
1008 else:
1009 break
1010 self.set_deviation()
1011 self.reset()
1012
1013 def set_deviation(self):
1014 self.deviation = choice(self.deviations)
1015 self.set_background()
1016
1017 def reset(self):
1018 self.increase_spawn_rate()
1019 self.clear_phages()
1020
1021 def clear_phages(self):
1022 self.phages = []
1023
1024 def increase_spawn_rate(self):
1025 self.spawn_rate = self.spawn_nodeset.get_y(self.parent.wave)
1026 self.phage_step = self.step_nodeset.get_y(self.parent.wave)
1027
1028 def set_background(self):
1029 end = self.parent.link.background.location.bottom
1030 length = self.depth - end
1031 color = Color(0, 0, 0)
1032 background = self.background = \
1033 Surface(self.get_display_surface().get_size())
1034 background.set_colorkey((0, 0, 0))
1035 for ii, y in enumerate(xrange(self.depth, end - 5, -5)):
1036 dx = self.stray * self.deviation.get_y(float(ii * 5) / length)
1037 px = self.stray * self.deviation.get_y(float((ii - 1) * 5) / length)
1038 hue = min(360, int(360 * float(ii * 5) / length))
1039 color.hsla = hue, 100, 40, 100
1040 for rail in self:
1041 modifier = rail.get_modifier()
1042 line(background, color, (rail.x + px * modifier, y),
1043 (rail.x + dx * modifier, y))
1044
1045 def update(self):
1046 if random() < self.spawn_rate:
1047 self.phages.append(Phage(self, choice(self)))
1048 height = 20
1049 self.display_surface.blit(self.background, (0, self.view_y),
1050 (0, self.view_y,
1051 self.display_surface.get_width(), height))
1052 self.view_y -= 3
1053 if self.view_y + height < self.parent.link.background.location.bottom:
1054 self.view_y = self.display_surface.get_height() - height
1055 for phage in self.phages:
1056 phage.update()
1057
1058
1059 class Rail(GameChild):
1060
1061 def __init__(self, parent, x, mirrored):
1062 GameChild.__init__(self, parent)
1063 self.x = x
1064 self.mirrored = mirrored
1065
1066 def get_modifier(self):
1067 return -1 if self.mirrored else 1
1068
1069
1070 class Phage(Animation):
1071
1072 TRANSPARENT_COLOR = (255, 0, 255)
1073
1074 def __init__(self, parent, rail):
1075 Animation.__init__(self, parent, self.die, 50)
1076 self.rail = rail
1077 self.t = 0
1078 self.health = 1
1079 body = self.body = []
1080 self.yr = self.parent.parent.link.background.location.bottom, \
1081 self.parent.depth
1082 for size in xrange(4, 0, -1):
1083 segment = Sprite(self, 500)
1084 for ii in xrange(2):
1085 surface = Surface((size, size))
1086 surface.fill([(255, 255, 255), (31, 31, 31)][(size + ii) % 2])
1087 surface.set_colorkey(self.TRANSPARENT_COLOR)
1088 segment.add_frame(surface)
1089 body.append(segment)
1090 center = self.parent.phage_step
1091 self.step = random() * .004 - .002 + center
1092
1093 def get_center(self):
1094 return self.body[0].location.midbottom
1095
1096 def die(self):
1097 for segment in self.body:
1098 alpha = segment.alpha - 48
1099 if alpha <= 0:
1100 self.parent.phages.remove(self)
1101 self.halt()
1102 break
1103 else:
1104 for _ in xrange(10):
1105 w, h = segment.location.size
1106 segment.get_current_frame().set_at((randrange(0, w),
1107 randrange(0, h)),
1108 self.TRANSPARENT_COLOR)
1109 segment.set_alpha(alpha)
1110
1111 def update(self):
1112 if not self.is_playing():
1113 step = self.parent.phage_step * self.health
1114 self.t += step
1115 yr = self.yr
1116 for ii, segment in sorted(enumerate(self.body), key=lambda b: b[0],
1117 reverse=True):
1118 dx = self.parent.deviation.get_y(self.t - ii * step) * \
1119 self.parent.stray * self.rail.get_modifier()
1120 segment.location.center = self.rail.x + dx, \
1121 yr[1] - (yr[1] - yr[0]) * (self.t - \
1122 step * ii)
1123 segment.update()
1124 else:
1125 for segment in self.body:
1126 segment.update()
1127 Animation.update(self)
1128
1129
1130 class Oracle(Animation):
1131
1132 def __init__(self, parent):
1133 Animation.__init__(self, parent)
1134 self.time_filter = self.get_game().time_filter
1135 self.display_surface = self.get_display_surface()
1136 # self.line_appears_fx = SoundEffect(self, "no")
1137 screens = self.screens = []
1138 for ii in xrange(2):
1139 surface = Surface((48, 48), SRCALPHA)
1140 surface.fill((127, 127, 127, 127))
1141 screens.append(surface)
1142 lines = self.lines = []
1143 for ii in xrange(2):
1144 line = Surface((36, 6))
1145 line.fill((31, 31, 31))
1146 if ii == 1:
1147 key = (255, 0, 255)
1148 line.set_colorkey(key)
1149 line.fill(key, (14, 0, 8, 6))
1150 lines.append(line)
1151 self.animals = Animals(self)
1152 self.coins = [Coin(self, ii) for ii in xrange(3)]
1153 self.register(self.display_indicator)
1154 self.clear()
1155
1156 def display_indicator(self):
1157 self.clear_screens()
1158 self.parent.parent.book.hide_explanations()
1159 response = choice(self.parent.parent.book)
1160 response.show_indicator()
1161 response.show_explanation()
1162 response.explanation.halt()
1163
1164 def clear_screens(self):
1165 for screen in self.screens:
1166 screen.fill(screen.get_at((0, 0)))
1167
1168 def clear(self):
1169 self.hexagram = []
1170 self.flips = 0
1171 self.flip_elapsed = 0
1172 self.wait_elapsed = 0
1173 self.waiting = False
1174 self.clear_screens()
1175 for coin in self.coins:
1176 coin.hide()
1177 self.animals.hide()
1178 self.halt()
1179
1180 def update(self):
1181 if not self.parent.is_frozen() and not self.waiting:
1182 if self.flips == 0:
1183 for coin in self.coins:
1184 coin.unhide()
1185 coin.set_frameset(0)
1186 if len(self.hexagram) == 6:
1187 self.clear()
1188 self.parent.parent.book.hide_indicators()
1189 for hexagram in self.parent.parent.book:
1190 explanation = hexagram.explanation
1191 if not explanation.is_hidden():
1192 frameset = explanation.get_current_frameset()
1193 frameset.current_index = frameset.length() - 1
1194 hexagram.explanation.play()
1195 self.parent.increase_wave()
1196 self.flip_elapsed = 0
1197 else:
1198 flip_length = 1500
1199 self.flip_elapsed += self.time_filter.get_last_frame_duration()
1200 if self.flip_elapsed > flip_length:
1201 self.coins[self.flips].stop()
1202 self.flips += 1
1203 self.flip_elapsed -= flip_length
1204 if self.flips == 3:
1205 self.hexagram.append(Result(self.coins))
1206 if len(self.hexagram) == 3:
1207 self.animals.send(self.hexagram)
1208 elif len(self.hexagram) == 6:
1209 self.play(self.display_indicator, delay=1000,
1210 play_once=True)
1211 self.flips = 0
1212 self.waiting = True
1213 # self.line_appears_fx.play()
1214 elif self.waiting:
1215 self.wait_elapsed += self.time_filter.get_last_frame_duration()
1216 if self.wait_elapsed > 7500:
1217 self.waiting = False
1218 self.wait_elapsed = 0
1219 ow, oh = 32, 16
1220 if self.parent.parent.book.are_indicators_hidden():
1221 for ii, result in enumerate(self.hexagram):
1222 screen = self.screens[ii / 3]
1223 screen.blit(self.lines[result.get_line_index()],
1224 (6, 5 + ((2 - ii) % 3) * 16))
1225 for ii, screen in enumerate(self.screens):
1226 rect = screen.get_rect()
1227 if ii == 0:
1228 rect.topleft = (ow, oh)
1229 else:
1230 rect.topright = self.display_surface.get_width() - ow, oh
1231 self.display_surface.blit(screen, rect)
1232 for coin in self.coins:
1233 coin.update()
1234 Animation.update(self)
1235
1236
1237 class Animals(GameChild, list):
1238
1239 def __init__(self, parent):
1240 GameChild.__init__(self, parent)
1241 self.collect_token_fx = SoundEffect(self, "power-up", .7)
1242 font = Font(self.get_resource("display", "font"), 18)
1243 color = Color(0, 0, 0)
1244 for glyph in [u"馬", u"羊", u"雉", u"龍", u"雞", u"豕", u"狗", u"牛"]:
1245 animal = Sprite(self, 80)
1246 for ii in xrange(3):
1247 hue = int(360 * float(ii) / 3)
1248 for jj in xrange(8):
1249 lightness = 20 + 80 * float(jj + 1) / 8
1250 w, h = font.size(glyph)
1251 frame = Surface((w + 2, h + 2), SRCALPHA)
1252 color.hsla = hue, 80, lightness, 100
1253 frame.blit(font.render(glyph, True, color), (0, 0))
1254 frame.blit(font.render(glyph, True, color), (1, 0))
1255 frame.blit(font.render(glyph, True, color), (2, 0))
1256 frame.blit(font.render(glyph, True, color), (2, 1))
1257 frame.blit(font.render(glyph, True, color), (2, 2))
1258 frame.blit(font.render(glyph, True, color), (1, 2))
1259 frame.blit(font.render(glyph, True, color), (0, 2))
1260 frame.blit(font.render(glyph, True, color), (0, 1))
1261 color.hsla = (hue + 60) % 360, 100, lightness, 100
1262 frame.blit(font.render(glyph, True, color), (1, 1))
1263 animal.add_frame(frame)
1264 paddles = self.parent.parent.paddles
1265 animal.add_location(count=3)
1266 self.append(animal)
1267 self.hide()
1268
1269 def hide(self):
1270 self.in_motion = None
1271 for animal in self:
1272 animal.hide()
1273
1274 def send(self, hexagram):
1275 index = sum(2 ** ii * x for ii, x in \
1276 enumerate(reversed([r.get_binary() for r in hexagram])))
1277 in_motion = self.in_motion = self[index]
1278 for location in in_motion.locations:
1279 location.bottom = self.parent.parent.link.background. \
1280 location.bottom
1281 in_motion.unhide()
1282
1283 def update(self):
1284 if self.in_motion:
1285 paddles = self.parent.parent.paddles
1286 for ii, location in enumerate(self.in_motion.locations):
1287 location.centerx = paddles.paddles.location.left + \
1288 paddles.paddle_length / 2
1289 if ii / 2:
1290 location.centerx += self.get_display_surface().get_width()
1291 if ii % 2:
1292 location.centerx += paddles.paddle_length + paddles.margin
1293 if self.in_motion.location.colliderect(paddles.paddles.location):
1294 paddles.arrange_graticules(self.index(self.in_motion) + 1)
1295 self.collect_token_fx.play(paddles.paddles.location.centerx / \
1296 float(self.get_display_surface().\
1297 get_width()))
1298 self.hide()
1299 else:
1300 self.in_motion.move(dy=4)
1301 self.in_motion.update()
1302
1303
1304 class Coin(Sprite):
1305
1306 HEADS, TAILS = "heads", "tails"
1307
1308 def __init__(self, parent, ii):
1309 Sprite.__init__(self, parent)
1310 key = (255, 0, 255)
1311 w = 8
1312 for frame_ii in xrange(w):
1313 frame = Surface((w, w))
1314 frame.fill(key)
1315 frame.set_colorkey(key)
1316 x = [0, 1, 2, 3, 3, 2, 1, 0][frame_ii]
1317 rect = (x, 0, (w / 2 - x) * 2, w)
1318 thickness = 0 if frame_ii < (w / 2) else 1
1319 ellipse(frame, (160, 120, 40), rect)
1320 ellipse(frame, (200, 150, 60), rect, thickness)
1321 self.add_frame(frame)
1322 self.add_frameset([0], name=self.HEADS)
1323 self.add_frameset([w - 1], name=self.TAILS)
1324 self.add_frameset(xrange(w), name="flipping")
1325 self.location.top = 36
1326 self.location.centerx = 20 * (ii - 3 / 2.0 + .5) + \
1327 self.display_surface.get_rect().centerx
1328 self.hide()
1329
1330 def stop(self):
1331 if randint(0, 1):
1332 self.side = self.HEADS
1333 else:
1334 self.side = self.TAILS
1335 self.set_frameset(self.side)
1336
1337
1338 class Result:
1339
1340 def __init__(self, coins):
1341 total = 0
1342 for coin in coins:
1343 if coin.side == coin.HEADS:
1344 total += 3
1345 else:
1346 total += 2
1347 self.total = total
1348
1349 def get_line_index(self):
1350 return 0 if self.total in [7, 9] else 1
1351
1352 def get_binary(self):
1353 return 0 if self.total in [7, 9] else 1