default level
authorFrank DeMarco <if.self.end@gmail.com>
Sat, 16 Feb 2019 20:15:06 +0000 (15:15 -0500)
committerFrank DeMarco <if.self.end@gmail.com>
Sat, 16 Feb 2019 20:15:06 +0000 (15:15 -0500)
PictureProcessing.py
config

index 57049d1..6f41fba 100644 (file)
@@ -1,4 +1,4 @@
-from os.path import basename, join
+from os.path import basename, join, exists
 from random import random, randrange, choice, randint
 from glob import glob
 from array import array
@@ -31,12 +31,12 @@ class PictureProcessing(Game):
         self.get_configuration().type_declarations.add("bool", "mouse", "resets")
         self.sound_effects = SoundEffects(self)
         self.glyphs = Glyphs(self)
+        self.editor = Editor(self)
         self.levels = Levels(self)
         self.interface = Interface(self)
         self.title = Title(self)
         self.introduction = Introduction(self)
         self.ending = Ending(self)
-        self.editor = Editor(self)
         self.subscribe(self.respond)
         self.subscribe(self.respond, MOUSEBUTTONDOWN)
         self.reset()
@@ -114,15 +114,47 @@ class Editor(GameChild):
         GameChild.__init__(self, parent)
         self.brush_position = Vector()
         self.subscribe(self.respond)
+        self.levels = []
         self.painting = []
         for _ in xrange(self.TILE_SIZE):
             self.painting.append([0] * self.TILE_SIZE)
+        self.at_level_select = True
 
     def reset(self):
         self.deactivate()
 
     def activate(self):
         self.active = True
+        if not self.levels:
+            self.read_levels()
+
+    def read_levels(self):
+        levels = self.levels = []
+        for path in glob(join(self.get_new_levels_path(), "*")):
+            level = Level(self)
+            if level.load_grid_file(path):
+                levels.append(level)
+            else:
+                print "Failed to load level %s" % path
+        self.levels.append(self.get_default_level())
+
+    def get_default_level(self):
+        level = Level(self)
+        level.load_default()
+        return level
+
+    def get_default_tile_path(self):
+        return join(self.get_tiles_path(),
+                    self.get_configuration("editor", "default-tile-file-name"))
+
+    def get_tiles_path(self):
+        config = self.get_configuration("editor")
+        return join(config["root-directory"], config["tiles-directory"])
+
+    def get_new_levels_path(self):
+        config = self.get_configuration("editor")
+        return join(config["root-directory"], config["levels-directory"],
+                    config["new-directory"])
 
     def deactivate(self):
         self.active = False
@@ -160,13 +192,14 @@ class Editor(GameChild):
         if self.active:
             ds = self.get_display_surface()
             ds.fill((0, 0, 0))
+            self.levels[0].update()
             pz = self.PIXEL_ZOOM
             for x, row in enumerate(self.painting):
                 for y, col in enumerate(row):
                     if col == 0:
-                        color = (0, 0, 0)
+                        color = 0, 0, 0
                     else:
-                        color = (255, 255, 255)
+                        color = 255, 255, 255
                     ds.fill(color, (x * pz, y * pz, pz, pz))
             bx, by = self.brush_position
             cr = bx * pz, by * pz, pz, pz
@@ -275,7 +308,9 @@ class Levels(GameChild):
         levels = self.levels = []
         for ii, directory in enumerate(sorted(glob(join(self.get_resource("tiles"),
                                                         "[0-9]*")))):
-            levels.append(Level(self, directory, self.TITLES[ii]))
+            level = Level(self)
+            level.load_map_file(directory, self.TITLES[ii])
+            levels.append(level)
 
     def reset(self):
         self.deactivate()
@@ -299,9 +334,9 @@ class Levels(GameChild):
     def is_final_level(self):
         return self.get_current_level_index() == len(self.levels) - 1
 
-    def load_next_level(self):
+    def begin_next_level(self):
         self.get_game().interface.deactivate()
-        self.levels[self.get_current_level_index() + 1].load()
+        self.levels[self.get_current_level_index() + 1].begin()
 
     def get_current_title(self):
         return self.TITLES[self.get_current_level_index()]
