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