view arrows
authorFrank DeMarco <if.self.end@gmail.com>
Sun, 17 Feb 2019 02:37:27 +0000 (21:37 -0500)
committerFrank DeMarco <if.self.end@gmail.com>
Sun, 17 Feb 2019 02:37:27 +0000 (21:37 -0500)
PictureProcessing.py
config

index 6f41fba..cab00b9 100644 (file)
@@ -46,8 +46,9 @@ class PictureProcessing(Game):
 
     def write_wallpaper(self):
         index = argv.index("--wallpaper")
-        surface = Surface(map(int, argv[index + 1:index + 3]))
-        self.title.paint_background(surface)
+        width, height = map(int, argv[index + 1:index + 3])
+        wallpaper = ScrollingBackground(self, size=(width, height))
+        surface = wallpaper.get_current_frame()
         if argv[index + 3] == "-":
             from tempfile import mkstemp
             from sys import stdout
@@ -109,6 +110,20 @@ class Editor(GameChild):
     TILE_SIZE = 8
     PIXEL_ZOOM = 32
     CURSOR_THICKNESS = 3
+    UI_EDITOR_HEADING = "EDITOR"
+    UI_EXIT = "ESC: EXIT", "B: EXIT"
+    UI_EDIT = "ENTER: EDIT", "A: EDIT"
+    UI_TILES = "\\24 TILES"
+    UI_PLAY = "TEST \\26"
+    UI_PAINT = "\\25 PAINT"
+    UI_MUSIC = "\\27 MUSIC"
+    UI_SAVE_EXIT = "ESC: SAVE", "B: SAVE"
+    UI_HIDE_MENU = "ENTER: HIDE", "A: HIDE"
+    UI_HEADING_MARGIN = 60
+    UI_BUTTONS_MARGIN = 60
+    UI_VIEW_MARGIN = 120
+    UI_DIRECTIONS_MARGIN = 0
+    UI_ALIGN_ONE_THIRD_BOTTOM, UI_ALIGN_TWO_THIRDS_BOTTOM = range(2)
 
     def __init__(self, parent):
         GameChild.__init__(self, parent)
@@ -119,14 +134,66 @@ class Editor(GameChild):
         for _ in xrange(self.TILE_SIZE):
             self.painting.append([0] * self.TILE_SIZE)
         self.at_level_select = True
+        self.level_index = 0
+        self.loaded = False
+        self.menu_hidden = False
 
     def reset(self):
         self.deactivate()
 
     def activate(self):
         self.active = True
-        if not self.levels:
-            self.read_levels()
+        if not self.loaded:
+            self.load()
+
+    def load(self):
+        self.read_levels()
+        glyphs = self.get_game().glyphs
+        dsr = self.get_display_surface().get_rect()
+        self.editor_heading = self.get_label(self.UI_EDITOR_HEADING, True)
+        self.editor_heading.location.midtop = dsr.centerx, self.UI_HEADING_MARGIN
+        self.arrows = Sprite(self), Sprite(self)
+        self.background = ScrollingBackground(self, self.levels[-1].tiles)
+        self.level_select_exit = self.get_label(
+            self.UI_EXIT, align=self.UI_ALIGN_ONE_THIRD_BOTTOM,
+            margin=self.UI_BUTTONS_MARGIN)
+        self.level_select_edit = self.get_label(
+            self.UI_EDIT, align=self.UI_ALIGN_TWO_THIRDS_BOTTOM,
+            margin=self.UI_BUTTONS_MARGIN)
+        self.view_exit = self.get_label(
+            self.UI_SAVE_EXIT, align=self.UI_ALIGN_ONE_THIRD_BOTTOM,
+            margin=self.UI_VIEW_MARGIN)
+        self.view_hide = self.get_label(
+            self.UI_HIDE_MENU, align=self.UI_ALIGN_TWO_THIRDS_BOTTOM,
+            margin=self.UI_VIEW_MARGIN)
+        self.arrow_tiles = self.get_label(self.UI_TILES)
+        self.arrow_tiles.location.midtop = dsr.midtop
+        self.arrow_play = self.get_label(self.UI_PLAY)
+        self.arrow_play.location.midright = dsr.midright
+        self.arrow_paint = self.get_label(self.UI_PAINT)
+        self.arrow_paint.location.midbottom = dsr.midbottom
+        self.arrow_music = self.get_label(self.UI_MUSIC)
+        self.arrow_music.location.midleft = dsr.midleft
+        self.loaded = True
+
+    def get_label(self, text, rainbow=False, align=None, margin=0):
+        if isinstance(text, tuple):
+            text = self.get_game().select_text(text)
+        surface = self.get_game().glyphs.get_surface_from_text(
+            text, background=([0, 80][rainbow], 0, 0))
+        if rainbow:
+            label = RainbowSprite(self, surface)
+        else:
+            label = Sprite(self)
+            label.add_frame(surface)
+        if align is not None:
+            dsr = self.get_display_surface().get_rect()
+            if align == self.UI_ALIGN_ONE_THIRD_BOTTOM:
+                label.location.midbottom = dsr.centerx / 2, dsr.h - margin
+            elif align == self.UI_ALIGN_TWO_THIRDS_BOTTOM:
+                label.location.midbottom = \
+                    dsr.centerx / 2 + dsr.centerx, dsr.h - margin
+        return label
 
     def read_levels(self):
         levels = self.levels = []
