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