@@ -318,10 +353,17 @@ class Level(Animation):
     TITLE_BORDER_SIZE = 20, 8
     TITLE_BORDER_SPEED = 400
     TITLE_LENGTH = 5000
+    DEFAULT_LEVEL_TITLE = "DEFAULT"
 
-    def __init__(self, parent, directory, title):
+    def __init__(self, parent):
         Animation.__init__(self, parent)
-        ds = self.display_surface = self.get_display_surface()
+        self.at_title = False
+        self.clip_audio = None
+        self.full_audio = None
+        self.title_plate = None
+
+    def load_map_file(self, directory, title):
+        ds = self.get_display_surface()
         tiles = self.tiles = []
         original = self.original_tiles = []
         for path in sorted(glob(join(directory, "*.png"))):
@@ -329,7 +371,7 @@ class Level(Animation):
             tiles.append(scale(original[-1], [original[-1].get_width() * \
                                               PictureProcessing.SCALE] * 2))
         colored = self.colored_tiles = {}
-        rects = self.tile_rects = {}
+        self.set_entire_grid()
         block = 0
         for line in open(join(directory, "map")):
             if line.strip() == "":
@@ -343,7 +385,6 @@ class Level(Animation):
                         digits = map(int, field.split())
                         if len(digits) == 1:
                             index = digits[0]
-                            rects[index] = []
                         elif len(digits) == 3:
                             surface = Surface(tiles[index].get_size())
                             surface.fill(digits)
@@ -352,7 +393,13 @@ class Level(Animation):
                         else:
                             if len(digits) == 2:
                                 digits *= 2
-                            rects[index].append((digits[0:2], digits[2:4]))
+                            for x in xrange(digits[0], digits[2] + 1):
+                                for y in xrange(digits[1], digits[3] + 1):
+                                    self.grid[x][y] = index
+        for x, row in enumerate(self.grid):
+            for y, value in enumerate(row):
+                if value is None:
+                    self.grid[x][y] = self.default_tile_index
         title_plate = self.title_plate = Sprite(self)
         title_plate.add_frame(self.get_game().glyphs.get_surface_from_text(title, background=(0, 0, 0)))
         title_background = self.title_background = Surface(ds.get_size())
@@ -372,11 +419,40 @@ class Level(Animation):
         path = glob(join(self.get_resource("aud/level"), basename(directory) + "*"))[0]
         self.full_audio = Sound(path)
         self.full_audio.set_volume(self.VOLUME)
-        self.clip_audio = None
         self.title_border_offset = 0
         self.register(self.rotate_title_border, interval=self.TITLE_BORDER_SPEED)
         self.play(self.rotate_title_border)
         self.subscribe(self.respond)
+        self.set_swap_status()
+
+    def set_entire_grid(self, index=None):
+        self.grid = []
+        ds = self.get_display_surface()
+        for x in xrange(ds.get_width() / self.tiles[0].get_width()):
+            self.grid.append([index] *
+                             (ds.get_height() / self.tiles[0].get_height()))
+
+    def load_grid_file(self):
+        return False
+
+    def load_default(self):
+        tiles = self.tiles = []
+        original = self.original_tiles = []
+        path = self.get_game().editor.get_default_tile_path()
+        if exists(path):
+            default = load(path).convert()
+        else:
+            default = Surface([Editor.TILE_SIZE] * 2)
+            default.fill((255, 255, 255))
+            for y in xrange(default.get_height()):
+                for x in xrange([1, 2, 2, 2, 2, 2, 7, 8][y]):
+                    default.set_at((x, y), (200, 200, 200))
+        self.original_tiles = [default]
+        self.tiles = [
+            scale(default, [default.get_width() * PictureProcessing.SCALE] * 2)]
+        self.colored_tiles = {}
+        self.set_entire_grid(0)
+        self.set_swap_status()
 
     def rotate_title_border(self):
         self.title_border_offset -= 1
@@ -391,19 +467,24 @@ class Level(Animation):
         self.get_game().interface.setup()
         self.get_game().sound_effects.play("granted")
 