@@ -140,7 +207,7 @@ class Editor(GameChild):
 
     def get_default_level(self):
         level = Level(self)
-        level.load_default()
+        level.load()
         return level
 
     def get_default_tile_path(self):
@@ -163,6 +230,19 @@ class Editor(GameChild):
         if self.active:
             compare = self.get_delegate().compare
             dx, dy = 0, 0
+            if compare(event, "action"):
+                if self.at_level_select:
+                    self.at_level_select = False
+                    self.at_view = True
+                    self.menu_hidden = False
+                elif self.at_view:
+                    self.menu_hidden = not self.menu_hidden
+            if compare(event, "cancel"):
+                if self.at_level_select:
+                    self.deactivate()
+                elif self.at_view:
+                    self.at_view = False
+                    self.at_level_select = True
             if compare(event, "right"):
                 dx = 1
             elif compare(event, "down"):
@@ -192,19 +272,36 @@ 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
-                    else:
-                        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
-            draw.rect(ds, (255, 255, 255), cr, self.CURSOR_THICKNESS)
-            draw.rect(ds, (0, 0, 0), cr, 1)
+            if self.at_level_select:
+                self.background.update()
+                self.editor_heading.update()
+                self.level_select_exit.update()
+                self.level_select_edit.update()
+                preview = self.levels[self.level_index].preview
+                rect = preview.get_rect()
+                rect.center = ds.get_rect().center
+                ds.blit(preview, rect)
+            else:
+                self.levels[self.level_index].update()
+                if not self.menu_hidden:
+                    self.view_exit.update()
+                    self.view_hide.update()
+                    self.arrow_tiles.update()
+                    self.arrow_play.update()
+                    self.arrow_paint.update()
+                    self.arrow_music.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
+                        else:
+                            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
+                draw.rect(ds, (255, 255, 255), cr, self.CURSOR_THICKNESS)
+                draw.rect(ds, (0, 0, 0), cr, 1)
 
 
 class SoundEffects(GameChild):
@@ -278,16 +375,38 @@ class Glyphs(GameChild):
     def get_surface_from_text(self, text, color=None, spacing=1,
                               key=TRANSPARENT_COLOR, background=None):
         tw, th = self.tiles[0].get_size()
-        surface = Surface((len(text) * tw + spacing * len(text), th))
+        tiles = self.get_tiles(text, color)
+        surface = Surface((len(tiles) * tw + spacing * len(tiles), th))
         if background is None:
             surface.set_colorkey(key)
             surface.fill(key)
         else:
             surface.fill(background)
         for ii, x in enumerate(xrange(0, surface.get_width(), tw + spacing)):
-            surface.blit(self.get_tile(ord(text[ii]), color), (x, 0))
+            surface.blit(tiles[ii], (x, 0))
         return surface
 
