1-10
[hoa] / hair_on_arm / HairOnArm.py
1 # -*- coding: utf-8 -*-
2
3 from random import randint, randrange, random, choice, shuffle
4 from math import pi, sin, cos, atan, copysign, sqrt, tan
5 from os import listdir
6 from os.path import join
7 from glob import glob
8 from copy import copy
9 from array import array
10
11 from pygame import Rect, Surface, mixer
12 from pygame.draw import line, circle, polygon, aaline, aalines
13 from pygame.mixer import find_channel, Sound, set_num_channels
14 from pygame.font import Font
15 from pygame.image import load, save
16 from pygame.locals import *
17
18 from hair_on_arm.pgfw.Game import Game
19 from hair_on_arm.pgfw.GameChild import GameChild
20 from hair_on_arm.pgfw.Sprite import Sprite
21 from hair_on_arm.pgfw.Animation import Animation
22 from hair_on_arm.Samples import Samples
23 from hair_on_arm.SoundEffect import SoundEffect
24
25 class HairOnArm(Game):
26
27 def __init__(self):
28 Game.__init__(self)
29 set_num_channels(16)
30 self.title.activate()
31
32 def set_children(self):
33 Game.set_children(self)
34 self.floor = Floor(self)
35 self.sky = Sky(self)
36 self.gate = Gate(self)
37 self.title = Title(self)
38 self.tour = Tour(self)
39
40 def update(self):
41 self.title.update()
42 self.tour.update()
43
44
45 class Floor(Animation, Rect):
46
47 BUFFER = 4
48
49 def __init__(self, parent):
50 Animation.__init__(self, parent, self.move)
51 self.display_surface = ds = self.get_display_surface()
52 Rect.__init__(self, (0, 0, ds.get_width(), 100))
53 self.bottom = ds.get_height()
54 self.delegate = self.get_game().delegate
55 self.dy_nodeset = self.get_game().interpolator.get_nodeset("floor")
56 self.reset()
57 self.subscribe(self.respond)
58 self.play()
59
60 def move(self):
61 self.y_offset += .5
62 if self.y_offset > self.BUFFER:
63 self.y_offset = 1
64 self.yi_switch = not self.yi_switch
65
66 def reset(self):
67 self.y_offset = 0
68 self.yi_switch = False
69
70 def respond(self, event):
71 if self.delegate.compare(event, "reset-game"):
72 self.reset()
73
74 def update(self):
75 Animation.update(self)
76 ds = self.display_surface
77 ds.set_clip(self)
78 ds.fill((225, 200, 225))
79 buf = self.BUFFER
80 y = self.y_offset
81 step = 58
82 x = range(0, ds.get_width() + step, step)
83 yi = self.yi_switch
84 cx = ds.get_rect().centerx
85 max_angle = pi / 2.1
86 while y < self.h + buf:
87 yw = int(round(self.top + y - buf))
88 dy = self.dy_nodeset.get_y(float(y) / (self.h + buf))
89 for xi in xrange(yi % 2, len(x) - 1, 2):
90 x1_a = tan(abs(x[xi] - cx) / float(cx) * max_angle)
91 x2_a = tan(abs(x[xi + 1] - cx) / float(cx) * max_angle)
92 points = (x[xi] + 1 + copysign(x1_a * y, x[xi] - cx), yw), \
93 (x[xi + 1] - 1 + copysign(x2_a * y, x[xi + 1] - cx),
94 yw), \
95 (x[xi + 1] + copysign(x2_a * (y + dy), x[xi + 1] - cx),
96 yw + dy), \
97 (x[xi] + copysign(x1_a * (y + dy), x[xi] - cx),
98 yw + dy)
99 polygon(ds, (200, 255, 225), points)
100 aalines(ds, (200, 255, 255), True, points)
101 y += dy
102 yi += 1
103 ds.set_clip(None)
104
105
106 class Sky(GameChild):
107
108 def __init__(self, parent):
109 GameChild.__init__(self, parent)
110 self.display_surface = self.get_display_surface()
111 self.background = load(join(self.get_resource("image",
112 "sky"), "Splat.png")). \
113 convert_alpha()
114 self.mountains = load(self.get_resource("image", "mountains")). \
115 convert_alpha()
116 self.mountains_rect = self.mountains.get_rect()
117 self.mountains_rect.bottom = self.parent.floor.top
118 self.set_bodies()
119 self.set_gradient()
120
121 def set_bodies(self):
122 self.bodies = bodies = []
123 paths = glob(join(self.get_resource("image", "sky"), "[0-9]*"))
124 shuffle(paths)
125 start = -16
126 for path in paths:
127 bodies.append(Sprite(self, 120))
128 image = load(path).convert_alpha()
129 tiles = []
130 tw = 32
131 for x in xrange(0, image.get_width(), tw):
132 for y in xrange(0, image.get_height(), tw):
133 tile = Surface((tw, tw), SRCALPHA)
134 tile.blit(image, (-x, -y))
135 tiles.append(tile)
136 for ii in xrange(8):
137 shuffle(tiles)
138 frame = Surface(image.get_size(), SRCALPHA)
139 ii = 0
140 for x in xrange(0, image.get_width(), tw):
141 for y in xrange(0, image.get_height(), tw):
142 frame.blit(tiles[ii], (x, y))
143 ii += 1
144 bodies[-1].add_frame(frame)
145 bodies[-1].location.midleft = start, 100
146 start += bodies[-1].location.w + 32
147 if start > self.display_surface.get_width():
148 break
149
150 def set_gradient(self):
151 ds = self.display_surface
152 self.gradient = gradient = Surface((ds.get_width(),
153 self.parent.floor.top), SRCALPHA)
154 color = Color(0, 0, 0)
155 resolution = 40
156 hue, hs = self.get_hue_range(resolution)
157 y = gradient.get_height() - 1
158 for yi, y in enumerate(xrange(y, y - resolution, -1)):
159 color.hsla = hue % 360, \
160 80 + float(resolution - yi) / resolution * 20, \
161 60 + float(resolution - yi) / resolution * 20, 100
162 gradient.fill(color, (0, y, ds.get_width(), 1))
163 hue += hs
164 resolution = 75
165 hue, hs = self.get_hue_range(resolution, hue)
166 for yi, y in enumerate(xrange(y, y - resolution, -1)):
167 ratio = float(resolution - yi) / resolution
168 color.hsla = hue % 360, 60 + ratio * 20, 50 + ratio * 10, 100
169 gradient.fill(color, (0, y, ds.get_width(), 1))
170 hue += hs
171 resolution = 190
172 hue, hs = self.get_hue_range(resolution, hue)
173 for yi, y in enumerate(xrange(y, y - resolution, -1)):
174 ratio = float(resolution - yi) / resolution
175 color.hsla = hue % 360, 30 + ratio * 30, 30 + ratio * 20, \
176 20 + ratio * 80
177 gradient.fill(color, (0, y, ds.get_width(), 1))
178 hue += hs
179 resolution = y
180 hue, hs = self.get_hue_range(y, hue)
181 for yi, y in enumerate(xrange(y, 0, -1)):
182 ratio = float(resolution - yi) / resolution
183 color.hsla = hue % 360, ratio * 30, ratio * 30, ratio * 20
184 gradient.fill(color, (0, y, ds.get_width(), 1))
185 hue += hs
186
187 def get_hue_range(self, resolution, start=None):
188 if start is None:
189 start = randrange(0, 360)
190 return start, float(randrange(30, 90)) / resolution
191
192 def update(self):
193 self.display_surface.blit(self.background, (0, 0))
194 for body in self.bodies:
195 body.update()
196 self.display_surface.blit(self.gradient, (0, 0))
197 self.display_surface.blit(self.mountains, self.mountains_rect)
198
199
200 class Title(GameChild):
201
202 def __init__(self, parent):
203 GameChild.__init__(self, parent)
204 self.delegate = self.get_game().delegate
205 self.menu = Menu(self, (500, 400), 18,
206 ("level", self.start, range(1, 11), True,
207 self.set_gate_index),
208 ("invert", self.set_invert, ("off", "on"), True),
209 ("records", self.show_records))
210 self.caster = Caster(self)
211 self.deactivate()
212 self.subscribe(self.respond)
213
214 def start(self, index):
215 self.parent.tour.activate()
216 self.deactivate()
217
218 def set_gate_index(self, index):
219 if index == "all":
220 index = 0
221 self.parent.gate.set_image_index(index - 1)
222
223 def set_invert(self, invert):
224 self.parent.invert = False if invert == "off" else True
225
226 def show_records(self):
227 print "show records"
228
229 def deactivate(self):
230 self.active = False
231 self.menu.deactivate()
232
233 def respond(self, event):
234 compare = self.delegate.compare
235 if compare(event, "reset-game"):
236 self.menu.reset()
237 self.activate()
238
239 def activate(self):
240 self.active = True
241 self.menu.activate()
242
243 def update(self):
244 if self.active:
245 self.caster.update()
246 self.get_display_surface().fill((255, 255, 0))
247 self.parent.sky.update()
248 self.parent.floor.update()
249 self.parent.gate.update()
250 self.menu.update()
251
252
253 class Caster(GameChild):
254
255 def __init__(self, parent):
256 GameChild.__init__(self, parent)
257 self.time_filter = self.get_game().time_filter
258 volume = .46
259 self.sounds = sounds = [SoundEffect(self, "caster", volume)]
260 for _ in xrange(2):
261 sound = array("h", sounds[0].get_buffer().raw)
262 start = randrange(0, len(sound) / 2 - 40000)
263 for ii in xrange(start, start + 40000, 3):
264 sound.insert(ii, sound[ii])
265 start = randrange(len(sound) / 2, len(sound) - 40000)
266 for ii in xrange(start, start + 40000, 3):
267 sound.insert(ii, sound[ii])
268 sounds.append(SoundEffect(self, sound, volume))
269 self.reset()
270
271 def reset(self):
272 self.playing = False
273 self.elapsed = 0
274 self.set_gap()
275
276 def set_gap(self):
277 self.gap = randint(2000, 5000)
278
279 def update(self):
280 self.elapsed += self.time_filter.get_last_frame_duration()
281 if not self.playing:
282 if self.elapsed >= self.gap:
283 self.elapsed = 0
284 sound = choice(self.sounds)
285 sound.play(random())
286 self.length = sound.get_length() * 1000
287 self.playing = True
288 elif self.elapsed >= self.length:
289 self.reset()
290
291
292 class Menu(Animation):
293
294 def __init__(self, parent, position, font_size, *args):
295 Animation.__init__(self, parent, self.highlight)
296 self.delegate = self.get_game().delegate
297 self.vertical_sfx = SoundEffect(self, "vertical", .2)
298 self.horizontal_sfx = SoundEffect(self, "horizontal", .56)
299 self.arrange(position, font_size, args)
300 self.subscribe(self.respond)
301 self.reset()
302 self.deactivate()
303 self.play()
304
305 def highlight(self):
306 self.highlit = not self.highlit
307
308 def arrange(self, position, font_size, options):
309 self.elements = elements = []
310 font = Font(self.get_resource("display", "font"), font_size)
311 height = 0
312 width = 0
313 margin = 5
314 for option in options:
315 values = [None] if len(option) < 3 else option[2]
316 value_pairs = []
317 for value in values:
318 text = str(option[0])
319 if value is not None:
320 text += " ‡ " + str(value).upper() + " ‡"
321 value_pairs.append((value, font.render(text, True,
322 (90, 90, 140))))
323 ew, eh = value_pairs[0][1].get_size()
324 height += eh + margin
325 if ew > width:
326 width = ew
327 respond_on_rotate = False if len(option) < 4 else option[3]
328 increment_callback = None if len(option) < 5 else option[4]
329 elements.append(Element(self, option[1], value_pairs,
330 respond_on_rotate, increment_callback))
331 x = position[0] - width / 2
332 y = position[1] - height / 2
333 step = elements[0].values[0][1].get_height() + margin
334 for element in elements:
335 element.set_position(x, y)
336 y += step
337
338 def respond(self, event):
339 if self.active:
340 compare = self.delegate.compare
341 if compare(event, ("up", "down")):
342 self.vertical_sfx.play()
343 self.increment_active_element((-1, 1)[compare(event, "down")])
344 elif compare(event, ("right", "left")):
345 active = self.elements[self.active_element]
346 if len(active.values) > 1:
347 self.horizontal_sfx.play()
348 active.increment_active_value((-1, 1)[compare(event, "right")])
349 elif compare(event, "advance"):
350 self.elements[self.active_element].call()
351
352 def increment_active_element(self, increment):
353 self.active_element += increment
354 if self.active_element < 0:
355 self.active_element = len(self.elements) - 1
356 elif self.active_element >= len(self.elements):
357 self.active_element = 0
358
359 def reset(self):
360 self.active_element = 0
361 self.highlit = True
362 for element in self.elements:
363 element.reset()
364
365 def deactivate(self):
366 self.active = False
367
368 def activate(self):
369 self.active = True
370
371 def update(self):
372 if self.active:
373 Animation.update(self)
374 for element in self.elements:
375 element.update(element == self.elements[self.active_element] \
376 and self.highlit)
377
378
379 class Element(GameChild):
380
381 def __init__(self, parent, callback, values, respond_on_increment,
382 increment_callback):
383 GameChild.__init__(self, parent)
384 self.display_surface = self.get_display_surface()
385 self.callback = callback
386 self.values = values
387 self.respond_on_increment = respond_on_increment
388 self.increment_callback = increment_callback
389 self.mask = mask = Surface(self.values[0][1].get_size())
390 mask.fill((255, 255, 255))
391 self.reset()
392
393 def reset(self):
394 self.active_value = 0
395 if self.respond_on_increment:
396 self.call(True)
397
398 def increment_active_value(self, increment):
399 self.active_value += increment
400 if self.active_value < 0:
401 self.active_value = len(self.values) - 1
402 elif self.active_value >= len(self.values):
403 self.active_value = 0
404 if self.respond_on_increment:
405 self.call(True)
406
407 def call(self, increment=False):
408 callback = self.callback
409 if increment and self.increment_callback:
410 callback = self.increment_callback
411 if callback:
412 value = self.values[self.active_value][0]
413 if value is not None:
414 callback(value)
415 else:
416 callback()
417
418 def set_position(self, x, y):
419 self.position = x, y
420
421 def update(self, highlight=False):
422 surface = self.values[self.active_value][1]
423 if highlight:
424 surface = surface.copy()
425 surface.blit(self.mask, (0, 0), None, BLEND_RGB_MAX)
426 self.display_surface.blit(surface, self.position)
427
428
429 class Tour(GameChild):
430
431 RADIUS = 220
432
433 def __init__(self, parent):
434 GameChild.__init__(self, parent)
435 self.delegate = self.get_game().delegate
436 self.music_elapsed = 0
437 self.music_elapsed_next = 0
438 self.axes = Axes(self)
439 self.nodes = [Node(self) for _ in xrange(12)]
440 self.loops = [Loop(self, choice(range(8, 12))) for _ in xrange(16)]
441 self.drums = Sound(self.get_resource("audio", "plastic"))
442 self.drums.set_volume(.85)
443 self.subscribe(self.respond)
444 self.deactivate()
445
446 def respond(self, event):
447 if self.delegate.compare(event, "reset-game"):
448 self.deactivate()
449
450 def deactivate(self):
451 self.active = False
452 fade = 500
453 for loop in self.loops:
454 loop.fadeout(fade)
455 self.drums.fadeout(fade)
456
457 def activate(self):
458 self.active = True
459 self.drums.play(-1)
460
461 def update(self):
462 if self.active:
463 ds = self.get_display_surface()
464 ds.fill((0, 0, 0))
465 self.parent.sky.update()
466 self.parent.floor.update()
467 x, y, max_magnitude = self.axes.update()
468 for node in self.nodes:
469 node.rotate(x, y, max_magnitude)
470 drawn = False
471 for node in sorted(self.nodes, key=lambda n: n.z):
472 if node.z >= 0 and not drawn:
473 self.parent.gate.update()
474 drawn = True
475 node.update()
476 self.music_elapsed += self.get_game().time_filter. \
477 get_last_frame_duration()
478 if self.music_elapsed > self.music_elapsed_next:
479 for loop in self.loops:
480 loop.stop()
481 for ii in xrange(3):
482 loop = choice(self.loops)
483 loop.set_volume(choice([x * .02 for x in xrange(2, 20)]))
484 channel = loop.play(-1)
485 channel.set_volume(ii == 0 or ii == 1, ii == 1 or ii == 2)
486 self.music_elapsed = 0
487 self.music_elapsed_next = choice([x * 40 for x in xrange(0, 4)])
488
489
490 class Axes(GameChild):
491
492 def __init__(self, parent):
493 GameChild.__init__(self, parent)
494 self.display_surface = self.get_display_surface()
495 self.delegate = self.get_game().delegate
496 self.directions = {}
497 for ii, name in enumerate(("up", "right", "down", "left")):
498 self.directions[name] = Direction(self, pi / 2 * ii)
499 self.waiting = []
500 self.rect = rect = Rect(0, 0, 80, 80)
501 rect.center = 597, 436
502 self.background = background = Sprite(self, 500)
503 color = Color(0, 0, 0)
504 frame_count = 16
505 gradient_count = 8
506 for ii in xrange(frame_count):
507 surface = Surface((rect.w * 2 + 65, rect.h * 2 + 65), SRCALPHA)
508 for jj in xrange(gradient_count):
509 ratio = float(jj + 1) / gradient_count
510 color.hsla = float(ii) / frame_count * 360, 80, 60, ratio * 100
511 circle(surface, color, surface.get_rect().center, \
512 int(round((1 - ratio) * (surface.get_width() / 2))))
513 background.add_frame(surface)
514 background.location.center = self.display_surface.get_rect().bottomright
515 self.subscribe(self.respond)
516
517 def respond(self, event):
518 if self.parent.active:
519 compare = self.delegate.compare
520 names = ["up", "right", "down", "left"]
521 for direction in names:
522 if compare(event, direction) or compare(event, direction, True):
523 opposite = names[(names.index(direction) + 2) % len(names)]
524 if event.cancel:
525 self.directions[direction].on = False
526 if direction in self.waiting:
527 self.waiting.remove(direction)
528 if opposite in self.waiting:
529 self.directions[opposite].on = True
530 else:
531 if not self.directions[opposite].on:
532 self.directions[direction].on = True
533 elif direction not in self.waiting:
534 self.waiting.append(direction)
535
536 def update(self):
537 self.background.update()
538 tx, ty, max_magnitude = 0, 0, 0
539 invert = (-1, 1)[self.parent.parent.invert]
540 ds = self.display_surface
541 cx, cy = self.rect.center
542 r = self.rect.w / 2
543 for direction in self.directions.itervalues():
544 magnitude = direction.get_magnitude()
545 direction.update()
546 dx = sin(direction.angle) * magnitude * invert
547 dy = -cos(direction.angle) * magnitude * invert
548 line(ds, (0, 0, 255), (cx, cy), (cx + dx * r * invert,
549 cy + dy * r * invert), 1)
550 tx += dx
551 ty += dy
552 if magnitude > max_magnitude:
553 max_magnitude = magnitude
554 angle = atan(float(tx) / ty) if ty else 0
555 x = copysign(sin(angle) * max_magnitude, tx * invert)
556 y = copysign(cos(angle) * max_magnitude, ty * invert)
557 line(ds, (255, 0, 0), (cx, cy), (cx + x * r, cy + y * r), 3)
558 angle = atan(float(ty) / tx) if tx else pi / 2
559 x = copysign(sin(angle), ty)
560 y = copysign(cos(angle), -tx)
561 length = 10
562 line(ds, (255, 255, 0), (cx, cy), (cx + x * length, cy + y * length), 1)
563 return x, y, max_magnitude
564
565
566 class Direction(GameChild):
567
568 def __init__(self, parent, angle):
569 GameChild.__init__(self, parent)
570 self.angle = angle
571 self.on = False
572 self.attack = Transition(self, "roll-start")
573 self.release = Transition(self, "roll-end")
574
575 def get_magnitude(self):
576 return self.attack.get() if self.on else self.release.get()
577
578 def update(self):
579 self.attack.update(self.on)
580 self.release.update(not self.on)
581
582
583 class Transition(GameChild):
584
585 def __init__(self, parent, name):
586 GameChild.__init__(self, parent)
587 self.nodeset = self.get_game().interpolator.get_nodeset(name)
588 self.elapsed = 0
589 self.time_filter = self.get_game().time_filter
590
591 def get(self):
592 return self.nodeset.get_y(self.elapsed)
593
594 def update(self, active):
595 if active and self.elapsed < self.nodeset[-1].x:
596 self.elapsed += self.time_filter.get_last_frame_duration()
597 if self.elapsed > self.nodeset[-1].x:
598 self.elapsed = self.nodeset[-1].x
599 elif not active and self.elapsed > 0:
600 self.elapsed -= self.time_filter.get_last_frame_duration()
601 if self.elapsed < 0:
602 self.elapsed = 0
603
604
605 class Node(Sprite):
606
607 def __init__(self, parent):
608 Sprite.__init__(self, parent)
609 magnitude = random() * .8 + .2
610 self.x = sqrt(random() * magnitude) * choice((1, -1))
611 self.y = sqrt(random() * (magnitude - self.x ** 2)) * choice((1, -1))
612 self.z = sqrt(magnitude - self.x ** 2 - self.y ** 2) * choice((1, -1))
613
614 def rotate(self, x, y, magnitude):
615 k = x, y, 0
616 v = self.x, self.y, self.z
617 th = magnitude * .125
618 c = cos(th)
619 vs = [cc * c for cc in v]
620 s = sin(th)
621 vc = [(k[1] * v[2] - k[2] * v[1]) * s,
622 (k[2] * v[0] - k[0] * v[2]) * s,
623 (k[0] * v[1] - k[1] * v[0]) * s]
624 dp = sum(k[ii] * v[ii] for ii in xrange(len(k)))
625 kd = [cc * dp * (1 - c) for cc in k]
626 self.x, self.y, self.z = (vs[ii] + vc[ii] + kd[ii] for ii in \
627 xrange(len(v)))
628
629 def update(self):
630 ds = self.get_display_surface()
631 cx, cy = ds.get_rect().center
632 circle(ds, (randint(0, 255), randint(0, 255), randint(0, 255)),
633 (int(round(cx + self.x * self.parent.RADIUS)),
634 int(round(cy + self.y * self.parent.RADIUS))),
635 int(round(self.z * 12 + 16)))
636
637
638 class Loop(Samples):
639
640 def __init__(self, parent, frequency):
641 self.frequency = frequency
642 Samples.__init__(self)
643 self.set_volume(.25)
644
645 def build(self):
646 samples = self.get_empty_array(2 ** self.frequency)
647 t = 0
648 while t < len(samples):
649 ii = 0
650 if t % 3 == 0:
651 while ii < [13, 21, 34][t % 3]:
652 samples[t] = int(choice((sin, cos))(1.0 / (ii + 1) * \
653 (pi / 2)) * \
654 self.amplitude)
655 t += 1
656 if t == len(samples):
657 break
658 ii += 1
659 elif t % 3 == 1:
660 while ii < choice([2 ** x for x in xrange(3, 6)]):
661 samples[t] = int(self.amplitude * 2 / ((ii % 16) + 1) - \
662 self.amplitude)
663 t += 1
664 if t == len(samples):
665 break
666 ii += 1
667 else:
668 while ii < [x ** 2 for x in xrange(5, 9)][t % 4]:
669 samples[t] = int(choice((1, -1)) * \
670 choice([self.amplitude / float(x) for \
671 x in xrange(2, 10)]))
672 t += 1
673 if t == len(samples):
674 break
675 ii += 1
676 return samples
677
678
679 class Gate(Animation):
680
681 def __init__(self, parent):
682 Animation.__init__(self, parent, self.show_next_image, 200)
683 self.display_surface = ds = self.get_display_surface()
684 root = self.get_resource("image", "gate")
685 self.images = images = []
686 for name in sorted(listdir(root)):
687 images.append(load(join(root, name)))
688 self.image_index = 0
689 self.image_rect = image_rect = images[0].get_rect()
690 image_rect.center = ds.get_rect().center
691 bevel_width = 2
692 frame_width = 3
693 border_width = 1
694 self.border = border = Sprite(self, 200)
695 for ii in xrange(3):
696 inflation = bevel_width * 2 + frame_width * 2 + border_width * 2
697 surface = Surface(image_rect.inflate(inflation, inflation).size)
698 surface.fill(((255, 255, 0), (255, 0, 0), (34, 139, 34))[ii])
699 border.add_frame(surface)
700 border.location.center = image_rect.center
701 self.frame_background = load(self.get_resource("image", "hand"))
702 inflation = bevel_width * 2 + frame_width * 2
703 self.frame_surface = Surface(image_rect.inflate(inflation, inflation). \
704 size)
705 self.frame_rect = self.frame_surface.get_rect()
706 self.frame_rect.center = image_rect.center
707 self.frame_background_offset = 0
708 self.bevel_surface = Surface(image_rect.inflate(bevel_width * 2,
709 bevel_width * 2).size)
710 self.bevel_rect = self.bevel_surface.get_rect()
711 self.bevel_rect.center = image_rect.center
712 self.bevel_surface.fill((150, 212, 150))
713 points = (0, 0), (self.bevel_rect.w - 1, 0), (0, self.bevel_rect.h - 1)
714 polygon(self.bevel_surface, (40, 120, 40), points)
715 aaline(self.bevel_surface, (150, 212, 150), (0, self.bevel_rect.h - 1),
716 (self.bevel_rect.w - 1, 0))
717 save(self.bevel_surface, "/tmp/bevel_surface.png")
718 self.screen = screen = Sprite(self, 100)
719 screen.set_alpha(40)
720 components = []
721 for ii in xrange(3):
722 surface = Surface((4, 2))
723 surface.fill(((255, 0, 0), (0, 255, 0), (0, 0, 255))[ii])
724 components.append(surface)
725 component_rect = components[0].get_rect()
726 for frame_ii in xrange(3):
727 frame = Surface(image_rect.size)
728 for yi, y in enumerate(xrange(0, image_rect.h, component_rect.h)):
729 offset = (0, -2)[y % 2]
730 for xi, x in enumerate(xrange(offset, image_rect.w,
731 component_rect.w)):
732 frame.blit(components[(frame_ii + yi + xi) % 3], (x, y))
733 screen.add_frame(frame)
734 screen.location.center = image_rect.center
735
736 def show_next_image(self):
737 self.image_index += 1
738 if self.image_index == len(self.images):
739 self.image_index = 0
740
741 def set_image_index(self, index):
742 if index == -1:
743 self.play()
744 else:
745 self.halt()
746 self.image_index = index
747
748 def update(self):
749 Animation.update(self)
750 self.border.update()
751 self.frame_background_offset -= 2
752 if self.frame_background_offset < -self.frame_background.get_width():
753 self.frame_background_offset += self.frame_background.get_width()
754 self.frame_surface.blit(self.frame_background,
755 (self.frame_background_offset, 0))
756 self.frame_surface.blit(self.frame_background,
757 (self.frame_background_offset + \
758 self.frame_background.get_width(), 0))
759 self.display_surface.blit(self.frame_surface, self.frame_rect)
760 self.display_surface.blit(self.bevel_surface, self.bevel_rect)
761 self.display_surface.blit(self.images[self.image_index],
762 self.image_rect)
763 self.screen.update()