-    def load(self):
+    def begin(self):
         self.parent.activate()
         self.parent.current_level = self
-        self.set_audio()
-        self.set_swap_status()
+        if self.full_audio is not None:
+            self.set_audio()
+        self.set_swap_status(True)
         self.parent.stop_audio()
-        self.clip_audio.play(-1)
-        self.at_title = True
-        self.title_plate.set_alpha(255)
-        dsr = self.display_surface.get_rect()
-        self.title_plate.location.center = dsr.centerx, dsr.centery + self.TITLE_OFFSET
-        self.title_plate.unhide()
-        self.title_elapsed = 0
+        if self.clip_audio is not None:
+            self.clip_audio.play(-1)
+        if self.title_plate is not None:
+            self.at_title = True
+            self.title_plate.set_alpha(255)
+            dsr = self.get_display_surface().get_rect()
+            self.title_plate.location.center = dsr.centerx, dsr.centery + self.TITLE_OFFSET
+            self.title_plate.unhide()
+            self.title_elapsed = 0
+        else:
+            self.close_title()
 
     def set_audio(self):
         if version.vernum < (1, 9, 2):
@@ -422,29 +503,34 @@ class Level(Animation):
             clip = self.clip_audio = Sound(buffer=buf)
         clip.set_volume(volume)
 
-    def set_swap_status(self):
+    def set_swap_status(self, randomize=False):
         swap_status = self.swap_status = {}
-        swapped = False
-        indicies = set(self.tile_rects.keys() + [self.default_tile_index])
-        while not swapped:
-            available_swap_positions = indicies.copy()
-            for index in indicies:
-                if len(available_swap_positions) == 1 and \
-                   list(available_swap_positions)[0] == index:
-                    break
-                while True:
-                    swap_status[index] = choice(list(available_swap_positions))
-                    if swap_status[index] != index:
+        if randomize and len(self.tiles) > 1:
+            swapped = False
+            indicies = set(range(len(self.tiles)))
+            while not swapped:
+                available_swap_positions = indicies.copy()
+                for index in indicies:
+                    if len(available_swap_positions) == 1 and \
+                       list(available_swap_positions)[0] == index:
                         break
-                available_swap_positions.remove(swap_status[index])
-                if not available_swap_positions:
-                    swapped = True
-                    break
+                    while True:
+                        swap_status[index] = choice(list(available_swap_positions))
+                        if swap_status[index] != index:
+                            break
+                    available_swap_positions.remove(swap_status[index])
+                    if not available_swap_positions:
+                        swapped = True
+                        break
+        else:
+            for index in xrange(len(self.tiles)):
+                swap_status[index] = index
 
     def stop_all_audio(self):
         if self.clip_audio:
             self.clip_audio.stop()
-        self.full_audio.stop()
+        if self.full_audio:
+            self.full_audio.stop()
 
     def is_solved(self):
         return all(index == position for index, position in self.swap_status.iteritems())
@@ -457,7 +543,7 @@ class Level(Animation):
 
     def update(self):
         Animation.update(self)
-        ds = self.display_surface
+        ds = self.get_display_surface()
         if self.at_title:
             ds.blit(self.title_background, (0, 0))
             bw, bh = self.TITLE_BORDER_SIZE
@@ -484,27 +570,17 @@ class Level(Animation):
             if self.title_elapsed >= self.TITLE_LENGTH:
                 self.close_title()
         else:
-            for xi, x in enumerate(xrange(0, ds.get_width(),
+            for grid_x, screen_x in enumerate(xrange(0, ds.get_width(),
                                           self.tiles[0].get_width())):
-                for yi, y in enumerate(xrange(0, ds.get_height(),
+                for grid_y, screen_y in enumerate(xrange(0, ds.get_height(),
                                               self.tiles[0].get_height())):
-                    current_tile_index = self.default_tile_index
-                    for tile_index, rects in self.tile_rects.iteritems():
-                        found = False
-                        for rect in rects:
-                            if rect[0][0] <= xi <= rect[1][0] and rect[0][1] <= yi \
-                               <= rect[1][1]:
-                                current_tile_index = tile_index
-                                found = True
-                                break
-                        if found:
-                            break
-                    swap_index = self.swap_status[current_tile_index]
+                    tile_index = self.grid[grid_x][grid_y]
+                    swap_index = self.swap_status[tile_index]
                     if swap_index in self.colored_tiles.keys():
                         tile = self.colored_tiles[swap_index]
                     else:
                         tile = self.tiles[swap_index]
-                    ds.blit(tile, (x, y))
+                    ds.blit(tile, (screen_x, screen_y))
 
 
 class Interface(Animation):
@@ -609,9 +685,17 @@ class Interface(Animation):
         if self.is_countdown_active():
             self.countdown_plate.toggle_hidden()
             if self.countdown_plate.is_hidden():
-                self.get_title_plate().unhide()
+                self.set_title_plate_visibility(False)
+            else:
+                self.set_title_plate_visibility(True)
+
+    def set_title_plate_visibility(self, visible=True):
+        title_plate = self.get_title_plate()
+        if title_plate is not None:
+            if visible:
+                title_plate.unhide()
             else:
-                self.get_title_plate().hide()
+                title_plate.hide()
 
     def is_countdown_active(self):
         return 0 < self.get_game().levels.current_level.get_remaining_swap_count() <= self.COUNTDOWN_THRESHOLD
@@ -632,7 +716,7 @@ class Interface(Animation):
                     self.get_game().ending.activate()
                     self.get_game().levels.deactivate()
                 else:
-                    self.get_game().levels.load_next_level()
+                    self.get_game().levels.begin_next_level()
             elif not self.closed:
                 if delegate.compare(event, "cancel"):
                     effects.play("cancel")
@@ -662,10 +746,10 @@ class Interface(Animation):
                                 self.set_countdown_plate()
                                 if self.is_countdown_active():
                                     self.countdown_plate.unhide()
-                                    self.get_title_plate().hide()
+                                    self.set_title_plate_visibility(True)
                                     self.reset_timer(self.blink_countdown)
                                 else:
-                                    self.get_title_plate().unhide()
+                                    self.set_title_plate_visibility(False)
                                     self.countdown_plate.hide()
                             else:
                                 effects.play("clear")
@@ -780,7 +864,9 @@ class Interface(Animation):
         self.play(self.blink)
         self.play(self.blink_countdown)
         dsr = self.get_display_surface().get_rect()
-        self.get_title_plate().location.midbottom = dsr.centerx, dsr.h
+        title_plate = self.get_title_plate()
+        if title_plate is not None:
+            title_plate.location.midbottom = dsr.centerx, dsr.h
         self.set_countdown_plate()
         self.countdown_plate.hide()
 
@@ -835,7 +921,9 @@ class Interface(Animation):
                 self.swap_plate.update()
                 self.hide_plate.update()
                 self.move_plate.update()
-                self.get_title_plate().update()
+                title_plate = self.get_title_plate()
+                if title_plate is not None:
+                    title_plate.update()
                 self.countdown_plate.update()
 
 
@@ -1008,7 +1096,7 @@ class Title(Animation):
                     elif self.menu_index == 1:
                         self.deactivate()
                         self.get_game().levels.\
-                            levels[self.get_game().get_progress()[0]].load()
+                            levels[self.get_game().get_progress()[0]].begin()
                     else:
                         pass
                     event.dict["command"] = ""
@@ -1112,7 +1200,7 @@ class Introduction(Animation):
                     self.get_game().sound_effects.play("go")
                     # self.get_game().sound_effects.play("granted")
                     self.deactivate()
-                    self.get_game().levels.levels[0].load()
+                    self.get_game().levels.levels[0].begin()
                 # elif self.block_index == len(self.blocks) - 1:
                     # self.get_game().sound_effects.play("granted")
                 else:
diff --git a/config b/config
index be2aec0..2ffdfc2 100644 (file)
--- a/config
+++ b/config
@@ -36,3 +36,11 @@ resets = yes
 [video-recordings]
 framerate = 100
 temp-directory = /tmp/
+
+[editor]
+root-directory = editor
+levels-directory = levels
+original-directory = original
+new-directory = new
+tiles-directory = tiles
+default-tile-file-name = default.png