+    def get_tiles(self, text, color):
+        tiles = []
+        index = 0
+        while index < len(text):
+            if text[index] == "\\":
+                index += 1
+                if text[index] == "\\":
+                    tile_index = ord(text[index])
+                    index += 1
+                else:
+                    code = ""
+                    while index < len(text) and text[index].isdigit():
+                        code += text[index]
+                        index += 1
+                    tile_index = int(code)
+            else:
+                tile_index = ord(text[index])
+                index += 1
+            tiles.append(self.get_tile(tile_index, color))
+        return tiles
+
     def get_tile(self, index, color=None):
         tile = self.tiles[index]
         if color is not None:
@@ -309,7 +428,7 @@ class Levels(GameChild):
         for ii, directory in enumerate(sorted(glob(join(self.get_resource("tiles"),
                                                         "[0-9]*")))):
             level = Level(self)
-            level.load_map_file(directory, self.TITLES[ii])
+            level.load(Level.LOAD_MAP_FILE, directory, self.TITLES[ii])
             levels.append(level)
 
     def reset(self):
@@ -354,6 +473,8 @@ class Level(Animation):
     TITLE_BORDER_SPEED = 400
     TITLE_LENGTH = 5000
     DEFAULT_LEVEL_TITLE = "DEFAULT"
+    LOAD_DEFAULT, LOAD_GRID_FILE, LOAD_MAP_FILE = range(3)
+    PREVIEW_SCALE = 2
 
     def __init__(self, parent):
         Animation.__init__(self, parent)
@@ -362,6 +483,15 @@ class Level(Animation):
         self.full_audio = None
         self.title_plate = None
 
+    def load(self, method=LOAD_DEFAULT, directory=None, title=None):
+        if method == self.LOAD_GRID_FILE:
+            self.load_grid_file()
+        elif method == self.LOAD_MAP_FILE:
+            self.load_map_file(directory, title)
+        else:
+            self.load_default()
+        self.set_preview()
+
     def load_map_file(self, directory, title):
         ds = self.get_display_surface()
         tiles = self.tiles = []
@@ -454,12 +584,25 @@ class Level(Animation):
         self.set_entire_grid(0)
         self.set_swap_status()
 
+    def set_preview(self):
+        tw, th = self.original_tiles[0].get_size()
+        preview = self.preview = Surface(
+            (tw * len(self.grid) * self.PREVIEW_SCALE,
+             th * len(self.grid[0]) * self.PREVIEW_SCALE))
+        for grid_x, surface_x in enumerate(xrange(0, preview.get_width(),
+                                                  tw * self.PREVIEW_SCALE)):
+            for grid_y, surface_y in enumerate(xrange(0, preview.get_height(),
+                                                      th * self.PREVIEW_SCALE)):
+                scaled = scale(self.original_tiles[self.grid[grid_x][grid_y]],
+                               [tw * self.PREVIEW_SCALE] * 2)
+                preview.blit(scaled, (surface_x, surface_y))
+
     def rotate_title_border(self):
         self.title_border_offset -= 1
 
     def respond(self, event):
         if self.parent.active and self.parent.current_level == self and self.at_title and \
-           self.get_game().delegate.compare(event, "advance"):
+           self.get_game().delegate.compare(event, "advance") and not self.get_game().editor.active:
             self.close_title()
 
     def close_title(self):
@@ -701,7 +844,7 @@ class Interface(Animation):
         return 0 < self.get_game().levels.current_level.get_remaining_swap_count() <= self.COUNTDOWN_THRESHOLD
 
     def respond(self, event, suppress_sound=False):
-        if self.active and not self.suppressing_commands:
+        if self.active and not self.suppressing_commands and not self.get_game().editor.active:
             delegate = self.get_game().delegate
             effects = self.get_game().sound_effects
             is_pad_mode = self.get_game().is_gamepad_mode()
@@ -932,15 +1075,11 @@ class Title(Animation):
     BLINK_INTERVAL = 400
     MENU_OFFSET = 100
     MENU_WIDTH = 440
