f71ab0e5862bb1754b8286806c84a9219829ab0b
[pp] / PictureProcessing.py
1 from os import makedirs, listdir
2 from os.path import basename, join, exists
3 from random import random, randrange, choice, randint
4 from glob import glob
5 from array import array
6 from sys import argv
7
8 from pygame import PixelArray, Surface, Color, joystick, version, mixer, draw
9 from pygame.event import clear, Event
10 from pygame.image import load, save
11 from pygame.transform import flip, scale
12 from pygame.mixer import Sound, set_num_channels, get_num_channels
13 from pygame.locals import *
14
15 from lib.pgfw.pgfw.Game import Game
16 from lib.pgfw.pgfw.GameChild import GameChild
17 from lib.pgfw.pgfw.Animation import Animation
18 from lib.pgfw.pgfw.Sprite import Sprite, RainbowSprite
19 from lib.pgfw.pgfw.Vector import Vector
20 from lib.pgfw.pgfw.extension import (get_color_swapped_surface, get_delta,
21 get_busy_channel_count)
22
23 class PictureProcessing(Game):
24
25 SCALE = 3
26 CHANNEL_COUNT = 16
27 KEYBOARD, GAMEPAD = xrange(2)
28
29 def __init__(self):
30 Game.__init__(self)
31 mixer.init(44100, -16, self.CHANNEL_COUNT, 4096)
32 self.get_configuration().type_declarations.add("bool", "mouse", "resets")
33 self.sound_effects = SoundEffects(self)
34 self.glyphs = Glyphs(self)
35 self.editor = Editor(self)
36 self.levels = Levels(self)
37 self.interface = Interface(self)
38 self.title = Title(self)
39 self.introduction = Introduction(self)
40 self.ending = Ending(self)
41 self.subscribe(self.respond)
42 self.subscribe(self.respond, MOUSEBUTTONDOWN)
43 self.reset()
44 clear()
45 if self.check_command_line("-wallpaper"):
46 self.write_wallpaper()
47
48 def write_wallpaper(self):
49 index = argv.index("--wallpaper")
50 width, height = map(int, argv[index + 1:index + 3])
51 wallpaper = ScrollingBackground(self, size=(width, height))
52 surface = wallpaper.get_current_frame()
53 if argv[index + 3] == "-":
54 from tempfile import mkstemp
55 from sys import stdout
56 from os import unlink
57 path = mkstemp(".png")[1]
58 save(surface, path)
59 print path
60 else:
61 save(surface, argv[index + 3])
62 self.delegate.post("quit")
63
64 def respond(self, event):
65 if self.delegate.compare(event, "reset-game") or \
66 (self.get_configuration("mouse", "resets") and event.type == MOUSEBUTTONDOWN and
67 (1 <= event.button <= 3)):
68 self.reset()
69 elif self.delegate.compare(event, "context"):
70 if not self.editor.active:
71 self.editor.activate()
72 else:
73 self.editor.deactivate()
74
75 def reset(self):
76 self.title.reset()
77 self.levels.reset()
78 self.interface.reset()
79 self.introduction.reset()
80 self.ending.reset()
81 self.editor.reset()
82
83 def get_progress(self):
84 return map(int, open(self.get_resource("progress")).read().split())
85
86 def write_progress(self, level_index, swap_count, update=False):
87 if update:
88 swap_count += self.get_progress()[1]
89 open(self.get_resource("progress"), "w").write("%s %s\n" % (str(level_index),
90 str(swap_count)))
91
92 def select_text(self, texts):
93 return texts[self.is_gamepad_mode()]
94
95 def is_gamepad_mode(self):
96 return not self.check_command_line("-keyboard") and joystick.get_count()
97
98 def update(self):
99 if self.editor.active:
100 self.editor.update()
101 else:
102 self.introduction.update()
103 self.title.update()
104 self.levels.update()
105 self.interface.update()
106 self.ending.update()
107
108
109 class Editor(GameChild):
110
111 TILE_SIZE = 8
112 PIXEL_ZOOM = 32
113 CANVAS_SCALE = 40
114 LABEL_EDITOR_HEADING = "EDITOR"
115 LABEL_EXIT = "ESC: EXIT", "B: EXIT"
116 LABEL_EDIT = "ENTER: EDIT", "A: EDIT"
117 LABEL_TILES = "\\24 EDIT TILES"
118 LABEL_PLAY = "TEST \\26"
119 LABEL_PAINT_ARROW = "\\25 PLACE TILES"
120 LABEL_MUSIC = "\\27 MUSIC"
121 LABEL_SAVE_EXIT = "ESC: SAVE", "B: SAVE"
122 LABEL_HIDE_MENU = "ENTER: HIDE", "A: HIDE"
123 LABEL_BACK = "ESC: BACK", "B: BACK"
124 LABEL_PAINT = "Z: PAINT", "A: PAINT"
125 LABEL_NEW = "ENTER: NEW", "A: NEW"
126 LABEL_SELECT = "ENTER: SELECT", "A: SELECT"
127 LABEL_DRAG = "Z: DRAG BOX", "A: DRAG BOX"
128 LABEL_SWAP = "ENTER: SWAP", "A: SWAP"
129 LABEL_SOLVED = "SOLVED"
130 LABEL_LEFT = "\\27"
131 LABEL_RIGHT = "\\26"
132 HEADING_MARGIN = 60
133 BUTTONS_MARGIN = 60
134 VIEW_MARGIN = 120
135 DIRECTIONS_MARGIN = 0
136 ALIGN_ONE_THIRD_BOTTOM, ALIGN_TWO_THIRDS_BOTTOM = range(2)
137 TILE_BAR_PADDING = 10
138 TILE_BAR_BACKGROUND = 0, 0, 0
139 TILE_BAR_SCALE = 3
140 PALETTE_WIDTH = 100
141 ZFILL_FILE = 5
142
143 def __init__(self, parent):
144 GameChild.__init__(self, parent)
145 self.brush_position = Vector()
146 self.subscribe(self.respond)
147 self.levels = []
148 self.painting = []
149 for _ in xrange(self.TILE_SIZE):
150 self.painting.append([0] * self.TILE_SIZE)
151 self.at_level_select = True
152 self.at_view = False
153 self.at_tile_bar = False
154 self.at_tile_edit = False
155 self.at_palette = False
156 self.at_box = False
157 self.at_test = False
158 self.level_index = 0
159 self.tile_index = 0
160 self.palette_index = 0
161 self.loaded = False
162 self.menu_hidden = False
163 self.paint_pressed = False
164
165 def reset(self):
166 self.deactivate()
167
168 def activate(self):
169 self.active = True
170 if not self.loaded:
171 self.load()
172 self.paint_pressed = False
173
174 def load(self):
175 self.read_levels()
176 glyphs = self.get_game().glyphs
177 dsr = self.get_display_surface().get_rect()
178 self.editor_heading = self.get_label(self.LABEL_EDITOR_HEADING, True)
179 self.editor_heading.location.midtop = dsr.centerx, self.HEADING_MARGIN
180 self.arrows = Sprite(self), Sprite(self)
181 self.background = ScrollingBackground(self, self.levels[-1].tiles)
182 self.level_select_exit = self.get_label(
183 self.LABEL_EXIT, align=self.ALIGN_ONE_THIRD_BOTTOM,
184 margin=self.BUTTONS_MARGIN)
185 self.label_edit_level = self.get_label(
186 self.LABEL_EDIT, align=self.ALIGN_TWO_THIRDS_BOTTOM,
187 margin=self.BUTTONS_MARGIN)
188 self.view_exit = self.get_label(
189 self.LABEL_SAVE_EXIT, align=self.ALIGN_ONE_THIRD_BOTTOM,
190 margin=self.VIEW_MARGIN)
191 self.view_hide = self.get_label(
192 self.LABEL_HIDE_MENU, align=self.ALIGN_TWO_THIRDS_BOTTOM,
193 margin=self.VIEW_MARGIN)
194 self.label_back = self.get_label(
195 self.LABEL_BACK, align=self.ALIGN_ONE_THIRD_BOTTOM,
196 margin=self.VIEW_MARGIN)
197 self.label_back_box = self.get_label(
198 self.LABEL_BACK, align=self.ALIGN_ONE_THIRD_BOTTOM,
199 margin=self.VIEW_MARGIN)
200 self.label_paint = self.get_label(
201 self.LABEL_PAINT, align=self.ALIGN_TWO_THIRDS_BOTTOM,
202 margin=self.VIEW_MARGIN)
203 self.label_edit_view = self.get_label(
204 self.LABEL_EDIT, align=self.ALIGN_TWO_THIRDS_BOTTOM,
205 margin=self.VIEW_MARGIN)
206 self.label_new = self.get_label(
207 self.LABEL_NEW, align=self.ALIGN_TWO_THIRDS_BOTTOM,
208 margin=self.VIEW_MARGIN)
209 self.label_select = self.get_label(
210 self.LABEL_SELECT, align=self.ALIGN_TWO_THIRDS_BOTTOM,
211 margin=self.VIEW_MARGIN)
212 self.label_drag = self.get_label(
213 self.LABEL_DRAG, align=self.ALIGN_TWO_THIRDS_BOTTOM,
214 margin=self.VIEW_MARGIN)
215 self.label_left = self.get_label(self.LABEL_LEFT, True, red=200)
216 self.label_left.location.midleft = dsr.left + self.BUTTONS_MARGIN, dsr.centery
217 self.label_right = self.get_label(self.LABEL_RIGHT, True, red=200)
218 self.label_right.location.midright = dsr.right - self.BUTTONS_MARGIN, dsr.centery
219 self.label_swap = self.get_label(
220 self.LABEL_SWAP, align=self.ALIGN_TWO_THIRDS_BOTTOM,
221 margin=self.VIEW_MARGIN)
222 self.label_solved = self.get_label(self.LABEL_SOLVED)
223 self.label_solved.location.bottomright = dsr.bottomright
224 self.arrow_tiles = self.get_label(self.LABEL_TILES)
225 self.arrow_tiles.location.midtop = dsr.midtop
226 self.arrow_play = self.get_label(self.LABEL_PLAY)
227 self.arrow_play.location.midright = dsr.midright
228 self.arrow_paint = self.get_label(self.LABEL_PAINT_ARROW)
229 self.arrow_paint.location.midbottom = dsr.midbottom
230 self.arrow_music = self.get_label(self.LABEL_MUSIC)
231 self.arrow_music.location.midleft = dsr.midleft
232 self.tile_bar = Sprite(self)
233 bar_tile_height = self.get_current_level().original_tiles[0].get_width() * \
234 self.TILE_BAR_SCALE
235 frame = Surface((dsr.w, bar_tile_height + self.TILE_BAR_PADDING * 2))
236 frame.fill(self.TILE_BAR_BACKGROUND)
237 self.tile_bar.add_frame(frame)
238 self.level_select_cursor = self.get_cursor(
239 *self.get_current_level().preview.get_size())
240 self.level_select_cursor.location.center = dsr.center
241 self.tile_cursor = self.get_cursor(
242 *([self.TILE_SIZE * self.TILE_BAR_SCALE] * 2))
243 self.paint_cursor = self.get_cursor(*([self.CANVAS_SCALE] * 2))
244 self.palette_cursor = self.get_cursor()
245 self.box_cursor = self.get_cursor()
246 new_tile = self.new_tile = Surface([self.TILE_SIZE] * 2)
247 new_tile.fill((255, 255, 255))
248 for x in xrange(new_tile.get_width()):
249 for y in xrange(new_tile.get_height()):
250 if 3 <= x <= 4 and y != 0 and y != 7:
251 new_tile.set_at((x, y), (0, 0, 0))
252 elif 3 <= y <= 4 and x != 0 and x != 7:
253 new_tile.set_at((x, y), (0, 0, 0))
254 self.build_default_palette()
255 self.box = FlashingCursor(self, *([self.get_level_tile_size()] * 2))
256 self.loaded = True
257 self.current_level_is_default = False
258
259 def get_level_tile_size(self):
260 return self.TILE_SIZE * PictureProcessing.SCALE
261
262 def build_default_palette(self):
263 self.default_palette = [Color(255, 255, 255), Color(0, 0, 0)]
264 for hue in xrange(0, 360 - 45, 45):
265 for lightness in xrange(25, 100, 25):
266 color = Color(0, 0, 0)
267 color.hsla = hue, 100, lightness, 100
268 self.default_palette.append(color)
269
270 def get_label(self, text, rainbow=False, align=None, margin=0, red=80):
271 if isinstance(text, tuple):
272 text = self.get_game().select_text(text)
273 surface = self.get_game().glyphs.get_surface_from_text(
274 text, background=([0, red][rainbow], 0, 0))
275 if rainbow:
276 label = RainbowSprite(self, surface)
277 else:
278 label = Sprite(self)
279 label.add_frame(surface)
280 if align is not None:
281 dsr = self.get_display_surface().get_rect()
282 if align == self.ALIGN_ONE_THIRD_BOTTOM:
283 label.location.midbottom = dsr.centerx / 2, dsr.h - margin
284 elif align == self.ALIGN_TWO_THIRDS_BOTTOM:
285 label.location.midbottom = \
286 dsr.centerx / 2 + dsr.centerx, dsr.h - margin
287 return label
288
289 def read_levels(self):
290 levels = self.levels = []
291 for path in sorted(glob(join(self.get_new_levels_path(), "*"))):
292 level = Level(self)
293 if level.load(Level.LOAD_GRID_FILE, path):
294 levels.append(level)
295 else:
296 print "Failed to load level %s" % path
297 self.add_default_level()
298
299 def add_default_level(self):
300 self.levels.append(self.get_default_level())
301
302 def get_default_level(self):
303 level = Level(self)
304 level.load()
305 return level
306
307 def get_default_tile_path(self):
308 return join(self.get_tiles_path(),
309 self.get_configuration("editor", "default-tile-file-name"))
310
311 def get_tiles_path(self):
312 config = self.get_configuration("editor")
313 return join(config["root-directory"], config["tiles-directory"])
314
315 def get_new_levels_path(self):
316 config = self.get_configuration("editor")
317 return join(config["root-directory"], config["levels-directory"],
318 config["new-directory"])
319
320 def deactivate(self):
321 self.active = False
322
323 def respond(self, event):
324 if self.active:
325 compare = self.get_delegate().compare
326 dx, dy = 0, 0
327 if compare(event, "action"):
328 if self.at_level_select:
329 self.at_level_select = False
330 self.at_view = True
331 self.menu_hidden = False
332 if self.level_index == len(self.levels) - 1:
333 self.current_level_is_default = True
334 elif self.at_view:
335 self.menu_hidden = not self.menu_hidden
336 elif self.at_tile_bar:
337 if self.add_tile_selected():
338 tile = Surface([self.TILE_SIZE] * 2)
339 tile.fill((0, 0, 0))
340 self.get_current_level().add_tile(tile)
341 if self.current_level_is_default:
342 self.current_level_is_default = False
343 self.add_default_level()
344 self.at_tile_edit = True
345 self.at_tile_bar = False
346 elif self.at_palette:
347 self.at_palette = False
348 self.at_tile_edit = True
349 if compare(event, "cancel"):
350 if self.at_level_select:
351 self.deactivate()
352 elif self.at_view:
353 self.at_view = False
354 self.at_level_select = True
355 self.tile_index = 0
356 self.palette_index = 0
357 elif self.at_tile_bar:
358 self.at_tile_bar = False
359 self.at_view = True
360 elif self.at_tile_edit:
361 self.at_tile_edit = False
362 self.at_tile_bar = True
363 elif self.at_palette:
364 self.at_palette = False
365 self.at_tile_edit = True
366 elif self.at_box:
367 self.at_box = False
368 self.at_view = True
369 elif self.at_test and self.get_game().interface.closed:
370 self.at_test = False
371 self.at_view = True
372 self.get_current_level().set_swap_status()
373 if compare(event, "right"):
374 if self.at_level_select:
375 self.level_index += 1
376 if self.level_index >= len(self.levels):
377 self.level_index = 0
378 if self.at_tile_bar:
379 self.tile_index += 1
380 if self.tile_index == Interface.MAX_TILE_COUNT or \
381 self.tile_index > len(self.get_current_level().tiles):
382 self.tile_index = 0
383 if self.at_tile_edit or self.at_box:
384 dx = 1
385 if self.at_tile_edit:
386 if self.brush_position.x == self.TILE_SIZE - 1:
387 self.at_tile_edit = False
388 self.at_palette = True
389 if self.palette_index_is_at_left():
390 self.wrap_palette_index()
391 elif self.at_palette:
392 if self.palette_index_is_at_left():
393 self.at_palette = False
394 self.at_tile_edit = True
395 else:
396 self.wrap_palette_index()
397 if self.at_view:
398 self.at_view = False
399 self.at_test = True
400 self.get_current_level().set_swap_status(True)
401 self.get_game().interface.setup()
402 elif compare(event, "down"):
403 if self.at_tile_edit or self.at_box:
404 dy = 1
405 if self.at_palette:
406 self.palette_index += 1
407 if self.at_view:
408 self.at_view = False
409 self.at_box = True
410 elif compare(event, "left"):
411 if self.at_level_select:
412 self.level_index -= 1
413 if self.level_index < 0:
414 self.level_index = len(self.levels) - 1
415 if self.at_tile_bar:
416 self.tile_index -= 1
417 if self.tile_index < 0:
418 self.tile_index = min(
419 Interface.MAX_TILE_COUNT - 1,
420 len(self.get_current_level().tiles))
421 if self.at_tile_edit or self.at_box:
422 dx = -1
423 if self.at_tile_edit:
424 if self.brush_position.x == 0:
425 self.at_tile_edit = False
426 self.at_palette = True
427 if not self.palette_index_is_at_left():
428 self.wrap_palette_index()
429 elif self.at_palette:
430 if not self.palette_index_is_at_left():
431 self.at_palette = False
432 self.at_tile_edit = True
433 else:
434 self.wrap_palette_index()
435 elif compare(event, "up"):
436 if self.at_view:
437 self.at_view = False
438 self.at_tile_bar = True
439 if self.at_tile_edit or self.at_box:
440 dy = -1
441 if self.at_palette:
442 self.palette_index -= 1
443 elif compare(event, "paint"):
444 self.paint_pressed = True
445 if self.at_box:
446 self.set_box_anchor()
447 if self.at_tile_edit and self.palette_index < len(self.get_full_palette()):
448 self.paint_tile()
449 elif compare(event, "paint", cancel=True):
450 self.paint_pressed = False
451 if self.at_box:
452 self.stamp()
453 self.box.size = Vector(*([self.get_level_tile_size()] * 2))
454 if dx != 0 or dy != 0:
455 if self.at_box:
456 if self.paint_pressed:
457 self.grow_box(x=dx, y=dy)
458 else:
459 self.move_box(x=dx, y=dy)
460 if self.at_tile_edit:
461 self.move_brush_position(dx, dy)
462 if self.paint_pressed and self.palette_index < len(self.get_full_palette()):
463 self.paint_tile()
464 if self.palette_index >= self.get_palette_cell_count():
465 self.palette_index = 0
466 elif self.palette_index < 0:
467 self.palette_index = self.get_palette_cell_count() - 1
468
469 def stamp(self):
470 size = self.get_level_tile_size()
471 x = self.box.location.left / size
472 y = self.box.location.top / size
473 w = self.box.location.w / size
474 h = self.box.location.h / size
475 for xi in xrange(x, x + w):
476 for yi in xrange(y, y + h):
477 self.get_current_level().grid[xi][yi] = self.tile_index
478 self.box.location.topleft = [size * coordinate for coordinate in self.box_corner]
479 self.get_current_level().set_preview()
480 self.save_level()
481
482 def save_level(self):
483 config = self.get_configuration("editor")
484 new_levels_directory = self.get_new_levels_path()
485 if not exists(new_levels_directory):
486 makedirs(new_levels_directory)
487 grid = self.get_current_level().grid
488 level = self.get_current_level()
489 if level.path is None:
490 directories = sorted(listdir(new_levels_directory))
491 index = len(directories) - 1
492 while True:
493 if index < 0:
494 level.path = join(new_levels_directory, "0" * self.ZFILL_FILE)
495 break
496 elif directories[index].isdigit():
497 level.path = join(
498 new_levels_directory,
499 str(int(directories[index]) + 1).zfill(self.ZFILL_FILE))
500 makedirs(level.path)
501 break
502 index -= 1
503 level_file = open(join(level.path, self.get_configuration("editor", "level-file-name")), "w")
504 for jj in xrange(len(grid[0])):
505 for ii in xrange(len(grid)):
506 level_file.write(str(hex(grid[ii][jj]))[-1])
507 level_file.write("\n")
508 for ii, tile in enumerate(level.original_tiles):
509 save(tile, join(level.path, "%s.png" % str(ii).zfill(2)))
510
511 def set_box_anchor(self):
512 size = self.get_level_tile_size()
513 self.box_anchor = Vector(self.box.location.left / size,
514 self.box.location.top / size)
515 self.box_corner = Vector(*self.box_anchor)
516
517 def grow_box(self, x=0, y=0):
518 size = self.get_level_tile_size()
519 dsr = self.get_display_surface().get_rect()
520 self.box_corner.x += x
521 if self.box_corner.x < 0:
522 self.box_corner.x = 0
523 elif self.box_corner.x >= dsr.right / size:
524 self.box_corner.x = dsr.right / size - 1
525 self.box_corner.y += y
526 if self.box_corner.y < 0:
527 self.box_corner.y = 0
528 elif self.box_corner.y >= dsr.bottom / size:
529 self.box_corner.y = dsr.bottom / size - 1
530 self.box.size = Vector((abs(self.box_corner.x - self.box_anchor.x) + 1) * size,
531 (abs(self.box_corner.y - self.box_anchor.y) + 1) * size)
532 self.box.location.topleft = \
533 min(self.box_corner.x, self.box_anchor.x) * size, \
534 min(self.box_corner.y, self.box_anchor.y) * size
535 self.box.update()
536
537 def move_box(self, x=0, y=0):
538 size = self.get_level_tile_size()
539 dsr = self.get_display_surface().get_rect()
540 self.box.move(x * size, y * size)
541 if self.box.location.right > dsr.right + self.box.thickness or \
542 self.box.location.left < -self.box.thickness:
543 self.box.move(dx=-x * dsr.w)
544 elif self.box.location.bottom > dsr.bottom + self.box.thickness or \
545 self.box.location.top < -self.box.thickness:
546 self.box.move(dy=-y * dsr.h)
547
548 def paint_tile(self):
549 original = self.get_current_tile()
550 scaled = self.get_current_level().tiles[self.tile_index]
551 x, y = self.brush_position
552 color = self.get_full_palette()[self.palette_index]
553 if original.get_at((x, y)) != color:
554 if self.current_level_is_default:
555 self.current_level_is_default = False
556 self.add_default_level()
557 original.set_at((x, y), color)
558 scale = PictureProcessing.SCALE
559 scaled.fill(color, (x * scale, y * scale, scale, scale))
560 self.get_current_level().set_preview()
561 self.save_level()
562
563 def wrap_palette_index(self):
564 count = self.get_palette_cell_count()
565 if count % 2 and self.palette_index == count / 2:
566 self.palette_index = count - 1
567 else:
568 self.palette_index += [-1, 1][self.palette_index_is_at_left()] * \
569 (count / 2 + count % 2)
570
571 def get_palette_cell_count(self):
572 return len(self.get_full_palette()) + 1
573
574 def palette_index_is_at_left(self):
575 length = self.get_palette_cell_count()
576 return self.palette_index < length / 2 + length % 2
577
578 def move_brush_position(self, dx, dy):
579 self.brush_position.move(dx, dy)
580 if self.brush_position.x >= self.TILE_SIZE:
581 self.brush_position.x = self.TILE_SIZE - 1
582 elif self.brush_position.x < 0:
583 self.brush_position.x = 0
584 if self.brush_position.y >= self.TILE_SIZE:
585 self.brush_position.y = 0
586 elif self.brush_position.y < 0:
587 self.brush_position.y = self.TILE_SIZE - 1
588
589 def get_current_level(self):
590 return self.levels[self.level_index]
591
592 def get_cursor(self, width=0, height=0, flashing=True):
593 return FlashingCursor(self, width, height, flashing)
594
595 def add_tile_selected(self):
596 return self.tile_index == len(self.get_current_level().original_tiles)
597
598 def update(self):
599 if self.active:
600 ds = self.get_display_surface()
601 ds.fill((0, 0, 0))
602 if self.at_level_select:
603 self.background.update()
604 self.editor_heading.update()
605 self.level_select_exit.update()
606 self.label_edit_level.update()
607 preview = self.get_current_level().preview
608 rect = preview.get_rect()
609 rect.center = ds.get_rect().center
610 ds.blit(preview, rect)
611 self.level_select_cursor.update()
612 if len(self.levels) > 1:
613 self.label_left.update()
614 self.label_right.update()
615 else:
616 self.get_current_level().update()
617 if self.at_view:
618 if not self.menu_hidden:
619 self.view_exit.update()
620 self.view_hide.update()
621 self.arrow_tiles.update()
622 self.arrow_play.update()
623 self.arrow_paint.update()
624 self.arrow_music.update()
625 if self.at_tile_bar or self.at_tile_edit or self.at_palette:
626 self.tile_bar.update()
627 tiles = self.get_current_level().original_tiles
628 if len(tiles) < Interface.MAX_TILE_COUNT:
629 full_tiles = tiles + [self.new_tile]
630 else:
631 full_tiles = tiles
632 for ii, tile in enumerate(full_tiles):
633 cell_width = self.tile_bar.location.w / Interface.MAX_TILE_COUNT
634 scaled_tile = scale(
635 tile, [tiles[0].get_width() * self.TILE_BAR_SCALE] * 2)
636 rect = scaled_tile.get_rect()
637 rect.center = cell_width * ii + cell_width / 2, \
638 self.tile_bar.location.centery
639 ds.blit(scaled_tile, rect)
640 if ii == self.tile_index:
641 self.tile_cursor.location.center = rect.center
642 if self.at_tile_bar:
643 self.tile_cursor.set_flashing(True)
644 self.tile_cursor.update()
645 else:
646 self.tile_cursor.set_flashing(False)
647 self.label_back.update()
648 if not self.add_tile_selected():
649 tile = tiles[self.tile_index]
650 canvas = scale(tile, [self.CANVAS_SCALE * tile.get_width()] * 2)
651 rect = canvas.get_rect()
652 rect.center = ds.get_rect().center
653 ds.blit(canvas, rect)
654 palette = self.get_full_palette()
655 length = len(palette) + 1
656 color_left_height = canvas.get_height() / (length / 2 + length % 2)
657 color_right_height = canvas.get_height() / (length / 2)
658 palette_left_height = color_left_height * (length / 2 + length % 2)
659 palette_right_height = color_right_height * (length / 2)
660 palette_left_rect = Rect(0, 0, self.PALETTE_WIDTH, palette_left_height)
661 palette_left_rect.midright = rect.left - 30, rect.centery
662 palette_right_rect = Rect(0, 0, self.PALETTE_WIDTH, palette_right_height)
663 palette_right_rect.midleft = rect.right + 30, rect.centery
664 y = palette_left_rect.top
665 for ii in xrange(length):
666 if ii < length / 2 + length % 2:
667 palette_rect = palette_left_rect
668 h = color_left_height
669 else:
670 palette_rect = palette_right_rect
671 h = color_left_height
672 if ii < length - 1:
673 ds.fill(palette[ii], (palette_rect.left, y, palette_rect.w, h))
674 else:
675 for x in xrange(palette_rect.w):
676 hue = int(float(x) / palette_rect.w * 360)
677 color = Color(0, 0, 0)
678 color.hsla = hue, 100, 50, 100
679 ds.fill(color, (palette_rect.left + x, y, 1, h))
680 if ii == (length / 2 + length % 2) - 1:
681 y = palette_right_rect.top
682 else:
683 y += color_left_height
684 if self.at_tile_bar:
685 self.label_edit_view.update()
686 elif self.at_tile_bar:
687 self.label_new.update()
688 if self.at_tile_edit or self.at_palette:
689 if self.at_tile_edit:
690 paint_flashing = True
691 palette_flashing = False
692 else:
693 paint_flashing = False
694 palette_flashing = True
695 self.paint_cursor.set_flashing(paint_flashing)
696 self.paint_cursor.set_size(*([self.CANVAS_SCALE] * 2))
697 self.palette_cursor.set_flashing(palette_flashing)
698 self.palette_cursor.set_size(palette_rect.w, color_left_height)
699 self.paint_cursor.location.topleft = \
700 rect.left + self.brush_position.x * self.CANVAS_SCALE, \
701 rect.top + self.brush_position.y * self.CANVAS_SCALE
702 self.paint_cursor.update()
703 if self.at_tile_edit:
704 self.label_paint.update()
705 elif self.at_palette:
706 self.label_select.update()
707 if self.palette_index < length / 2 + length % 2:
708 left = palette_left_rect.left
709 top = color_left_height * self.palette_index
710 else:
711 left = palette_right_rect.left
712 top = color_left_height * (self.palette_index - (length / 2 + length % 2))
713 self.palette_cursor.location.topleft = left - 1, palette_rect.top + top - 1
714 self.palette_cursor.update()
715 if self.at_box:
716 if self.box.location.centery > ds.get_rect().centery:
717 self.label_drag.location.top = self.VIEW_MARGIN
718 self.label_back_box.location.top = self.VIEW_MARGIN
719 else:
720 self.label_drag.location.bottom = ds.get_height() - self.VIEW_MARGIN
721 self.label_back_box.location.bottom = ds.get_height() - self.VIEW_MARGIN
722 self.box.move(-1, -1)
723 self.box.update()
724 self.box.move(1, 1)
725 self.label_drag.update()
726 self.label_back_box.update()
727 if self.at_test:
728 interface = self.get_game().interface
729 if interface.closed:
730 self.label_back.update()
731 self.label_swap.update()
732 else:
733 if self.get_current_level().is_solved():
734 self.label_solved.update()
735 interface.update()
736
737 def get_full_palette(self):
738 return self.default_palette + self.get_current_colors()
739
740 def get_current_tile(self):
741 return self.get_current_level().original_tiles[self.tile_index]
742
743 def get_current_colors(self):
744 colors = []
745 if not self.add_tile_selected():
746 tile = self.get_current_tile()
747 for x in xrange(0, tile.get_width()):
748 for y in xrange(0, tile.get_height()):
749 color = tile.get_at((x, y))
750 if color not in colors and color not in self.default_palette:
751 colors.append(color)
752 return colors
753
754
755 class FlashingCursor(Sprite):
756
757 THICKNESS = 3
758
759 def __init__(self, parent, width=0, height=0, flashing=True):
760 Sprite.__init__(self, parent)
761 self.set_flashing(flashing)
762 self.set_size(width, height)
763 self.hue = 0
764 self.thickness = self.THICKNESS
765 self.set_frame()
766
767 def set_flashing(self, state):
768 self.flashing = state
769
770 def set_size(self, width, height):
771 self.size = Vector(width, height)
772
773 def set_frame(self):
774 offset = self.thickness / 2
775 surface = Surface((self.size.x + offset * 2, self.size.y + offset * 2))
776 rect = offset, offset, self.size.x, self.size.y
777 cursor = Sprite(self)
778 if self.flashing:
779 self.hue = (self.hue + 30) % 360
780 surface.set_colorkey((0, 0, 0))
781 color = Color(0, 0, 0)
782 color.hsla = self.hue, 100, 50, 100
783 else:
784 surface.fill((255, 0, 0))
785 surface.set_colorkey((255, 0, 0))
786 color = 0, 0, 0
787 draw.rect(surface, color, rect, self.thickness)
788 draw.rect(surface, (255, 255, 255), rect, 1)
789 self.clear_frames()
790 self.add_frame(surface)
791
792 def grow(self, dx=0, dy=0):
793 self.size += dx, dy
794
795 def update(self):
796 self.set_frame()
797 Sprite.update(self)
798
799
800 class SoundEffects(GameChild):
801
802 def __init__(self, parent):
803 GameChild.__init__(self, parent)
804 effects = self.effects = []
805 for path in glob(join(self.get_resource("aud/effect"), "*.wav")):
806 effects.append(SoundEffect(self, path))
807
808 def play(self, name, volume=None):
809 for effect in self.effects:
810 if effect.name == name:
811 effect.play(volume=volume)
812
813
814 class SoundEffect(GameChild, Sound):
815
816 DEFAULT_VOLUME = .5
817
818 def __init__(self, parent, path, volume=DEFAULT_VOLUME):
819 GameChild.__init__(self, parent)
820 Sound.__init__(self, path)
821 self.name = basename(path).split(".")[0]
822 if self.name == "swap":
823 volume = .4
824 elif self.name == "menu-move":
825 volume = .22
826 elif self.name == "cancel":
827 volume = .35
828 elif self.name == "start":
829 volume = .35
830 elif self.name == "clear":
831 volume = .35
832 self.display_surface = self.get_display_surface()
833 self.set_volume(volume)
834
835 def play(self, loops=0, maxtime=0, fade_ms=0, position=None, x=None, volume=None):
836 channel = Sound.play(self, loops, maxtime, fade_ms)
837 if x is not None:
838 position = float(x) / self.display_surface.get_width()
839 if position is not None and channel is not None:
840 channel.set_volume(*self.get_panning(position))
841 return channel
842
843 def get_panning(self, position):
844 return 1 - max(0, ((position - .5) * 2)), \
845 1 + min(0, ((position - .5) * 2))
846
847
848 class Glyphs(GameChild):
849
850 TILE_SIZE = 8
851 TRANSPARENT_COLOR = (255, 0, 255)
852 LETTER_COLOR = (255, 255, 255)
853
854 def __init__(self, parent):
855 GameChild.__init__(self, parent)
856 sheet = load(self.get_resource("font.png")).convert()
857 sheet.set_colorkey(sheet.get_at((0, 0)))
858 tiles = self.tiles = []
859 for y in xrange(0, sheet.get_height(), self.TILE_SIZE):
860 surface = Surface([self.TILE_SIZE] * 2)
861 surface.set_colorkey(self.TRANSPARENT_COLOR)
862 surface.fill(self.TRANSPARENT_COLOR)
863 surface.blit(sheet, (0, -y))
864 surface = scale(surface, [surface.get_width() * \
865 PictureProcessing.SCALE] * 2)
866 tiles.append(surface)
867
868 def get_surface_from_text(self, text, color=None, spacing=1,
869 key=TRANSPARENT_COLOR, background=None):
870 tw, th = self.tiles[0].get_size()
871 tiles = self.get_tiles(text, color)
872 surface = Surface((len(tiles) * tw + spacing * len(tiles), th))
873 if background is None:
874 surface.set_colorkey(key)
875 surface.fill(key)
876 else:
877 surface.fill(background)
878 for ii, x in enumerate(xrange(0, surface.get_width(), tw + spacing)):
879 surface.blit(tiles[ii], (x, 0))
880 return surface
881
882 def get_tiles(self, text, color):
883 tiles = []
884 index = 0
885 while index < len(text):
886 if text[index] == "\\":
887 index += 1
888 if text[index] == "\\":
889 tile_index = ord(text[index])
890 index += 1
891 else:
892 code = ""
893 while index < len(text) and text[index].isdigit():
894 code += text[index]
895 index += 1
896 tile_index = int(code)
897 else:
898 tile_index = ord(text[index])
899 index += 1
900 tiles.append(self.get_tile(tile_index, color))
901 return tiles
902
903 def get_tile(self, index, color=None):
904 tile = self.tiles[index]
905 if color is not None:
906 tile = tile.copy()
907 pixels = PixelArray(tile)
908 pixels.replace(self.LETTER_COLOR, color)
909 del pixels
910 return tile
911
912
913 class Levels(GameChild):
914
915 CLIP_SETTINGS = ((.325, 5512), (.25, 11025), (.5, 11025), (.325, 5512), (.5, 11025))
916 TITLES = "POWER UP", "SPIN OUT", "SELECTION", "SPOOKY DIG", "DON'T FALL MOUSE"
917
918 def __init__(self, parent):
919 GameChild.__init__(self, parent)
920 levels = self.levels = []
921 for ii, directory in enumerate(sorted(glob(join(self.get_resource("tiles"),
922 "[0-9]*")))):
923 level = Level(self)
924 level.load(Level.LOAD_MAP_FILE, directory, self.TITLES[ii])
925 levels.append(level)
926
927 def reset(self):
928 self.deactivate()
929 self.current_level = self.levels[0]
930
931 def deactivate(self):
932 self.active = False
933 self.stop_audio()
934 self.get_game().interface.deactivate()
935
936 def activate(self):
937 self.active = True
938
939 def stop_audio(self):
940 for level in self.levels:
941 level.stop_all_audio()
942
943 def get_current_level_index(self):
944 return self.levels.index(self.current_level)
945
946 def is_final_level(self):
947 return self.get_current_level_index() == len(self.levels) - 1
948
949 def begin_next_level(self):
950 self.get_game().interface.deactivate()
951 self.levels[self.get_current_level_index() + 1].begin()
952
953 def get_current_title(self):
954 return self.TITLES[self.get_current_level_index()]
955
956 def update(self):
957 if self.active:
958 self.current_level.update()
959
960
961 class Level(Animation):
962
963 VOLUME = .5
964 TITLE_OFFSET = 32
965 TITLE_BORDER_SIZE = 20, 8
966 TITLE_BORDER_SPEED = 400
967 TITLE_LENGTH = 5000
968 DEFAULT_LEVEL_TITLE = "DEFAULT"
969 LOAD_DEFAULT, LOAD_GRID_FILE, LOAD_MAP_FILE = range(3)
970 PREVIEW_SCALE = 2
971
972 def __init__(self, parent):
973 Animation.__init__(self, parent)
974 self.at_title = False
975 self.clip_audio = None
976 self.full_audio = None
977 self.title_plate = None
978 self.path = None
979
980 def load(self, method=LOAD_DEFAULT, directory=None, title=None):
981 self.tiles = []
982 self.original_tiles = []
983 self.colored_tiles = {}
984 if method == self.LOAD_GRID_FILE:
985 loaded = self.load_grid_file(directory)
986 elif method == self.LOAD_MAP_FILE:
987 loaded = self.load_map_file(directory, title)
988 else:
989 loaded = self.load_default()
990 if loaded:
991 self.set_preview()
992 return loaded
993
994 def load_map_file(self, directory, title):
995 tiles = self.tiles
996 original_tiles = self.original_tiles
997 ds = self.get_display_surface()
998 for path in sorted(glob(join(directory, "*.png"))):
999 self.add_tile(load(path).convert_alpha())
1000 colored = self.colored_tiles
1001 self.set_entire_grid()
1002 block = 0
1003 for line in open(join(directory, "map")):
1004 if line.strip() == "":
1005 block += 1
1006 elif block == 0:
1007 fields = line.split("-")
1008 if len(fields) == 1:
1009 self.default_tile_index = int(fields[0])
1010 else:
1011 for field in fields:
1012 digits = map(int, field.split())
1013 if len(digits) == 1:
1014 index = digits[0]
1015 elif len(digits) == 3:
1016 surface = Surface(tiles[index].get_size())
1017 surface.fill(digits)
1018 surface.blit(tiles[index], (0, 0))
1019 colored[index] = surface
1020 else:
1021 if len(digits) == 2:
1022 digits *= 2
1023 for x in xrange(digits[0], digits[2] + 1):
1024 for y in xrange(digits[1], digits[3] + 1):
1025 self.grid[x][y] = index
1026 for x, row in enumerate(self.grid):
1027 for y, value in enumerate(row):
1028 if value is None:
1029 self.grid[x][y] = self.default_tile_index
1030 title_plate = self.title_plate = Sprite(self)
1031 title_plate.add_frame(self.get_game().glyphs.get_surface_from_text(title, background=(0, 0, 0)))
1032 title_background = self.title_background = Surface(ds.get_size())
1033 title_background.fill((0, 0, 0))
1034 scene_plate = self.scene_plate = Sprite(self)
1035 level_number = (Levels.TITLES.index(title) + 1)
1036 if level_number == len(Levels.TITLES):
1037 scene_text = "- FINAL SCENE -"
1038 else:
1039 scene_text = "- SCENE %d -" % level_number
1040 scene_plate.add_frame(self.get_game().glyphs.get_surface_from_text(scene_text, background=(0, 0, 0)))
1041 dsr = ds.get_rect()
1042 scene_plate.location.center = dsr.centerx, dsr.centery - self.TITLE_OFFSET
1043 border_tiles = self.border_tiles = []
1044 for ii in xrange(self.TITLE_BORDER_SIZE[0] * 2 + self.TITLE_BORDER_SIZE[1] * 2 - 4):
1045 border_tiles.append(self.tiles[ii % len(self.tiles)])
1046 path = glob(join(self.get_resource("aud/level"), basename(directory) + "*"))[0]
1047 self.full_audio = Sound(path)
1048 self.full_audio.set_volume(self.VOLUME)
1049 self.title_border_offset = 0
1050 self.register(self.rotate_title_border, interval=self.TITLE_BORDER_SPEED)
1051 self.play(self.rotate_title_border)
1052 self.subscribe(self.respond)
1053 self.set_swap_status()
1054 self.path = directory
1055 return True
1056
1057 def add_tile(self, surface):
1058 self.original_tiles.append(surface)
1059 scaled = scale(surface, [surface.get_width() * PictureProcessing.SCALE] * 2)
1060 self.tiles.append(scaled)
1061 self.set_swap_status()
1062
1063 def get_visible_tile_indicies(self):
1064 indicies = set()
1065 for row in self.grid:
1066 for index in row:
1067 indicies.add(index)
1068 return indicies
1069
1070 def set_entire_grid(self, index=None):
1071 self.grid = []
1072 ds = self.get_display_surface()
1073 for x in xrange(ds.get_width() / self.tiles[0].get_width()):
1074 self.grid.append([index] *
1075 (ds.get_height() / self.tiles[0].get_height()))
1076
1077 def load_grid_file(self, directory):
1078 for path in sorted(glob(join(directory, "*.png"))):
1079 self.add_tile(load(path).convert())
1080 if not self.tiles:
1081 return False
1082 level_path = join(
1083 directory, self.get_configuration("editor", "level-file-name"))
1084 if not exists(level_path):
1085 return False
1086 level_file = open(level_path, "r")
1087 self.set_entire_grid()
1088 for jj, line in enumerate(level_file):
1089 for ii, character in enumerate(line.strip()):
1090 self.grid[ii][jj] = int(character, 16)
1091 if self.grid[ii][jj] >= len(self.tiles):
1092 return False
1093 self.set_swap_status()
1094 self.path = directory
1095 return True
1096
1097 def load_default(self):
1098 path = self.get_game().editor.get_default_tile_path()
1099 if exists(path):
1100 default = load(path).convert()
1101 else:
1102 default = Surface([Editor.TILE_SIZE] * 2)
1103 default.fill((255, 255, 255))
1104 for y in xrange(default.get_height()):
1105 for x in xrange([1, 2, 2, 2, 2, 2, 7, 8][y]):
1106 default.set_at((x, y), (200, 200, 200))
1107 self.add_tile(default)
1108 self.set_entire_grid(0)
1109 self.set_swap_status()
1110 return True
1111
1112 def set_preview(self):
1113 tw, th = self.original_tiles[0].get_size()
1114 preview = self.preview = Surface(
1115 (tw * len(self.grid) * self.PREVIEW_SCALE,
1116 th * len(self.grid[0]) * self.PREVIEW_SCALE))
1117 for grid_x, surface_x in enumerate(xrange(0, preview.get_width(),
1118 tw * self.PREVIEW_SCALE)):
1119 for grid_y, surface_y in enumerate(xrange(0, preview.get_height(),
1120 th * self.PREVIEW_SCALE)):
1121 scaled = scale(self.original_tiles[self.grid[grid_x][grid_y]],
1122 [tw * self.PREVIEW_SCALE] * 2)
1123 preview.blit(scaled, (surface_x, surface_y))
1124
1125 def rotate_title_border(self):
1126 self.title_border_offset -= 1
1127
1128 def respond(self, event):
1129 if self.parent.active and self.parent.current_level == self and self.at_title and \
1130 self.get_game().delegate.compare(event, "advance") and not self.get_game().editor.active:
1131 self.close_title()
1132
1133 def close_title(self):
1134 self.at_title = False
1135 self.get_game().interface.setup()
1136 self.get_game().sound_effects.play("granted")
1137
1138 def begin(self):
1139 self.parent.activate()
1140 self.parent.current_level = self
1141 if self.full_audio is not None:
1142 self.set_audio()
1143 self.set_swap_status(True)
1144 self.parent.stop_audio()
1145 if self.clip_audio is not None:
1146 self.clip_audio.play(-1)
1147 if self.title_plate is not None:
1148 self.at_title = True
1149 self.title_plate.set_alpha(255)
1150 dsr = self.get_display_surface().get_rect()
1151 self.title_plate.location.center = dsr.centerx, dsr.centery + self.TITLE_OFFSET
1152 self.title_plate.unhide()
1153 self.title_elapsed = 0
1154 else:
1155 self.close_title()
1156
1157 def set_audio(self):
1158 if version.vernum < (1, 9, 2):
1159 raw = self.full_audio.get_buffer().raw
1160 else:
1161 raw = self.full_audio.get_raw()
1162 samples = array("h", raw)
1163 volume, count = Levels.CLIP_SETTINGS[self.parent.get_current_level_index()]
1164 index = randrange(0, len(samples) - count)
1165 buf = array("h", [0] * 50000) + \
1166 samples[index:index + count] + \
1167 array("h", [0] * 150000)
1168 if version.vernum < (1, 9, 2):
1169 clip = self.clip_audio = Sound(buf)
1170 else:
1171 clip = self.clip_audio = Sound(buffer=buf)
1172 clip.set_volume(volume)
1173
1174 def set_swap_status(self, randomize=False):
1175 swap_status = self.swap_status = {}
1176 if randomize and len(self.tiles) > 1:
1177 swapped = False
1178 indicies = self.get_visible_tile_indicies()
1179 while not swapped:
1180 available_swap_positions = indicies.copy()
1181 for index in indicies:
1182 if len(available_swap_positions) == 1 and \
1183 list(available_swap_positions)[0] == index:
1184 break
1185 while True:
1186 swap_status[index] = choice(list(available_swap_positions))
1187 if swap_status[index] != index:
1188 break
1189 available_swap_positions.remove(swap_status[index])
1190 if not available_swap_positions:
1191 swapped = True
1192 break
1193 else:
1194 for index in xrange(len(self.tiles)):
1195 swap_status[index] = index
1196
1197 def stop_all_audio(self):
1198 if self.clip_audio:
1199 self.clip_audio.stop()
1200 if self.full_audio:
1201 self.full_audio.stop()
1202
1203 def is_solved(self):
1204 return all(index == position for index, position in self.swap_status.iteritems())
1205
1206 def get_remaining_swap_count(self):
1207 off = 0
1208 for index, position in self.swap_status.iteritems():
1209 off += index != position
1210 return off / 2 + (off % 2)
1211
1212 def update(self):
1213 Animation.update(self)
1214 ds = self.get_display_surface()
1215 if self.at_title:
1216 ds.blit(self.title_background, (0, 0))
1217 bw, bh = self.TITLE_BORDER_SIZE
1218 tr = self.tiles[0].get_rect()
1219 br = Rect(0, 0, bw * tr.w, bh * tr.h)
1220 br.center = ds.get_rect().center
1221 index = self.title_border_offset % len(self.border_tiles)
1222 y = br.top
1223 for x in xrange(br.left, br.right, tr.w):
1224 ds.blit(self.border_tiles[index % len(self.border_tiles)], (x, y))
1225 index += 1
1226 for y in xrange(br.top + tr.h, br.bottom, tr.h):
1227 ds.blit(self.border_tiles[index % len(self.border_tiles)], (x, y))
1228 index += 1
1229 for x in xrange(br.right - tr.w * 2, br.left - tr.w, -tr.w):
1230 ds.blit(self.border_tiles[index % len(self.border_tiles)], (x, y))
1231 index += 1
1232 for y in xrange(br.bottom - tr.h * 2, br.top, -tr.h):
1233 ds.blit(self.border_tiles[index % len(self.border_tiles)], (x, y))
1234 index += 1
1235 self.scene_plate.update()
1236 self.title_plate.update()
1237 self.title_elapsed += self.get_game().time_filter.get_last_frame_duration()
1238 if self.title_elapsed >= self.TITLE_LENGTH:
1239 self.close_title()
1240 else:
1241 for grid_x, screen_x in enumerate(xrange(0, ds.get_width(),
1242 self.tiles[0].get_width())):
1243 for grid_y, screen_y in enumerate(xrange(0, ds.get_height(),
1244 self.tiles[0].get_height())):
1245 tile_index = self.grid[grid_x][grid_y]
1246 swap_index = self.swap_status[tile_index]
1247 if swap_index in self.colored_tiles.keys():
1248 tile = self.colored_tiles[swap_index]
1249 else:
1250 tile = self.tiles[swap_index]
1251 ds.blit(tile, (screen_x, screen_y))
1252
1253
1254 class Interface(Animation):
1255
1256 BLINK_INTERVAL = 400
1257 BLINK_COUNTDOWN_INTERVAL = 2400
1258 OPEN_TEXT = "ENTER: EDIT MEMORY", "A: EDIT MEMORY"
1259 CELL_ALPHA = 240
1260 OPEN_TEXT_OFFSET = 36
1261 MAX_TILE_COUNT = 16
1262 CELL_INDENT = 32
1263 CELL_MARGIN = 56
1264 CELL_VERTICAL_PADDING = 24
1265 BACKGROUND = 0, 0, 0
1266 TEXT_COLOR = 255, 255, 255
1267 NAME_CHARACTER = "H"
1268 CELL_INDICATOR = 16
1269 CELL_INDICATOR_OFFSET = 2
1270 SHADOW_COLOR = 255, 255, 255
1271 SHADOW_OFFSET = -1, 1
1272 SWAP_INDICATOR = 16
1273 ADVANCE_TEXT = "PRESS ENTER", "PRESS START"
1274 SWAP_TEXT = "ENT: SWAP", "A: SWAP"
1275 HIDE_TEXT = "ESC: VIEW", "B: VIEW"
1276 MOVE_TEXT = ": MOVE"
1277 PLAY_SONG_DELAY = 1000
1278 UNSUPPRESS_ADVANCE_DELAY = 2000
1279 MOVE_ICONS = 24, 25, 26, 27
1280 COUNTDOWN_THRESHOLD = 2
1281 TILE_ZOOM_SCALE = 2
1282
1283 def __init__(self, parent):
1284 Animation.__init__(self, parent)
1285 ds = self.display_surface = self.get_display_surface()
1286 glyphs = self.get_game().glyphs
1287 open_plate = self.open_plate = Sprite(self)
1288 open_plate.add_frame(glyphs.get_surface_from_text(self.get_game().select_text(self.OPEN_TEXT),
1289 background=(0, 0, 0)))
1290 open_plate.location.midbottom = ds.get_rect().centerx, ds.get_height() - \
1291 self.OPEN_TEXT_OFFSET
1292 self.cell_frames = cell_frames = []
1293 tile_size = glyphs.get_tile(0).get_width()
1294 for ii in xrange(self.MAX_TILE_COUNT):
1295 title = ("%02x%s" % (ii, self.NAME_CHARACTER)).upper()
1296 title_surface = glyphs.get_surface_from_text(title, self.TEXT_COLOR)
1297 frame = Surface((self.CELL_INDENT * 2 + title_surface.get_width() + \
1298 self.CELL_MARGIN + tile_size,
1299 self.CELL_VERTICAL_PADDING * 2 + tile_size))
1300 frame.fill(self.BACKGROUND)
1301 frame.blit(title_surface, (self.CELL_INDENT, self.CELL_VERTICAL_PADDING))
1302 cell_frames.append(Sprite(self))
1303 cell_frames[-1].add_frame(frame)
1304 cell_frames[-1].set_alpha(self.CELL_ALPHA)
1305 indicator = self.cell_indicator = Sprite(self)
1306 indicator.add_frame(glyphs.get_tile(self.CELL_INDICATOR, self.TEXT_COLOR))
1307 indicator = self.swap_indicator = Sprite(self)
1308 indicator.add_frame(glyphs.get_tile(self.SWAP_INDICATOR, self.TEXT_COLOR))
1309 shadow = self.tile_shadow = Surface([tile_size * self.TILE_ZOOM_SCALE] * 2)
1310 shadow.fill(self.SHADOW_COLOR)
1311 advance_plate = self.advance_plate = Sprite(self)
1312 advance_plate.add_frame(glyphs.get_surface_from_text(self.get_game().select_text(self.ADVANCE_TEXT),
1313 background=(0, 0, 0)))
1314 advance_plate.location.midbottom = open_plate.location.midbottom
1315 swap_plate = self.swap_plate = Sprite(self)
1316 text = self.SWAP_TEXT[1]
1317 swap_plate.add_frame(glyphs.get_surface_from_text(self.get_game().select_text(self.SWAP_TEXT),
1318 background=(0, 0, 0)))
1319 swap_plate.set_alpha(self.CELL_ALPHA)
1320 move_plate = self.move_plate = Sprite(self)
1321 text_surface = glyphs.get_surface_from_text(self.MOVE_TEXT,
1322 background=(0, 0, 0))
1323 frame = Surface((tile_size * len(self.MOVE_ICONS) + text_surface.get_width(),
1324 tile_size))
1325 x = 0
1326 for index in self.MOVE_ICONS:
1327 frame.blit(glyphs.get_tile(index), (x, 0))
1328 x += tile_size
1329 frame.blit(text_surface, (x, 0))
1330 move_plate.add_frame(frame)
1331 move_plate.set_alpha(self.CELL_ALPHA)
1332 move_plate.location.midtop = ds.get_width() / 2, 0
1333 hide_plate = self.hide_plate = Sprite(self)
1334 hide_plate.add_frame(glyphs.get_surface_from_text(self.get_game().select_text(self.HIDE_TEXT),
1335 background=(0, 0, 0)))
1336 hide_plate.set_alpha(self.CELL_ALPHA)
1337 hide_plate.location.topright = ds.get_width(), 0
1338 self.register(self.blink, interval=self.BLINK_INTERVAL)
1339 self.register(self.blink_countdown, interval=self.BLINK_COUNTDOWN_INTERVAL)
1340 self.register(self.play_song)
1341 self.register(self.unsuppress_advance)
1342 self.subscribe(self.respond)
1343
1344 def blink(self):
1345 if self.closed and not self.solved:
1346 self.open_plate.toggle_hidden()
1347 if not self.closed and self.swapping_index is not None:
1348 self.swap_indicator.toggle_hidden()
1349 if self.solved and not self.suppressing_commands and not self.get_level().at_title:
1350 self.advance_plate.toggle_hidden()
1351
1352 def get_level(self):
1353 if self.get_game().editor.active:
1354 level = self.get_game().editor.get_current_level()
1355 else:
1356 level = self.get_game().levels.current_level
1357 return level
1358
1359 def blink_countdown(self):
1360 if self.is_countdown_active():
1361 self.countdown_plate.toggle_hidden()
1362 if self.countdown_plate.is_hidden():
1363 self.set_title_plate_visibility(False)
1364 else:
1365 self.set_title_plate_visibility(True)
1366
1367 def set_title_plate_visibility(self, visible=True):
1368 title_plate = self.get_title_plate()
1369 if title_plate is not None:
1370 if visible:
1371 title_plate.unhide()
1372 else:
1373 title_plate.hide()
1374
1375 def is_countdown_active(self):
1376 return 0 < self.get_level().get_remaining_swap_count() <= self.COUNTDOWN_THRESHOLD
1377
1378 def respond(self, event, suppress_sound=False):
1379 if self.active and not self.suppressing_commands and \
1380 (not self.get_game().editor.active or self.get_game().editor.at_test):
1381 delegate = self.get_game().delegate
1382 effects = self.get_game().sound_effects
1383 is_pad_mode = self.get_game().is_gamepad_mode()
1384 if self.closed and not self.solved and delegate.compare(event, "action"):
1385 effects.play("memory")
1386 self.unclose()
1387 elif self.closed and self.solved and (delegate.compare(event, "advance") or \
1388 (not is_pad_mode and delegate.compare(event, "action"))):
1389 if not self.get_game().editor.active:
1390 effects.play("go")
1391 self.advance_plate.hide()
1392 if self.get_game().levels.is_final_level():
1393 self.get_game().ending.activate()
1394 self.get_game().levels.deactivate()
1395 else:
1396 self.get_game().levels.begin_next_level()
1397 elif not self.closed:
1398 if delegate.compare(event, "cancel"):
1399 effects.play("cancel")
1400 if self.swapping_index is not None:
1401 self.swapping_index = None
1402 else:
1403 self.close()
1404 elif delegate.compare(event, "action"):
1405 if self.swapping_index is None:
1406 effects.play("start")
1407 self.swapping_index = self.get_selected_tile_index()
1408 self.set_indicator_position(False)
1409 self.respond(Event(delegate.command_event_id,
1410 {"command": "down", "cancel": False}), True)
1411 else:
1412 if self.get_selected_tile_index() == self.swapping_index:
1413 effects.play("cancel")
1414 else:
1415 level = self.get_level()
1416 status = level.swap_status
1417 temp = status[self.swapping_index]
1418 selected_index = self.get_selected_tile_index()
1419 status[self.swapping_index] = status[selected_index]
1420 status[selected_index] = temp
1421 self.swap_count += 1
1422 if not level.is_solved():
1423 effects.play("swap")
1424 self.set_countdown_plate()
1425 if self.is_countdown_active():
1426 self.countdown_plate.unhide()
1427 self.set_title_plate_visibility(True)
1428 self.reset_timer(self.blink_countdown)
1429 else:
1430 self.set_title_plate_visibility(False)
1431 self.countdown_plate.hide()
1432 elif not self.get_game().editor.active:
1433 effects.play("clear")
1434 if not self.get_game().levels.is_final_level():
1435 self.get_game().write_progress(self.get_game().levels.\
1436 get_current_level_index() + 1,
1437 self.swap_count, True)
1438 else:
1439 self.get_game().ending.\
1440 set_swap_count(self.get_game().get_progress()[1] + \
1441 self.swap_count)
1442 self.get_game().write_progress(0, 0)
1443 self.suppressing_commands = True
1444 self.solved = True
1445 self.play(self.play_song, delay=self.PLAY_SONG_DELAY, play_once=True)
1446 self.swapping_index = None
1447 elif delegate.compare(event, ["up", "down", "left", "right"]):
1448 if not suppress_sound:
1449 effects.play("menu-move")
1450 cell_count = self.get_cell_count()
1451 bp = (cell_count - 1) / 2
1452 if delegate.compare(event, "up"):
1453 self.cell_index -= 1
1454 if self.cell_index == -1:
1455 self.cell_index = bp
1456 elif self.cell_index == bp:
1457 self.cell_index = cell_count - 1
1458 elif delegate.compare(event, "down"):
1459 self.cell_index += 1
1460 if self.cell_index == bp + 1:
1461 self.cell_index = 0
1462 elif self.cell_index == cell_count:
1463 self.cell_index = bp + 1
1464 elif delegate.compare(event, "left"):
1465 if self.cell_index == 0:
1466 self.cell_index = bp + 1
1467 elif self.cell_index > bp:
1468 self.cell_index -= bp + 1
1469 else:
1470 self.cell_index += bp
1471 if self.cell_index == cell_count:
1472 self.cell_index -= 1
1473 elif delegate.compare(event, "right"):
1474 if self.cell_index == cell_count - 1:
1475 self.cell_index = bp
1476 elif self.cell_index <= bp:
1477 self.cell_index += bp + 1
1478 if self.cell_index == cell_count:
1479 self.cell_index -= 1
1480 else:
1481 self.cell_index -= bp
1482 self.set_indicator_position()
1483
1484 def get_selected_tile_index(self):
1485 for ii, index in enumerate(sorted(self.get_level().swap_status.keys())):
1486 if ii == self.cell_index:
1487 return index
1488
1489 def play_song(self):
1490 self.close()
1491 level = self.get_level()
1492 level.clip_audio.stop()
1493 level.full_audio.play(-1)
1494 self.play(self.unsuppress_advance, delay=self.UNSUPPRESS_ADVANCE_DELAY,
1495 play_once=True)
1496
1497 def unsuppress_advance(self):
1498 self.suppressing_commands = False
1499 self.advance_plate.unhide()
1500
1501 def set_indicator_position(self, cell=True):
1502 if cell:
1503 indicator = self.cell_indicator
1504 else:
1505 indicator = self.swap_indicator
1506 indicator.location.midleft = self.cell_frames[self.cell_index].location.\
1507 midleft
1508 indicator.location.left += self.CELL_INDICATOR_OFFSET
1509
1510 def get_cell_count(self):
1511 return len(self.get_level().swap_status)
1512
1513 def reset(self):
1514 self.deactivate()
1515 self.solved = False
1516 self.close()
1517 self.halt()
1518 self.suppressing_commands = False
1519
1520 def deactivate(self):
1521 self.active = False
1522
1523 def setup(self):
1524 self.activate()
1525 self.close()
1526 cell_count = self.get_cell_count()
1527 left_count = cell_count / 2 + cell_count % 2
1528 right_count = cell_count / 2
1529 sw, sh = self.display_surface.get_size()
1530 step = sh / (left_count + 1)
1531 frames = self.cell_frames
1532 index = 0
1533 for y in xrange(step, step * (left_count + 1), step):
1534 frames[index].location.midleft = 0, y
1535 index += 1
1536 step = sh / (right_count + 1)
1537 for y in xrange(step, step * (right_count + 1), step):
1538 frames[index].location.midright = sw, y
1539 index += 1
1540 self.cell_index = 0
1541 self.set_indicator_position()
1542 self.swapping_index = None
1543 self.swap_count = 0
1544 self.suppressing_commands = False
1545 self.solved = False
1546 self.advance_plate.hide()
1547 self.play(self.blink)
1548 self.play(self.blink_countdown)
1549 dsr = self.get_display_surface().get_rect()
1550 title_plate = self.get_title_plate()
1551 if title_plate is not None:
1552 title_plate.location.midbottom = dsr.centerx, dsr.h
1553 self.set_countdown_plate()
1554 self.countdown_plate.hide()
1555
1556 def get_title_plate(self):
1557 return self.get_level().title_plate
1558
1559 def set_countdown_plate(self):
1560 countdown_plate = self.countdown_plate = Sprite(self)
1561 count = self.get_level().get_remaining_swap_count()
1562 swap_text = "SWAPS" if count > 1 else "SWAP"
1563 text = "%i %s UNTIL FIXED!" % (count, swap_text)
1564 countdown_plate.add_frame(self.get_game().glyphs.
1565 get_surface_from_text(text, background=(0, 0, 0)))
1566 countdown_plate.location.midbottom = self.get_display_surface().get_rect().midbottom
1567
1568 def activate(self):
1569 self.active = True
1570
1571 def close(self):
1572 self.closed = True
1573 if not self.solved:
1574 self.open_plate.unhide()
1575
1576 def unclose(self):
1577 self.closed = False
1578 self.open_plate.hide()
1579
1580 def update(self):
1581 Animation.update(self)
1582 if self.active:
1583 if not self.get_game().editor.active:
1584 self.open_plate.update()
1585 self.advance_plate.update()
1586 if not self.closed:
1587 for frame in self.cell_frames[:self.get_cell_count()]:
1588 frame.update()
1589 self.cell_indicator.update()
1590 if self.swapping_index is not None:
1591 self.swap_indicator.update()
1592 level = self.get_level()
1593 for ii, index in enumerate(sorted(level.swap_status.keys())):
1594 position = level.swap_status[index]
1595 if position in level.colored_tiles.keys():
1596 tile = level.colored_tiles[position]
1597 else:
1598 tile = level.tiles[position]
1599 zoomed = scale(tile, [tile.get_width() * self.TILE_ZOOM_SCALE] * 2)
1600 rect = zoomed.get_rect()
1601 frame = self.cell_frames[ii]
1602 rect.midright = frame.location.right - self.CELL_INDENT, \
1603 frame.location.centery
1604 self.display_surface.blit(self.tile_shadow, rect.move(self.SHADOW_OFFSET))
1605 self.display_surface.blit(zoomed, rect)
1606 self.swap_plate.update()
1607 self.hide_plate.update()
1608 self.move_plate.update()
1609 title_plate = self.get_title_plate()
1610 if title_plate is not None:
1611 title_plate.update()
1612 if not self.get_game().editor.active:
1613 self.countdown_plate.update()
1614
1615
1616 class Title(Animation):
1617
1618 BLINK_INTERVAL = 400
1619 MENU_OFFSET = 100
1620 MENU_WIDTH = 440
1621 ADVANCE_TEXT = "PRESS ENTER", "PRESS START"
1622 MENU_OPTIONS = "NEW GAME", "CONTINUE"
1623 MENU_MARGIN = 12
1624 MENU_PADDING = 16
1625 TRANSPARENT_COLOR = (128, 128, 128)
1626 BACKGROUND_FRAME_COUNT = 1
1627 BACKGROUND_FRAMERATE = 200
1628 MENU_INDICATOR_LOCATION = 224
1629 CHUNK_PROBABILITY = .5
1630 CHUNK_VOLUME = .1
1631 NAME = "PICTURE PROCESSING"
1632 NAME_OFFSET = 300
1633
1634 def __init__(self, parent):
1635 Animation.__init__(self, parent)
1636 self.display_surface = self.get_display_surface()
1637 self.set_audio()
1638 self.set_name_plate()
1639 self.set_advance_plate()
1640 self.register(self.blink, interval=self.BLINK_INTERVAL)
1641 self.subscribe(self.respond)
1642 self.play(self.blink)
1643
1644 def set_audio(self):
1645 self.audio_loop = Sound(self.get_resource("aud/title.wav"))
1646 chunks = self.audio_chunks = []
1647 for path in glob(join(self.get_resource("aud/chunk"), "*.ogg")):
1648 chunks.append(Sound(path))
1649 chunks[-1].set_volume(self.CHUNK_VOLUME)
1650
1651 def set_name_plate(self):
1652 plate = self.name_plate = RainbowSprite(
1653 self, self.get_game().glyphs.get_surface_from_text(self.NAME, background=(80, 0, 0)))
1654 plate.location.center = self.get_display_surface().get_rect().centerx, self.NAME_OFFSET
1655
1656 def set_advance_plate(self):
1657 offset = 0
1658 plate = self.advance_plate = Sprite(self, self.BLINK_INTERVAL)
1659 plate.add_frame(self.get_game().glyphs.\
1660 get_surface_from_text(self.get_game().select_text(self.ADVANCE_TEXT),
1661 (255, 255, 255), background=(0, 0, 0)))
1662 rect = self.display_surface.get_rect()
1663 plate.location.center = rect.centerx, rect.bottom - self.MENU_OFFSET
1664
1665 def blink(self):
1666 self.advance_plate.toggle_hidden()
1667
1668 def reset(self):
1669 self.set_background()
1670 self.activate()
1671 self.advance_pressed = False
1672 self.advance_plate.unhide()
1673 self.background.set_scroll_step()
1674 self.menu_index = 0
1675
1676 def set_background(self):
1677 self.background = ScrollingBackground(self,
1678 count=self.BACKGROUND_FRAME_COUNT,
1679 framerate=self.BACKGROUND_FRAMERATE)
1680
1681 def activate(self):
1682 self.active = True
1683 # self.audio_loop.play(-1)
1684 self.set_menu()
1685
1686 def set_menu(self):
1687 glyphs = self.get_game().glyphs
1688 if self.get_game().get_progress()[0] > 0:
1689 option_texts = self.MENU_OPTIONS
1690 else:
1691 option_texts = self.MENU_OPTIONS[:1]
1692 height = glyphs.get_tile(0).get_height() * len(option_texts) + \
1693 self.MENU_MARGIN * (len(option_texts) - 1) + \
1694 self.MENU_PADDING * 2 + 3
1695 background = self.menu_background = Surface((int(self.MENU_WIDTH),
1696 height))
1697 background.fill((0, 0, 0))
1698 rect = self.menu_rect = background.get_rect()
1699 rect.center = self.advance_plate.location.center
1700 options = self.options = []
1701 for option in option_texts:
1702 options.append(Sprite(self))
1703 options[-1].add_frame(glyphs.get_surface_from_text(option))
1704 options[-1].location.centerx = rect.centerx
1705 options[-1].location.top = rect.top + self.MENU_PADDING + \
1706 (len(options) - 1) * options[0].location.h + \
1707 (len(options) - 1) * self.MENU_MARGIN
1708 indicator = self.indicator = Sprite(self)
1709 indicator.add_frame(self.get_game().glyphs.get_tile(16))
1710
1711 def respond(self, event):
1712 if self.active and not self.get_game().editor.active:
1713 delegate = self.get_game().delegate
1714 if not self.advance_pressed:
1715 if delegate.compare(event, "advance") or not self.get_game().is_gamepad_mode() and \
1716 delegate.compare(event, "action"):
1717 self.advance_pressed = True
1718 self.get_game().sound_effects.play("start")
1719 elif delegate.compare(event, "cancel"):
1720 self.get_game().sound_effects.play("cancel")
1721 else:
1722 if delegate.compare(event, "cancel"):
1723 self.get_game().sound_effects.play("cancel")
1724 self.advance_pressed = False
1725 elif delegate.compare(event, "up"):
1726 self.get_game().sound_effects.play("menu-move")
1727 self.menu_index -= 1
1728 elif delegate.compare(event, "down"):
1729 self.get_game().sound_effects.play("menu-move")
1730 self.menu_index += 1
1731 elif delegate.compare(event, "advance") or delegate.compare(event, "action"):
1732 self.get_game().sound_effects.play("start")
1733 if self.menu_index == 0:
1734 self.deactivate()
1735 self.get_game().introduction.load()
1736 self.get_game().write_progress(0, 0)
1737 elif self.menu_index == 1:
1738 self.deactivate()
1739 self.get_game().levels.\
1740 levels[self.get_game().get_progress()[0]].begin()
1741 else:
1742 pass
1743 event.dict["command"] = ""
1744 if self.menu_index < 0:
1745 self.menu_index = len(self.options) - 1
1746 elif self.menu_index >= len(self.options):
1747 self.menu_index = 0
1748
1749 def deactivate(self):
1750 self.active = False
1751 self.audio_loop.stop()
1752
1753 def update(self):
1754 Animation.update(self)
1755 if self.active:
1756 self.background.update()
1757 if not self.advance_pressed:
1758 self.advance_plate.update()
1759 else:
1760 self.display_surface.blit(self.menu_background, self.menu_rect)
1761 for option in self.options:
1762 option.update()
1763 self.indicator.location.centery = self.options[self.menu_index]\
1764 .location.centery
1765 self.indicator.location.right = self.MENU_INDICATOR_LOCATION
1766 self.indicator.update()
1767 if random() < self.CHUNK_PROBABILITY and \
1768 get_busy_channel_count() < get_num_channels() - 5:
1769 for _ in xrange(randint(1, 4)):
1770 chunk = choice(self.audio_chunks)
1771 channel = chunk.play()
1772 if channel is not None and not isinstance(channel, tuple):
1773 channel.set_volume(random(), random())
1774 self.name_plate.update()
1775
1776
1777 class ScrollingBackground(Sprite):
1778
1779 DEFAULT_SIZE = 40 * 24, 40 * 24
1780 DEFAULT_SCROLL_SPEED = 2
1781 TURN_PROBABILITY = .0001
1782
1783 def __init__(self, parent, tiles=None, size=DEFAULT_SIZE, fill=(0, 0, 0),
1784 count=1, framerate=None):
1785 Sprite.__init__(self, parent, framerate)
1786 frames = []
1787 for _ in xrange(count):
1788 frames.append(Surface((size[0], size[1])))
1789 frames[-1].fill(fill)
1790 if tiles is None:
1791 tiles = self.get_all_tiles()
1792 tw = tiles[0].get_width()
1793 fw, fh = frames[0].get_size()
1794 ox = -(tw - (fw % tw)) / 2 if fw % tw else 0
1795 oy = -(tw - (fh % tw)) / 2 if fh % tw else 0
1796 for x in xrange(ox, fw - ox, tw):
1797 for y in xrange(oy, fh - oy, tw):
1798 for frame in frames:
1799 tile = choice(tiles)
1800 if random() >= .5:
1801 inverted = Surface((tw, tw))
1802 inverted.fill((255, 255, 255))
1803 inverted.blit(tile, (0, 0), None, BLEND_SUB)
1804 tile = inverted
1805 fx, fy = random() >= .5, random() >= .5
1806 if fx or fy:
1807 tile = flip(tile, fx, fy)
1808 frame.blit(tile, (x, y))
1809 for frame in frames:
1810 self.add_frame(frame)
1811 self.add_location(offset=(self.location.w, 0))
1812 self.add_location(offset=(0, self.location.h))
1813 self.add_location(offset=(self.location.w, self.location.h))
1814 self.set_scroll_step()
1815
1816 def get_all_tiles(self):
1817 tiles = []
1818 for level in self.get_game().levels.levels:
1819 tiles.extend(level.tiles)
1820 return tiles
1821
1822 def set_scroll_step(self, speed=DEFAULT_SCROLL_SPEED):
1823 self.scroll_step = get_delta(randrange(0, 360), speed)
1824
1825 def update(self):
1826 if random() < self.TURN_PROBABILITY:
1827 self.set_scroll_step()
1828 self.move(*self.scroll_step)
1829 if self.location.top > 0:
1830 self.move(dy=-self.location.h)
1831 if self.location.right < 0:
1832 self.move(self.location.w)
1833 if self.location.bottom < 0:
1834 self.move(dy=self.location.h)
1835 if self.location.left > 0:
1836 self.move(-self.location.w)
1837 Sprite.update(self)
1838
1839
1840 class Introduction(Animation):
1841
1842 BACKGROUND = (0, 0, 0)
1843 TEXT_COLOR = (255, 255, 255)
1844 INDICATOR_INDEX = 31
1845 TEXT = "ONE AFTERNOON IN YOUR/LIVING ROOM", "HOOKING UP YOUR TV GAME/SYSTEM", \
1846 "PLAYING CARTRIDGES FROM/YOUR COLLECTION", "SUDDENLY THE GRAPHICS/GLITCH UNCONTROLLABLY", \
1847 "MEDITATING YOU ACCESS/THE CPU TELEPATHICALLY", "FIXING GAMES BY SWAPPING/TILES IN MEMORY"
1848 INDENT = 48
1849 OFFSET = 128
1850 MARGIN = 12
1851 BLINK_INTERVAL = 400
1852 PANEL_MARGIN = 100
1853
1854 def __init__(self, parent):
1855 Animation.__init__(self, parent)
1856 ds = self.display_surface = self.get_display_surface()
1857 background = self.background = Surface(ds.get_size())
1858 background.fill(self.BACKGROUND)
1859 blocks = self.blocks = []
1860 glyphs = self.get_game().glyphs
1861 tile_height = glyphs.get_tile(0).get_height()
1862 for block in self.TEXT:
1863 blocks.append([])
1864 for ii, line in enumerate(block.split("/")):
1865 plate = Sprite(self)
1866 plate.add_frame(glyphs.\
1867 get_surface_from_text(line, self.TEXT_COLOR,
1868 background=self.BACKGROUND))
1869 plate.location.left = self.INDENT
1870 plate.location.top = ds.get_height() - self.OFFSET + ii * \
1871 (tile_height + self.MARGIN)
1872 blocks[-1].append(plate)
1873 indicator = self.indicator = Sprite(self)
1874 indicator.add_frame(glyphs.get_tile(self.INDICATOR_INDEX, self.TEXT_COLOR))
1875 indicator.location.top = ds.get_height() - self.OFFSET + 2 * \
1876 (tile_height + self.MARGIN)
1877 indicator.location.right = ds.get_width() - self.INDENT
1878 panels = self.panels = []
1879 for path in sorted(glob(join(self.get_resource("intro"), "*.png"))):
1880 panels.append(Sprite(self))
1881 panels[-1].load_from_path(path)
1882 panels[-1].location.midtop = ds.get_rect().centerx, self.PANEL_MARGIN
1883 self.register(self.blink, interval=self.BLINK_INTERVAL)
1884 self.subscribe(self.respond)
1885 self.play(self.blink)
1886
1887 def respond(self, event):
1888 if self.active and not self.get_game().editor.active:
1889 if self.get_game().delegate.compare(event, "action"):
1890 self.block_index += 1
1891 self.current_panel_index += 1
1892 if self.block_index == len(self.blocks):
1893 self.get_game().sound_effects.play("go")
1894 # self.get_game().sound_effects.play("granted")
1895 self.deactivate()
1896 self.get_game().levels.levels[0].begin()
1897 # elif self.block_index == len(self.blocks) - 1:
1898 # self.get_game().sound_effects.play("granted")
1899 else:
1900 self.get_game().sound_effects.play("next")
1901
1902 def deactivate(self):
1903 self.active = False
1904
1905 def load(self):
1906 self.activate()
1907 self.block_index = 0
1908 self.current_panel_index = 0
1909 self.indicator.unhide()
1910
1911 def activate(self):
1912 self.active = True
1913
1914 def blink(self):
1915 self.indicator.toggle_hidden()
1916
1917 def reset(self):
1918 self.deactivate()
1919
1920 def update(self):
1921 Animation.update(self)
1922 if self.active:
1923 ds = self.display_surface
1924 ds.blit(self.background, (0, 0))
1925 for block in self.blocks[self.block_index]:
1926 block.update()
1927 self.indicator.update()
1928 self.panels[self.current_panel_index].update()
1929
1930
1931 class Ending(Animation):
1932
1933 GLYPH_RANGE = 1, 31
1934 SCROLL_SPEED = 2
1935 TURN_PROBABILITY = .0001
1936 PLATE_SIZE = 312, 72
1937 PLATE_BACKGROUND = 0, 0, 0
1938 TEXT_COLOR = 255, 255, 255
1939 END_TEXT = "THE END!"
1940 SWAP_COUNT_TEXT = "SWAPS:"
1941 UNSUPPRESS_DELAY = 1000
1942
1943 def __init__(self, parent):
1944 Animation.__init__(self, parent)
1945 ds = self.display_surface = self.get_display_surface()
1946 end_plate = self.end_plate = Sprite(self)
1947 frame = Surface(self.PLATE_SIZE)
1948 frame.fill(self.PLATE_BACKGROUND)
1949 text_surface = self.get_game().glyphs.get_surface_from_text(self.END_TEXT,
1950 self.TEXT_COLOR)
1951 rect = text_surface.get_rect()
1952 rect.center = frame.get_rect().center
1953 frame.blit(text_surface, rect)
1954 end_plate.add_frame(frame)
1955 # end_plate.location.center = ds.get_rect().centerx, 3 * ds.get_height() / 8
1956 end_plate.location.center = ds.get_rect().center
1957 self.audio = Sound(self.get_resource("aud/end.ogg"))
1958 self.audio.set_volume(Level.VOLUME)
1959 self.subscribe(self.respond)
1960 self.register(self.unsuppress_advance)
1961
1962 def respond(self, event):
1963 if self.active and not self.suppress_commands and not self.get_game().editor.active \
1964 ((self.get_game().is_gamepad_mode() and self.get_game().delegate.compare(event, "advance")) or \
1965 (not self.get_game().is_gamepad_mode() and self.get_game().delegate.compare(event, "action"))):
1966 self.deactivate()
1967 self.get_game().reset()
1968
1969 def unsuppress_advance(self):
1970 self.suppress_commands = False
1971
1972 def set_swap_count(self, count):
1973 self.swap_count = count
1974
1975 def reset(self):
1976 self.deactivate()
1977 self.halt()
1978
1979 def deactivate(self):
1980 self.active = False
1981 self.audio.stop()
1982
1983 def activate(self):
1984 self.active = True
1985 self.set_swap_plate()
1986 self.set_background()
1987 self.set_scroll_step()
1988 self.suppress_commands = True
1989 self.play(self.unsuppress_advance, delay=self.UNSUPPRESS_DELAY,
1990 play_once=True)
1991 self.audio.play(-1)
1992
1993 def set_swap_plate(self):
1994 swap_count_plate = self.swap_count_plate = Sprite(self)
1995 frame = Surface(self.PLATE_SIZE)
1996 frame.fill(self.PLATE_BACKGROUND)
1997 text_surface = self.get_game().glyphs.\
1998 get_surface_from_text("%s %i" % (self.SWAP_COUNT_TEXT,
1999 self.swap_count), self.TEXT_COLOR)
2000 rect = text_surface.get_rect()
2001 rect.center = frame.get_rect().center
2002 frame.blit(text_surface, rect)
2003 swap_count_plate.add_frame(frame)
2004 swap_count_plate.location.center = self.display_surface.get_rect().centerx, \
2005 5 * self.display_surface.get_height() / 8
2006
2007 def set_background(self):
2008 background = self.background = Sprite(self)
2009 glyphs = self.get_game().glyphs
2010 tw = glyphs.get_tile(0).get_width()
2011 ds = self.get_display_surface()
2012 frame = Surface(ds.get_size())
2013 for xi, x in enumerate(xrange(0, ds.get_width(), tw)):
2014 for yi, y in enumerate(xrange(0, ds.get_height(), tw)):
2015 background_color = Color(0, 0, 0)
2016 background_color.hsla = randrange(0, 360), 100, 50, 100
2017 foreground_color = Color(0, 0, 0)
2018 foreground_color.hsla = (background_color.hsla[0] + 180) % 360, \
2019 100, 50, 100
2020 surface = Surface([tw] * 2)
2021 surface.fill(background_color)
2022 surface.blit(glyphs.get_tile(randint(*self.GLYPH_RANGE)), (0, 0))
2023 frame.blit(surface, (x, y))
2024 background.add_frame(frame)
2025 background.add_location(offset=(background.location.w, 0))
2026 background.add_location(offset=(0, background.location.h))
2027 background.add_location(offset=(background.location.w,
2028 background.location.h))
2029
2030 def set_scroll_step(self):
2031 self.scroll_step = get_delta(randrange(0, 360), self.SCROLL_SPEED)
2032
2033 def update(self):
2034 Animation.update(self)
2035 if self.active:
2036 if random() < self.TURN_PROBABILITY:
2037 self.set_scroll_step()
2038 self.background.move(*self.scroll_step)
2039 if self.background.location.top > 0:
2040 self.background.move(dy=-self.background.location.h)
2041 if self.background.location.right < 0:
2042 self.background.move(self.background.location.w)
2043 if self.background.location.bottom < 0:
2044 self.background.move(dy=self.background.location.h)
2045 if self.background.location.left > 0:
2046 self.background.move(-self.background.location.w)
2047 self.background.update()
2048 self.end_plate.update()
2049 # self.swap_count_plate.update()