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