-    BACKGROUND_SIZE = 40
     ADVANCE_TEXT = "PRESS ENTER", "PRESS START"
     MENU_OPTIONS = "NEW GAME", "CONTINUE"
     MENU_MARGIN = 12
     MENU_PADDING = 16
     TRANSPARENT_COLOR = (128, 128, 128)
-    TURN_PROBABILITY = .0001
-    SCROLL_SPEED = 2
-    BACKGROUND_BACKGROUND = (0, 0, 0)
     BACKGROUND_FRAME_COUNT = 1
     BACKGROUND_FRAMERATE = 200
     MENU_INDICATOR_LOCATION = 224
@@ -988,51 +1127,13 @@ class Title(Animation):
         self.activate()
         self.advance_pressed = False
         self.advance_plate.unhide()
-        self.set_scroll_step()
+        self.background.set_scroll_step()
         self.menu_index = 0
 
     def set_background(self):
-        background = self.background = Sprite(self, self.BACKGROUND_FRAMERATE)
-        tw = self.get_all_tiles()[0].get_width()
-        frames = []
-        for _ in xrange(self.BACKGROUND_FRAME_COUNT):
-            frames.append(Surface([self.BACKGROUND_SIZE * tw] * 2))
-            frames[-1].fill(self.BACKGROUND_BACKGROUND)
-        self.paint_background(frames)
-        for frame in frames:
-            background.add_frame(frame)
-        background.add_location(offset=(background.location.w, 0))
-        background.add_location(offset=(0, background.location.h))
-        background.add_location(offset=(background.location.w,
-                                        background.location.h))
-
-    def get_all_tiles(self):
-        tiles = []
-        for level in self.get_game().levels.levels:
-            tiles.extend(level.tiles)
-        return tiles
-
-    def paint_background(self, frames):
-        if isinstance(frames, Surface):
-            frames = [frames]
-        tiles = self.get_all_tiles()
-        tw = tiles[0].get_width()
-        fw, fh = frames[0].get_size()
-        ox = -(tw - (fw % tw)) / 2 if fw % tw else 0
-        oy = -(tw - (fh % tw)) / 2 if fh % tw else 0
-        for x in xrange(ox, fw - ox, tw):
-            for y in xrange(oy, fh - oy, tw):
-                for frame in frames:
-                    tile = choice(tiles)
-                    if random() >= .5:
-                        inverted = Surface((tw, tw))
-                        inverted.fill((255, 255, 255))
-                        inverted.blit(tile, (0, 0), None, BLEND_SUB)
-                        tile = inverted
-                    fx, fy = random() >= .5, random() >= .5
-                    if fx or fy:
-                        tile = flip(tile, fx, fy)
-                    frame.blit(tile, (x, y))
+        self.background = ScrollingBackground(self,
+            count=self.BACKGROUND_FRAME_COUNT,
+            framerate=self.BACKGROUND_FRAMERATE)
 
     def activate(self):
         self.active = True
@@ -1064,11 +1165,8 @@ class Title(Animation):
         indicator = self.indicator = Sprite(self)
         indicator.add_frame(self.get_game().glyphs.get_tile(16))
 
-    def set_scroll_step(self):
-        self.scroll_step = get_delta(randrange(0, 360), self.SCROLL_SPEED)
-
     def respond(self, event):
-        if self.active:
+        if self.active and not self.get_game().editor.active:
             delegate = self.get_game().delegate
             if not self.advance_pressed:
                 if delegate.compare(event, "advance") or not self.get_game().is_gamepad_mode() and \
@@ -1112,17 +1210,6 @@ class Title(Animation):
     def update(self):
         Animation.update(self)
         if self.active:
-            if random() < self.TURN_PROBABILITY:
-                self.set_scroll_step()
-            self.background.move(*self.scroll_step)
-            if self.background.location.top > 0:
-                self.background.move(dy=-self.background.location.h)
-            if self.background.location.right < 0:
-                self.background.move(self.background.location.w)
-            if self.background.location.bottom < 0:
-                self.background.move(dy=self.background.location.h)
-            if self.background.location.left > 0:
-                self.background.move(-self.background.location.w)
             self.background.update()
             if not self.advance_pressed:
                 self.advance_plate.update()
