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