999c6f2e97f26b649dcc02683902d7d80dc1b1e4
1 # -*- coding: utf-8 -*-
4 from os
.path
import join
5 from math
import sin
, cos
, radians
, sqrt
6 from random
import randint
, randrange
, choice
, random
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 *
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
22 class LakeOfHeavenlyWind(Game
):
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
)
37 self
.game_screen
.update()
41 class Book(GameChild
, list):
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
))
49 def hide_indicators(self
):
51 hexagram
.indicator
.hide()
53 def hide_explanations(self
):
55 hexagram
.explanation
.hide()
57 def are_indicators_hidden(self
):
58 return all(hexagram
.indicator
.is_hidden() for hexagram
in self
)
65 class Hexagram(GameChild
):
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:]):
78 frame
= Surface((640, 16), SRCALPHA
)
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
90 indicator
= self
.indicator
= Sprite(self
, 500)
91 font
= Font(font_path
, 36)
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))
103 def show_indicator(self
):
104 self
.indicator
.unhide()
106 def show_explanation(self
):
107 self
.explanation
.get_current_frameset().reset()
108 self
.explanation
.unhide()
111 self
.explanation
.update()
112 self
.indicator
.update()
115 class HighScores(GameChild
):
117 def __init__(self
, parent
):
118 GameChild
.__init__(self
, parent
)
122 for line
in file(self
.get_resource("text", "scores")):
123 scores
.append(int(line
))
126 def add(self
, score
):
127 file(self
.get_resource("text", "scores"), "a").write(str(score
) + "\n")
130 class Title(GameChild
):
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)
140 background
= self
.background
= Sprite(self
)
141 tile_size
= self
.tile_size
= 8
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
= []
157 tile
= Surface((tile_size
, tile_size
))
159 for x
in xrange(tile
.get_width()):
160 for y
in xrange(tile
.get_height()):
162 tile
.set_at((x
, y
), [(255, 255, 80), (80, 255, 255),
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
)
174 self
.subscribe(self
.respond
)
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)
181 texts
= ["Wind Off of Heaven's Lake", u
"澤 天 風"]
183 texts
= ["Pleasure in Strong Penetration", u
"兌 乾 巽"]
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
197 def deactivate(self
):
199 self
.music
.fadeout(500)
201 def respond(self
, event
):
203 if self
.delegate
.compare(event
, "any"):
205 self
.parent
.game_screen
.activate()
208 def activate(self
, incoming
=None):
210 self
.music
.play(-1, 0, 500)
211 scores
= self
.scores
= []
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),
219 if score
== incoming
:
220 surface
= Surface(sprite
.location
.size
)
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) - \
228 scores
.append(sprite
)
234 self
.background
.update()
235 ds
= self
.display_surface
236 for ii
, layer
in enumerate(reversed(self
.layers
)):
238 layer
.move(-.25, -.25)
239 if layer
.location
.right
< ds
.get_width():
240 layer
.move(self
.tile_size
* 16, self
.tile_size
* 16)
243 if layer
.location
.right
< ds
.get_width():
244 layer
.move(self
.tile_size
* 16)
247 if layer
.location
.left
> 0:
248 layer
.move(-self
.tile_size
* 16, -self
.tile_size
* 16)
250 for score
in self
.scores
:
252 self
.caption
.update()
255 class SoundEffect(GameChild
, Sound
):
257 def __init__(self
, parent
, name
, volume
=1.0):
258 GameChild
.__init__(self
, parent
)
259 Sound
.__init__(self
, self
.get_resource("audio", name
))
261 self
.set_volume(volume
)
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
)
270 class GameScreen(GameChild
):
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)]]
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
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
)
300 def set_background(self
):
302 tile
= Surface((w
, h
))
304 palette
= choice(self
.PALETTE
)
305 if self
.previous_palette
!= palette
:
306 self
.previous_palette
= palette
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
))
319 self
.game_over_elapsed
= 0
326 self
.paddles
.set_background()
327 self
.paddles
.reset_position()
328 self
.paddles
.arrange_graticules(0)
329 self
.set_background()
333 self
.introducing
= True
334 self
.introducing_elapsed
= 0
336 def increase_wave(self
):
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()
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()
361 def deactivate(self
):
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()
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()
382 self
.parent
.title
.activate(int(self
.pulp
.score
))
386 self
.display_surface
.blit(self
.background
, (0, 0))
388 font
= Font(self
.get_resource("display", "font"), 10)
389 text
= "WAVE " + str(self
.wave
)
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
401 self
.introducing_elapsed
+= self
.time_filter
. \
402 get_last_frame_duration()
404 font
= Font(self
.get_resource("display", "font"), 10)
405 text
= font
.render("G A M E O V E R", True,
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()
414 if not self
.is_frozen():
416 self
.paddles
.update()
421 class Pulp(GameChild
, Surface
):
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)
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
), \
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])
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
)
475 for explosion
in self
.explosions
:
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
)
500 for phage
in self
.parent
.rails
.phages
:
501 if phage
.get_center()[1] <= self
.rect
.centery
:
502 outgoing
.append(phage
)
504 for explosion
in self
.explosions
:
505 if explosion
.is_hidden():
506 explosion
.get_current_frameset().reset()
507 explosion
.location
.center
= phage
.get_center()
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
:
526 class Explosion(Sprite
):
528 def __init__(self
, parent
):
529 Sprite
.__init__(self
, parent
)
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
)
544 def shift_frame(self
):
545 Sprite
.shift_frame(self
)
546 frameset
= self
.get_current_frameset()
547 if frameset
.current_index
== frameset
.length() - 1:
551 class Link(GameChild
):
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])
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,
575 self
.background
.add_frame(frame
)
576 self
.background
.location
.top
= self
.parent
.pulp
.get_rect().bottom
579 self
.background
.update()
582 class Paddles(GameChild
):
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)))
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)
604 self
.set_background()
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
)
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)
624 hue_base
= randrange(0, 360)
625 for ii
in xrange(count
):
626 frame
= Surface((self
.display_surface
.get_width(), h
), SRCALPHA
)
628 hue
= 30 * (float(hue_ii
) / (count
/ 2)) + hue_base
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(). \
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()
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
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) - \
666 surface
= Surface((image
.get_width() * 2 + margin
, image
.get_height()))
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)
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()
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
, \
695 location
.left
+= ds
.get_width()
697 def reset_throttle(self
):
698 self
.throttle
= [0, 0]
700 def arrange_graticules(self
, index
=None):
702 index
= randrange(1, len(self
.GRATICULES
))
705 states
= self
.GRATICULES
[index
], self
.GRATICULES
[index
+ 1]
707 states
= self
.GRATICULES
[index
], self
.GRATICULES
[index
- 1]
709 states
= self
.GRATICULES
[index
], self
.GRATICULES
[index
]
712 states
= self
.GRATICULES
[index
], self
.GRATICULES
[0]
714 states
= self
.GRATICULES
[0], self
.GRATICULES
[index
]
715 graticule
= self
.graticule
716 graticule
.remove_locations()
718 y
= self
.graticule
.location
.centery
719 for ii
, state
in enumerate(states
):
721 x
= self
.paddles
.location
.left
+ self
.paddle_length
/ 2
723 x
= self
.paddles
.location
.right
- self
.paddle_length
/ 2
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
))
730 graticule
.location
.center
= center
731 graticule
.location
.side
= self
.LEFT
734 location
= graticule
.add_location((0, 0))
736 location
.center
= center
738 def explode_mines(self
):
741 for side
in self
.active_mines
:
744 total_x
+= mine
.location
.centerx
745 mine
.set_frameset("explode")
746 for location
in self
.graticule
.locations
:
749 self
.detonate_mine_fx
.play(float(total_x
) / count
/ \
750 self
.get_display_surface().get_width())
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
]:
765 count
= len(self
.active_mines
[side
])
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)
782 phage
.health
-= 1.5 * (reach
- float(d
)) / reach
784 if phage
not in (r
[0] for r
in outgoing
):
786 if record
[0] == phage
:
790 outgoing
.append((phage
, start
))
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(). \
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
807 print increase
* len(outgoing
) * 10, len(outgoing
)
809 self
.eliminate_phage_fx
.play(float(phage_total_x
) / \
814 for location
in self
.graticule
.locations
:
815 if location
.side
== side
:
817 self
.throttle
[side
] = 0
818 elif self
.throttle
[side
] > 500:
820 for location
in self
.graticule
.locations
:
821 if location
.side
== side
:
823 mine
= choice(self
.mines
)
826 mine
.location
.center
= location
.center
827 mine
.get_current_frameset().reset()
829 self
.active_mines
[side
].append(mine
)
830 total_x
+= mine
.location
.centerx
832 self
.drop_mine_fx
.play(float(total_x
) / \
833 len(self
.active_mines
[side
]) / \
834 self
.get_display_surface(). \
838 for ii
in xrange(len(self
.throttle
)):
839 self
.throttle
[ii
] += self
.time_filter
.get_last_frame_duration()
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
:
875 self
.paddles
.update()
876 self
.background
.update()
879 class Graticule(Sprite
):
881 def __init__(self
, parent
):
882 Sprite
.__init__(self
, parent
, 120)
883 surface
= Surface((14, 14), SRCALPHA
)
884 rect
= surface
.get_rect()
887 inner_color
, outer_color
, bg_color
= Color(0, 0, 0), Color(0, 0, 0), \
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
907 def __init__(self
, parent
):
908 Sprite
.__init__(self
, parent
, 120)
909 surface
= Surface((20, 20), SRCALPHA
)
911 distances
= [4, 2, 2, 1, 0, 1, 2, 2, 4]
912 radii
= [2, 2, 2, 2, 2, 2, 2, 2, 2]
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
))
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)),
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
) * \
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
)
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
)))
950 self
.add_frame(frame
)
951 self
.set_frameset("wait")
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")
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),
972 self
.distance_ii
= distance_ii
975 class Rails(GameChild
, list):
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
983 interpolator
= self
.get_game().interpolator
984 self
.spawn_nodeset
= interpolator
.get_nodeset("spawn")
985 self
.step_nodeset
= interpolator
.get_nodeset("step")
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
= []
995 nodeset
= self
.get_game().interpolator
.get_nodeset("deviation-" + \
999 deviations
.append(nodeset
)
1002 self
.set_deviation()
1005 def set_deviation(self
):
1006 self
.deviation
= choice(self
.deviations
)
1007 self
.set_background()
1010 self
.increase_spawn_rate()
1013 def clear_phages(self
):
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
)
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
1033 modifier
= rail
.get_modifier()
1034 line(background
, color
, (rail
.x
+ px
* modifier
, y
),
1035 (rail
.x
+ dx
* modifier
, y
))
1038 if random() < self
.spawn_rate
:
1039 self
.phages
.append(Phage(self
, choice(self
)))
1041 self
.display_surface
.blit(self
.background
, (0, self
.view_y
),
1043 self
.display_surface
.get_width(), height
))
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
:
1051 class Rail(GameChild
):
1053 def __init__(self
, parent
, x
, mirrored
):
1054 GameChild
.__init__(self
, parent
)
1056 self
.mirrored
= mirrored
1058 def get_modifier(self
):
1059 return -1 if self
.mirrored
else 1
1062 class Phage(Animation
):
1064 TRANSPARENT_COLOR
= (255, 0, 255)
1066 def __init__(self
, parent
, rail
):
1067 Animation
.__init__(self
, parent
, self
.die
, 50)
1071 body
= self
.body
= []
1072 self
.yr
= self
.parent
.parent
.link
.background
.location
.bottom
, \
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
1085 def get_center(self
):
1086 return self
.body
[0].location
.midbottom
1089 for segment
in self
.body
:
1090 alpha
= segment
.alpha
- 48
1092 self
.parent
.phages
.remove(self
)
1096 for _
in xrange(10):
1097 w
, h
= segment
.location
.size
1098 segment
.get_current_frame().set_at((randrange(0, w
),
1100 self
.TRANSPARENT_COLOR
)
1101 segment
.set_alpha(alpha
)
1104 if not self
.is_playing():
1105 step
= self
.parent
.phage_step
* self
.health
1108 for ii
, segment
in sorted(enumerate(self
.body
), key
=lambda b
: b
[0],
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
- \
1117 for segment
in self
.body
:
1119 Animation
.update(self
)
1122 class Oracle(Animation
):
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))
1140 line
.set_colorkey(key
)
1141 line
.fill(key
, (14, 0, 8, 6))
1143 self
.animals
= Animals(self
)
1144 self
.coins
= [Coin(self
, ii
) for ii
in xrange(3)]
1145 self
.register(self
.display_indicator
)
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()
1156 def clear_screens(self
):
1157 for screen
in self
.screens
:
1158 screen
.fill(screen
.get_at((0, 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
:
1173 if not self
.parent
.is_frozen() and not self
.waiting
:
1175 for coin
in self
.coins
:
1177 coin
.set_frameset(0)
1178 if len(self
.hexagram
) == 6:
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
1191 self
.flip_elapsed
+= self
.time_filter
.get_last_frame_duration()
1192 if self
.flip_elapsed
> flip_length
:
1193 self
.coins
[self
.flips
].stop()
1195 self
.flip_elapsed
-= flip_length
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,
1205 # self.line_appears_fx.play()
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
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()
1220 rect
.topleft
= (ow
, oh
)
1222 rect
.topright
= self
.display_surface
.get_width() - ow
, oh
1223 self
.display_surface
.blit(screen
, rect
)
1224 for coin
in self
.coins
:
1226 Animation
.update(self
)
1229 class Animals(GameChild
, list):
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)
1262 self
.in_motion
= None
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
. \
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
1282 location
.centerx
+= self
.get_display_surface().get_width()
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().\
1292 self
.in_motion
.move(dy
=4)
1293 self
.in_motion
.update()
1298 HEADS
, TAILS
= "heads", "tails"
1300 def __init__(self
, parent
, ii
):
1301 Sprite
.__init__(self
, parent
)
1304 for frame_ii
in xrange(w
):
1305 frame
= Surface((w
, w
))
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
1324 self
.side
= self
.HEADS
1326 self
.side
= self
.TAILS
1327 self
.set_frameset(self
.side
)
1332 def __init__(self
, coins
):
1335 if coin
.side
== coin
.HEADS
:
1341 def get_line_index(self
):
1342 return 0 if self
.total
in [7, 9] else 1
1344 def get_binary(self
):
1345 return 0 if self
.total
in [7, 9] else 1