@@ -1144,6 +1231,69 @@ class Title(Animation):
             self.name_plate.update()
 
 
+class ScrollingBackground(Sprite):
+
+    DEFAULT_SIZE = 40 * 24, 40 * 24
+    DEFAULT_SCROLL_SPEED = 2
+    TURN_PROBABILITY = .0001
+
+    def __init__(self, parent, tiles=None, size=DEFAULT_SIZE, fill=(0, 0, 0),
+                 count=1, framerate=None):
+        Sprite.__init__(self, parent, framerate)
+        frames = []
+        for _ in xrange(count):
+            frames.append(Surface((size[0], size[1])))
+            frames[-1].fill(fill)
+        if tiles is None:
+            tiles = self.get_all_tiles()
+        tw = tiles[0].get_width()
+        fw, fh = frames[0].get_size()
+        ox = -(tw - (fw % tw)) / 2 if fw % tw else 0
+        oy = -(tw - (fh % tw)) / 2 if fh % tw else 0
+        for x in xrange(ox, fw - ox, tw):
+            for y in xrange(oy, fh - oy, tw):
+                for frame in frames:
+                    tile = choice(tiles)
+                    if random() >= .5:
+                        inverted = Surface((tw, tw))
+                        inverted.fill((255, 255, 255))
+                        inverted.blit(tile, (0, 0), None, BLEND_SUB)
+                        tile = inverted
+                    fx, fy = random() >= .5, random() >= .5
+                    if fx or fy:
+                        tile = flip(tile, fx, fy)
+                    frame.blit(tile, (x, y))
+        for frame in frames:
+            self.add_frame(frame)
+        self.add_location(offset=(self.location.w, 0))
+        self.add_location(offset=(0, self.location.h))
+        self.add_location(offset=(self.location.w, self.location.h))
+        self.set_scroll_step()
+
+    def get_all_tiles(self):
+        tiles = []
+        for level in self.get_game().levels.levels:
+            tiles.extend(level.tiles)
+        return tiles
+
+    def set_scroll_step(self, speed=DEFAULT_SCROLL_SPEED):
+        self.scroll_step = get_delta(randrange(0, 360), speed)
+
+    def update(self):
+        if random() < self.TURN_PROBABILITY:
+            self.set_scroll_step()
+        self.move(*self.scroll_step)
+        if self.location.top > 0:
+            self.move(dy=-self.location.h)
+        if self.location.right < 0:
+            self.move(self.location.w)
+        if self.location.bottom < 0:
+            self.move(dy=self.location.h)
+        if self.location.left > 0:
+            self.move(-self.location.w)
+        Sprite.update(self)
+
+
 class Introduction(Animation):
 
     BACKGROUND = (0, 0, 0)
@@ -1192,7 +1342,7 @@ class Introduction(Animation):
         self.play(self.blink)
 
     def respond(self, event):
-        if self.active:
+        if self.active and not self.get_game().editor.active:
             if self.get_game().delegate.compare(event, "action"):
                 self.block_index += 1
                 self.current_panel_index += 1
@@ -1267,7 +1417,7 @@ class Ending(Animation):
         self.register(self.unsuppress_advance)
 
     def respond(self, event):
-        if self.active and not self.suppress_commands and \
+        if self.active and not self.suppress_commands and not self.get_game().editor.active \
            ((self.get_game().is_gamepad_mode() and self.get_game().delegate.compare(event, "advance")) or \
             (not self.get_game().is_gamepad_mode() and self.get_game().delegate.compare(event, "action"))):
             self.deactivate()
diff --git a/config b/config
index 2ffdfc2..e584d0b 100644 (file)
--- a/config
+++ b/config
@@ -28,6 +28,7 @@ single-xy = no
 delay-axis = .15
 vertical-axis = 1
 horizontal-axis = 0
+paint = 1
 
 [mouse]
 visible = no