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
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)
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 = []
def get_default_level(self):
level = Level(self)
- level.load_default()
+ level.load()
return level
def get_default_tile_path(self):
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"):
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):
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:
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):
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)
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 = []
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):
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()
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
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
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 \
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()
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)
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
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()