multitet-1.0+svn6432.orig/0000775000000000000000000000000011712407027014002 5ustar rootrootmultitet-1.0+svn6432.orig/setup.py0000664000000000000000000000057611602443333015522 0ustar rootroot#!/usr/bin/env python # -*- coding: utf-8 -*- from distutils.core import setup setup( name='Multitet', version='1.0', author='Ka-Ping Yee', author_email='', url='http://multitetris.com/', license='GPL3', packages=['multitet'], scripts=['scripts/multitet'], package_data={ 'multitet': ['media/*.png',], } ) multitet-1.0+svn6432.orig/scripts/0000775000000000000000000000000011712407027015471 5ustar rootrootmultitet-1.0+svn6432.orig/scripts/multitet0000775000000000000000000000175011602447443017275 0ustar rootroot#!/usr/bin/env python # Copyright 2009 by Ka-Ping Yee # # multitet is free software: you can redistribute and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation (either version 3, or, at your option, any later version). # # multitet is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. # You should have received a copy of the GNU General Public License along # with multitet. If not, see . import sys try: import multitet except ImportError: sys.path = ['..', '/usr/share/games'] + sys.path try: import multitet except ImportError: sys.stderr.write('ERROR: Cannot find multitet package: reinstall the game.\n') sys.exit(1) if __name__ == '__main__': multitet.Multitet.start() multitet-1.0+svn6432.orig/multitet/0000775000000000000000000000000011712407027015651 5ustar rootrootmultitet-1.0+svn6432.orig/multitet/buttons.py0000664000000000000000000001423411530015400017711 0ustar rootroot#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2009 # Martin Heistermann, # # This library is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this library. If not, see . # # Original author of this file: Thomas Schott, # from libavg import avg g_player = avg.Player.get() class Button(object): def __init__(self, node): self._cursorID = None self._node = node self._node.setEventHandler(avg.CURSORDOWN, avg.MOUSE | avg.TOUCH, self.__onDown) def __onDown(self, event): assert self._cursorID is None self._node.setEventHandler(avg.CURSORDOWN, avg.MOUSE | avg.TOUCH, None) self._cursorID = event.cursorid self._node.setEventCapture(self._cursorID) self._node.setEventHandler(avg.CURSORUP, avg.MOUSE | avg.TOUCH, self.__onUp) self._onDown(event) def __onUp(self, event): if not self._cursorID == event.cursorid: return self._node.setEventHandler(avg.CURSORUP, avg.MOUSE | avg.TOUCH, None) self._node.releaseEventCapture(self._cursorID) self._cursorID = None self._node.setEventHandler(avg.CURSORDOWN, avg.MOUSE | avg.TOUCH, self.__onDown) self._onUp(event) def delete(self): if not self._cursorID is None: self._node.releaseEventCapture(self._cursorID) self._node.setEventHandler(avg.CURSORUP, avg.MOUSE | avg.TOUCH, None) self._node.setEventHandler(avg.CURSORDOWN, avg.MOUSE | avg.TOUCH, None) class LabelButton(Button): def __init__(self, parentNode, pos, text, size, callback): node = g_player.createNode('words', { 'pos':pos, 'fontsize':size, 'text':text}) parentNode.appendChild(node) self.__callback = callback self.__isActive = True self.__isOver = False super(LabelButton, self).__init__(node) def _onDown(self, event): self._node.setEventHandler(avg.CURSOROUT, avg.MOUSE | avg.TOUCH, self.__onOut) if self.__isActive: self._node.color = 'ff6000' # red self.__isOver = True def _onUp(self, event): self._node.setEventHandler(avg.CURSOROUT, avg.MOUSE | avg.TOUCH, None) self._node.setEventHandler(avg.CURSOROVER, avg.MOUSE | avg.TOUCH, None) if self.__isActive: self._node.color = 'ffffff' # white if self.__isOver: self.__callback() self.__isOver = False def __onOut(self, event): if not self._cursorID == event.cursorid: return self._node.setEventHandler(avg.CURSOROUT, avg.MOUSE | avg.TOUCH, None) self._node.setEventHandler(avg.CURSOROVER, avg.MOUSE | avg.TOUCH, self.__onOver) if self.__isActive: self._node.color = 'ffffff' # white self.__isOver = False def __onOver(self, event): if not self._cursorID == event.cursorid: return self._node.setEventHandler(avg.CURSOROVER, avg.MOUSE | avg.TOUCH, None) self._node.setEventHandler(avg.CURSOROUT, avg.MOUSE | avg.TOUCH, self.__onOut) if self.__isActive: self._node.color = 'ff6000' # red self.__isOver = True def setActive(self, active): self.__isActive = active if self.__isActive: if self.__isOver: self._node.color = 'ff6000' # red else: self._node.color = 'ffffff' # white else: self._node.color = '7f7f7f' # gray class MoveButton(Button): def __init__(self, node, onDown, onUp, onMotion): self.__onDownCallback = onDown self.__onUpCallback = onUp self.__onMotionCallback = onMotion self.__slowdownID = None super(MoveButton, self).__init__(node) def delete(self): super(MoveButton, self).delete() self.__stopSlowdown() self._node.setEventHandler(avg.CURSORMOTION, avg.MOUSE | avg.TOUCH, None) def _onDown(self, event): self.__stopSlowdown() self._node.setEventHandler(avg.CURSORMOTION, avg.MOUSE | avg.TOUCH, self.__onMotion) self.__lastPos = event.pos self.__onDownCallback(event) def _onUp(self, event): self._node.setEventHandler(avg.CURSORMOTION, avg.MOUSE | avg.TOUCH, None) self.__onUpCallback(event) if event.speed.x or event.speed.y: self.__startSlowdown(event) def __onMotion(self, event): if not self._cursorID == event.cursorid: return event.motion = event.pos - self.__lastPos if event.motion.x or event.motion.y: # avoid (0,0) motion events when using the mouse self.__lastPos = event.pos self.__onMotionCallback(event) def __onSlowdownMotion(self): self.__speed *= 0.95 if self.__speed.getNorm() < 1: self.__stopSlowdown() else: self.__motionDiff += self.__speed motion = avg.Point2D(round(self.__motionDiff.x), round(self.__motionDiff.y)) if motion.x or motion.y: fakeEvent = avg.Event fakeEvent.motion = motion self.__motionDiff -= motion self.__onMotionCallback(fakeEvent) def __startSlowdown(self, event): self.__speed = event.speed * 10.0 self.__motionDiff = avg.Point2D(0, 0) if self.__slowdownID is None: self.__slowdownID = g_player.setInterval(10, self.__onSlowdownMotion) def __stopSlowdown(self): if not self.__slowdownID is None: g_player.clearInterval(self.__slowdownID) self.__slowdownID = None multitet-1.0+svn6432.orig/multitet/__init__.py0000775000000000000000000000060311621332016017756 0ustar rootrootimport os from libavg.utils import getMediaDir, createImagePreviewNode from multitet import Multitet __all__ = [ 'apps', ] def createPreviewNode(maxSize): filename = os.path.join(getMediaDir(__file__), 'preview.png') return createImagePreviewNode(maxSize, absHref = filename) apps = ( {'class': Multitet, 'createPreviewNode': createPreviewNode}, ) multitet-1.0+svn6432.orig/multitet/multitet.py0000664000000000000000000010612711621332016020073 0ustar rootroot#!/usr/bin/env python # Copyright 2009 by Ka-Ping Yee # # multitet is free software: you can redistribute and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation (either version 3, or, at your option, any later version). # # multitet is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. # You should have received a copy of the GNU General Public License along # with multitet. If not, see . # Naming convention: x and y always refer to coordinates in pixels, not in # grid space. For grids, c refers to a column number, r refers to a row # number, and cr refers to a (column, row) pair. from libavg import avg, Point2D, AVGApp, AVGNode, fadeOut, gameapp from libavg.utils import getMediaDir from buttons import LabelButton from grid import Grid import math, random STARTING_ZONE_NROWS = 1 # size of the starting zone, in rows MANIPULATOR_RADIUS = 140 # radius of the circular manipulator, in pixels TICKS_PER_LEVEL = 40 def make_cells(str): return [[ch != '-' and ch or None for ch in row] for row in str.split()] NORMAL_SHAPES = [ Grid(4, 4, make_cells('---- -FF- -F-- -F--')), # F-shape Grid(4, 4, make_cells('-L-- -L-- -LL- ----')), # L-shape Grid(4, 4, make_cells('--I- --I- --I- --I-')), # I-shape Grid(3, 3, make_cells('-SS SS- ---')), # S-shape Grid(3, 3, make_cells('ZZ- -ZZ ---')), # Z-shape Grid(3, 3, make_cells('--- TTT -T-')), # T-shape Grid(2, 2, make_cells('XX XX')), # X-shape ] BIG_SHAPES = [ Grid(3, 3, make_cells('XXX X-- X--')), Grid(5, 5, make_cells('--I-- --I-- --I-- --I-- --I--')), Grid(3, 3, make_cells('-SS -SS -S-')), Grid(3, 3, make_cells('ZZ- ZZ- -Z-')), Grid(5, 5, make_cells('--L-- --L-- --L-- --LL- -----')), Grid(5, 5, make_cells('----- --FF- --F-- --F-- --F--')), ] TOUGH_SHAPES = [ Grid(3, 3, make_cells('TTT -T- -T-')), Grid(4, 4, make_cells('-S-- -S-- -SS- --S-')), Grid(4, 4, make_cells('--Z- --Z- -ZZ- -Z--')), Grid(4, 4, make_cells('-F-- -FF- -F-- -F--')), Grid(4, 4, make_cells('--L- -LL- --L- --L-')), Grid(3, 3, make_cells('--- X-X XXX')), ] AWFUL_SHAPES = [ Grid(3, 3, make_cells('-X- XXX -X-')), Grid(3, 3, make_cells('LL- -LL -L-')), Grid(3, 3, make_cells('-FF FF- -F-')), Grid(3, 3, make_cells('-SS -S- SS-')), Grid(3, 3, make_cells('ZZ- -Z- -ZZ')), Grid(3, 3, make_cells('II- -II --I')), ] EASY_SHAPES = [ Grid(2, 2, make_cells('XX X-')), Grid(3, 3, make_cells('-I- -I- -I-')), ] TINY_SHAPES = [ Grid(2, 2, make_cells('II --')), Grid(1, 1, make_cells('X')), ] # As levels progress, the scale must not increase (the board cannot shrink)! LEVELS = [ # scale, sections, tick_interval, ticks_per_drop, pieces_per_drop, shapes (40, [4], 1500, 3, 1, NORMAL_SHAPES), # Normal (40, [4], 1200, 2, 1, NORMAL_SHAPES), # Faster (40, [4], 1200, 6, 3, NORMAL_SHAPES), # Crazy round! (40, [3], 1200, 2, 1, NORMAL_SHAPES), # Back to normal... (40, [3], 1000, 6, 3, NORMAL_SHAPES), # Scale up the board! (34, [4], 1200, 2, 1, NORMAL_SHAPES), (34, [4], 1000, 4, 2, NORMAL_SHAPES), (34, [4], 1200, 6, 4, NORMAL_SHAPES), (34, [3], 1200, 4, 2, NORMAL_SHAPES), (34, [3], 1000, 6, 4, NORMAL_SHAPES), # Introduce some new shapes. (34, [4], 1400, 2, 1, NORMAL_SHAPES*3 + BIG_SHAPES*2), (34, [4], 1200, 3, 2, NORMAL_SHAPES*3 + BIG_SHAPES*2), (34, [4], 1000, 6, 3, NORMAL_SHAPES*3 + BIG_SHAPES*2), # Scale up again! Take one level to recover. (30, [4], 1500, 3, 2, NORMAL_SHAPES), (30, [4], 1200, 6, 5, NORMAL_SHAPES), # Crazy again! # A higher proportion of big shapes. (30, [4], 1500, 3, 2, NORMAL_SHAPES*2 + BIG_SHAPES*3), (30, [4], 1200, 3, 2, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES), # More new shapes! Add some easy ones to make up for the hard ones. (30, [4], 1200, 6, 4, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES + EASY_SHAPES*6), (30, [4], 1200, 3, 2, NORMAL_SHAPES*3 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*6), (30, [4], 1200, 6, 4, NORMAL_SHAPES*3 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*12 + TINY_SHAPES*12), # Lightning round! Fast, but easier. (30, [4], 800, 2, 1, NORMAL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*3), (30, [4], 1200, 6, 6, NORMAL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*3), (30, [4], 800, 6, 6, EASY_SHAPES*2 + TINY_SHAPES), # Scale up again. No easy shapes. (26, [4], 1400, 3, 2, NORMAL_SHAPES*3 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES), (26, [4], 1200, 6, 5, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES), # Go nuts with the horrible shapes! (26, [4], 1500, 3, 2, NORMAL_SHAPES*3 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*6), (26, [4], 1000, 3, 2, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*6), (26, [4], 1200, 6, 4, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*6), (26, [4], 1200, 6, 6, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*6), (26, [4], 1000, 6, 6, NORMAL_SHAPES*2 + BIG_SHAPES + TOUGH_SHAPES + AWFUL_SHAPES + EASY_SHAPES*6 + TINY_SHAPES*6), ] def set_handler(node, type, handler): node.setEventHandler(type, avg.TOUCH, handler) def start_capture(node, cursor_id): node.setEventCapture(cursor_id) def end_capture(node, cursor_id): node.releaseEventCapture(cursor_id) def create_node(parent_node, type, **props): node = avg.Player.get().createNode(type, props) parent_node.appendChild(node) return node def raise_node(node): parent_node = node.getParent() parent_node.removeChild(node) parent_node.appendChild(node) def lower_node(node): parent_node = node.getParent() parent_node.removeChild(node) parent_node.insertChild(node, 0) def set_timeout(millis, handler): return avg.Player.get().setTimeout(millis, handler) def set_interval(millis, handler): return avg.Player.get().setInterval(millis, handler) def clear_interval(id): avg.Player.get().clearInterval(id) def get_centroid(points): return sum(points, Point2D(0.0, 0.0))/len(points) def get_heading(origin, point): """Get the direction of the screen vector from 'origin' to 'point' as a value in degrees from 0 to 360, where 0 is north and 90 is east.""" vector = point - origin radians = math.atan2(-vector.y, vector.x) # 0 = east, pi/2 = north degrees = radians*180/math.pi # 0 = east, 90 = north heading = 90 - degrees # 0 = north, 90 = east return (heading + 360) % 360 def get_vector(heading, radius): """Get the vector with the given heading in degrees (where 0 is north and 90 is east) and the given radius.""" degrees = 90 - heading radians = degrees*math.pi/180 return Point2D(radius*math.cos(radians), -radius*math.sin(radians)) def get_length(origin, point): vector = point - origin return (vector.x*vector.x + vector.y*vector.y)**0.5 def add_cr((c1, r1), (c2, r2)): """Add two (column, row) pairs together.""" return (c1 + c2, r1 + r2) def transitive_closure(marked_nodes, edges): """Find the transitive closure of a set of nodes over a graph. 'edges' expresses the graph as a dictionary mapping each node N to the set of nodes that have edges from N. 'marked_nodes' is the initially marked subset.""" closure = set() while marked_nodes: closure = closure.union(marked_nodes) for node in closure: marked_nodes = marked_nodes.union(edges[node]) marked_nodes -= closure return closure def shuffled(items): new_items = items[:] random.shuffle(new_items) return new_items class Piece: """A falling game piece. This class maintains a Grid for the shape of the piece and Nodes that display the piece, and handles player events that manipulate the Piece.""" def __init__(self, parent_node, board, cr, grid): """'parent_node' is the parent AVGNode; 'board' is the game board; 'cr' is the position of the NW corner of this piece on the board; 'grid' is a Grid of strings that refer to block images.""" self.parent_node = parent_node self.board = board self.touches = {} # Current positions of touches on this piece. self.grab_centroid = None # Centroid of initial grab points. self.grab_center = None # Center point of this piece when grabbed. self.grab_angle = None # Heading from grab_centroid to grab_center. self.grab_radius = None # Distance from grab_centroid to grab_center. self.grab_cr = None # Grid position of this piece when grabbed. self.grab_heading = None # Angle of ray between initial grab points. self.grab_grid = None # Initial grid shape when grabbed. # Just create the nodes; self.move_to() will position them. self.block_nodes = [] for block in grid.get_blocks(): block_node = create_node(self.parent_node, 'image') set_handler(block_node, avg.CURSORDOWN, self.handle_down) set_handler(block_node, avg.CURSORMOTION, self.handle_motion) set_handler(block_node, avg.CURSORUP, self.handle_up) self.block_nodes.append(block_node) # Provide a circular manipulator to make the piece easier to grab. # The manipulator is initially behind all other nodes. self.manip_node = create_node(self.parent_node, 'circle', r=MANIPULATOR_RADIUS, color='000000', opacity=0, fillcolor='ffffff', fillopacity=0) set_handler(self.manip_node, avg.CURSORDOWN, self.handle_down) set_handler(self.manip_node, avg.CURSORMOTION, self.handle_motion) set_handler(self.manip_node, avg.CURSORUP, self.handle_up) self.move_to(cr, grid) def destroy(self): """Remove this Piece from the display.""" for node in self.block_nodes: node.unlink() self.manip_node.unlink() def get_blocks(self): """Get the grid positions of the blocks that make up this piece.""" for cr, value in self.grid.get_blocks(): yield add_cr(self.cr, cr), value def get_center_point(self): """Get the center point of this piece, in absolute coordinates.""" nw_cr = self.cr se_cr = add_cr(nw_cr, (self.grid.ncolumns, self.grid.nrows)) board_point = get_centroid(map(self.board.get_nw_point, [nw_cr, se_cr])) return self.board.parent_node.getAbsPos(board_point) def is_grabbed(self): return self.touches def update_blocks(self): """Update the image nodes for this piece's blocks.""" for node, (cr, value) in zip(self.block_nodes, self.get_blocks()): node.href = 'falling-%s.png' % value node.pos = self.board.get_nw_point(cr) node.size = self.board.block_size def update_manip(self): """Update the display of the piece's manipulator.""" center = self.board.get_nw_point( add_cr(self.cr, (self.grid.ncolumns*0.5, self.grid.nrows*0.5))) self.manip_node.pos = center if self.touches: raise_node(self.manip_node) self.manip_node.fillopacity = 0.15 * len(self.touches) else: lower_node(self.manip_node) self.manip_node.fillopacity = 0 def handle_down(self, event): if len(self.touches) == 2: # Ignore third and subsequent touches. return self.touches[event.cursorid] = event.pos start_capture(self.block_nodes[0], event.cursorid) # On the first or second touch, start a translation grab. self.grab_center = self.get_center_point() self.grab_centroid = get_centroid(self.touches.values()) self.grab_angle = get_heading(self.grab_centroid, self.grab_center) self.grab_radius = get_length(self.grab_centroid, self.grab_center) self.grab_cr = self.cr # On the second touch, also start a rotation grab. if len(self.touches) == 2: self.grab_heading = get_heading(*self.touches.values()) self.grab_grid = self.grid self.update_manip() def handle_up(self, event): if event.cursorid in self.touches: del self.touches[event.cursorid] end_capture(self.block_nodes[0], event.cursorid) if len(self.touches) == 1: # Reset any remaining translation grab. self.grab_center = self.get_center_point() self.grab_centroid = get_centroid(self.touches.values()) self.grab_angle = get_heading(self.grab_centroid, self.grab_center) self.grab_radius = get_length(self.grab_centroid, self.grab_center) self.grab_cr = self.cr self.update_manip() def handle_motion(self, event): if event.cursorid not in self.touches: # Ignore extraneous touches. return self.touches[event.cursorid] = event.pos # If there are two touches, update rotation. if len(self.touches) == 2: new_heading = get_heading(*self.touches.values()) delta_heading = new_heading - self.grab_heading cw_quarter_turns = int(round(delta_heading/90)) new_grid = self.grab_grid.get_rotated(cw_quarter_turns) else: delta_heading = 0 new_grid = self.grid # Update translation. The center of the piece is positioned at a # fixed distance from the centroid of the grab points, and at a # fixed angle with respect to the line between the grab points. # This creates the effect of grabbing a rigid disk; if the grab # points stay the same distance apart, the two points on the piece # that were initially grabbed will be anchored to the touch points. new_centroid = get_centroid(self.touches.values()) new_center = new_centroid + get_vector( self.grab_angle + delta_heading, self.grab_radius) delta_center = new_center - self.grab_center delta_c = int(round(delta_center.x/self.board.scale)) delta_r = int(round(delta_center.y/self.board.scale)) new_cr = add_cr(self.grab_cr, (delta_c, delta_r)) if self.board.can_put_piece(self, new_cr, new_grid): self.move_to(new_cr, new_grid) def move_to(self, cr, grid=None): """Move this Piece to the given grid position, optionally with a new grid of blocks, updating the display and the Board. The caller is responsible for avoiding overlaps with existing blocks on the Board.""" self.cr = cr self.grid = grid or self.grid self.update_blocks() self.update_manip() self.board.put_piece(self) class Board: """The game board. This class maintains a Grid for the pieces on the board and a Grid for the frozen blocks on the board.""" def __init__(self, parent_node, ncolumns, nrows, pos, scale): self.parent_node = parent_node self.piece_grid = None # set_size will set these self.frozen_grid = None self.set_size(ncolumns, nrows, pos, scale) def set_size(self, ncolumns, nrows, pos, scale): self.ncolumns = ncolumns self.nrows = nrows self.pos = pos self.scale = float(scale) self.size = Point2D(ncolumns, nrows)*self.scale self.block_size = Point2D(self.scale, self.scale) if self.piece_grid: row_offset = nrows - self.piece_grid.nrows self.piece_grid.grow(ncolumns, nrows) self.frozen_grid.grow(ncolumns, nrows) pieces = set(value for cr, value in self.piece_grid.get_blocks()) for piece in pieces: c, r = piece.cr piece.cr = (c, r + row_offset) piece.update_blocks() piece.update_manip() for cr, value in self.frozen_grid.get_blocks(): value.pos = self.get_nw_point(cr) value.size = self.block_size else: self.piece_grid = Grid(ncolumns, nrows) # contains Piece references self.frozen_grid = Grid(ncolumns, nrows) # contains Node references def destroy(self): for cr, value in self.frozen_grid.get_blocks(): value.unlink() def get_nw_point(self, (c, r)): return self.pos + Point2D(c, r)*self.scale def get_cr(self, point): cr_float = (point - self.pos)/self.scale return (int(cr_float.x), int(cr_float.y)) def can_put_piece(self, piece, cr, grid): """Check whether a piece can be placed on the piece grid.""" return (self.piece_grid.all_in_bounds(grid, cr) and not self.piece_grid.overlaps_any(grid, cr, piece) and not self.frozen_grid.overlaps_any(grid, cr)) def put_piece(self, piece): """Put a piece on the piece grid, removing it from its old location.""" self.piece_grid.remove_value(piece) self.piece_grid.put_all(piece.grid, piece.cr, piece) def freeze_piece(self, piece): """Copy a piece to the frozen grid, and destroy the piece.""" self.piece_grid.remove_value(piece) for cr, value in piece.get_blocks(): self.frozen_grid.put(cr, create_node(self.parent_node, 'image', size=self.block_size, href='frozen-%s.png' % value, pos=self.get_nw_point(cr))) piece.destroy() def delete_frozen(self, grid): """Delete the specified frozen blocks, and let the frozen blocks above them fall down one space, unless they would collide with pieces.""" # This algorithm relies on get_blocks returning blocks in top-to-bottom # order (so deleting one block does not affect future deletions). for (c, r), value in grid.get_blocks(): node = self.frozen_grid.get((c, r)) if node: node.unlink() self.frozen_grid.put((c, r), None) for rr in reversed(range(r)): if self.piece_grid.get((c, rr + 1)): break self.move_frozen_block((c, rr), (c, rr + 1)) def move_frozen_block(self, cr, new_cr): """Move a single frozen block to a new location.""" node = self.frozen_grid.get(cr) if node: self.frozen_grid.put(new_cr, node) self.frozen_grid.put(cr, None) node.pos = self.get_nw_point(new_cr) def highlight_dissolving(self, grid): """Highlight frozen blocks to indicate that they are dissolving.""" for (c, r), value in grid.get_blocks(): node = self.frozen_grid.get((c, r)) if node: node.href = 'dissolving.png' class Game: """The main game controller. This class holds the Board and the list of Pieces, and causes the Pieces to fall with each clock tick. When the fall of a Piece is stopped by blocks below, the Piece "freezes": the blocks of the Piece are copied onto the board and the Piece object is destroyed. Pieces appear in a "starting zone" at the top where they cannot be manipulated; if a piece freezes in the starting zone, the game ends.""" def __init__(self, parent_node, app, scale, section_pattern, tick_interval, ticks_per_drop, pieces_per_drop, shapes): self.width, self.height = parent_node.size.x, parent_node.size.y self.app = app self.node = create_node(parent_node, 'div') self.section_node = create_node(self.node, 'div', sensitive=False) # Create a dummy game board; it will be resized in self.set_scale(). self.board = Board(self.node, 1, 1, Point2D(0, 0), scale) self.board_rect = create_node(self.node, 'rect', color='a0a0a0', strokewidth=4, sensitive=False) self.dissolving = None self.starting_zone = None self.starting_zone_node = create_node(parent_node, 'rect', opacity=0, fillcolor='ff0000', fillopacity=0.2) self.can_add_pieces = True # For debouncing taps on the starting zone. # Normally, the starting zone can be touched to request more pieces; # however, this feature is disabled because of an exhaust hose in the # c-base multitouch table that causes extraneous touches along the top. # set_handler(self.starting_zone_node, avg.CURSORDOWN, self.handle_down) self.pieces = [] self.interval_id = None self.ticks_to_next_drop = 1 self.section_nodes = None self.set_difficulty(scale, section_pattern, tick_interval, ticks_per_drop, pieces_per_drop, shapes) def set_difficulty(self, scale, section_pattern, tick_interval, ticks_per_drop, pieces_per_drop, shapes): self.set_scale(scale) self.set_section_pattern(section_pattern) self.tick_interval = tick_interval self.ticks_per_drop = ticks_per_drop self.pieces_per_drop = pieces_per_drop self.shapes = shapes def set_scale(self, scale): # Resize the game board. ncolumns, nrows = int(self.width/scale), int(self.height/scale) board_width = ncolumns*scale self.board.set_size(ncolumns, nrows, Point2D((self.width - board_width)/2, self.height % scale), scale) self.board_rect.pos = self.board.pos self.board_rect.size = self.board.size if self.dissolving: self.dissolving.grow(self.board.ncolumns, self.board.nrows) else: self.dissolving = Grid(self.board.ncolumns, self.board.nrows) # Set up the starting zone. self.starting_zone = Grid(self.board.ncolumns, STARTING_ZONE_NROWS, ['X'*self.board.ncolumns]*STARTING_ZONE_NROWS) self.starting_zone_node.pos = self.board.pos self.starting_zone_node.size = \ Point2D(self.board.ncolumns, STARTING_ZONE_NROWS)*scale def handle_down(self, event): if self.can_add_pieces: self.starting_zone_node.fillopacity = 0.4 self.add_piece(self.board.get_cr(event.pos)[0]) self.can_add_pieces = False # Ignore extraneous double-taps. set_timeout(300, self.reset_starting_zone) def reset_starting_zone(self): self.starting_zone_node.fillopacity = 0.2 self.can_add_pieces = True def set_section_pattern(self, section_pattern): if self.section_nodes: for node in self.section_nodes: node.unlink() self.section_nodes = [] self.sections = [] s = 0 for r in reversed(range(STARTING_ZONE_NROWS, self.board.nrows)): nsections = section_pattern[s] s = (s + 1) % len(section_pattern) # If the resolution is too low, 4 sections are no fun. if self.width < 1200 and nsections > 3: nsections = 3 section_min = 0 for i in range(nsections): section_max = int(self.board.ncolumns*float(i + 1)/nsections) section_ncolumns = section_max - section_min section = Grid(self.board.ncolumns, self.board.nrows) for j in range(section_min, section_max): section.put((j, r), 'X') self.sections.append(section) self.section_nodes.append(create_node(self.section_node, 'rect', pos=self.board.get_nw_point((section_min, r)), size=Point2D(section_ncolumns, 1)*self.board.scale, opacity=0.2, color='ffffff', sensitive=False)) section_min = section_max def destroy(self): self.pause() for piece in self.pieces: piece.destroy() for node in self.section_nodes: node.unlink() self.board.destroy() self.node.unlink() self.board_rect.unlink() self.section_node.unlink() self.starting_zone_node.unlink() def run(self): self.pause() self.interval_id = set_interval(self.tick_interval, self.tick) def pause(self): if self.interval_id: clear_interval(self.interval_id) self.interval_id = None def tick(self): """Advance the game by one step.""" self.fall_and_freeze() self.delete_dissolving() self.mark_dissolving() if (not list(self.dissolving.get_blocks()) and self.board.frozen_grid.overlaps_any(self.starting_zone, (0, 0))): self.app.end_game() return self.ticks_to_next_drop -= 1 if (self.ticks_to_next_drop <= 0 or not list(self.board.piece_grid.get_blocks())): self.ticks_to_next_drop = self.ticks_per_drop for p in range(self.pieces_per_drop): if not self.add_piece(): print 'end_game: add_piece failed' self.app.end_game() return self.app.tick() def fall_and_freeze(self): """Classify all pieces as suspended (by a player grab), stopped (by frozen blocks), or falling; move falling pieces down by one step and freeze all stopped pieces.""" # 1. Determine what supports each piece. Pieces can be supported by # other pieces, by frozen blocks, or by the bottom edge of the board. supported_pieces = dict((piece, set()) for piece in self.pieces) stopped = set() for piece in self.pieces: for cr, value in piece.get_blocks(): cr_below = add_cr(cr, (0, 1)) if not self.board.piece_grid.in_bounds(cr_below): stopped.add(piece) # at bottom edge else: piece_below = self.board.piece_grid.get(cr_below) frozen_below = self.board.frozen_grid.get(cr_below) if piece_below: # supported by a Piece supported_pieces[piece_below].add(piece) elif frozen_below: # supported by a frozen block stopped.add(piece) # 2. Stop pieces supported by the board, and pieces they support. stopped = transitive_closure(stopped, supported_pieces) # 3. Suspend pieces that are grabbed, and pieces they support. suspended = set(piece for piece in self.pieces if piece.is_grabbed()) suspended = transitive_closure(suspended, supported_pieces) # 4. Freeze all stopped pieces. for piece in stopped: if piece not in suspended: self.board.freeze_piece(piece) self.pieces.remove(piece) # 5. Move down all falling pieces. for piece in self.pieces: if piece not in suspended: piece.move_to(add_cr(piece.cr, (0, 1))) def delete_dissolving(self): """Delete any frozen blocks that are dissolving.""" self.board.delete_frozen(self.dissolving) self.dissolving.clear() def mark_dissolving(self): """Mark dissolving blocks (to be deleted on the next tick).""" for section in self.sections: if self.board.frozen_grid.overlaps_all(section, (0, 0)): self.dissolving.put_all(section, (0, 0)) self.board.highlight_dissolving(self.dissolving) def add_piece(self, c=None): """Place a new Piece with a randomly selected shape at a random rotation and position somewhere along the top of the game board.""" columns = (c is None) and range(self.board.ncolumns) or [c] # Permute the shapes, rotations, and possible positions, so that we # get a random result, but eventually try them all. for shape in shuffled(self.shapes): for rotation in shuffled(range(4)): for c in shuffled(columns): grid = shape.get_rotated(rotation) min_r = min(r for (c, r), value in grid.get_blocks()) cr = (c - grid.ncolumns/2, -min_r) if self.board.can_put_piece(None, cr, grid): self.pieces.append( Piece(self.node, self.board, cr, grid)) return True return False # couldn't find anywhere to put a new piece class Multitet(gameapp.GameApp): def init(self): self._parentNode.mediadir = getMediaDir(__file__) self.game = None self.size = self._parentNode.size self.text_size = 20 self.text_line = Point2D(0, self.text_size) self.margin = Point2D(20, 20) self.background = create_node(self._parentNode, 'rect', size=self.size, fillcolor='000000', fillopacity=1) self.game_node = create_node(self._parentNode, 'div', pos=self.margin + self.text_line/2, size=self.size - self.margin*2 - self.text_line/2) self.create_button( self._parentNode, self.show_about_box, 'About', 1, self.margin - self.text_line/2, 'left') exit_button_pos = avg.Point2D(self._parentNode.size.x/2, self.margin.y-self.text_line.y/2) self.create_button(self._parentNode, self.quit, 'Exit', 1, exit_button_pos, 'center') self.level_button_nodes = [] self.game_over_box = self.create_game_over_box() self.about_box = self.create_about_box() self.start_game() def create_words(self, parent, text, scale=1.0, alignment='left', **props): return create_node(parent, 'words', text=text, fontsize=self.text_size*scale, alignment=alignment, **props) def create_button(self, parent_node, callback, text, scale, pos, alignment): padding = Point2D(self.text_size*0.4, self.text_size*0.2) button = LabelButton( parent_node, pos, text, self.text_size*scale, callback) button._node.alignment = alignment if alignment == 'left': pos = button._node.pos - padding if alignment == 'center': pos = button._node.pos - Point2D(button._node.size.x/2, 0) - padding if alignment == 'right': pos = button._node.pos - Point2D(button._node.size.x, 0) - padding button_box = create_node(parent_node, 'rect', pos=pos, size=button._node.size + padding*2, color='ffffff', fillcolor='808080', fillopacity=0.75) parent_node.removeChild(button_box) parent_node.insertChildBefore(button_box, button._node) return [button._node, button_box] def create_game_over_box(self): box = create_node(self._parentNode, 'div') create_node(box, 'rect', fillcolor='404040', fillopacity=0.75, pos=self.get_pos(0.5, 0.5, -self.text_size*9, -self.text_size*6), size=Point2D(self.text_size*18, self.text_size*12)) self.create_words(box, 'Game over', 3, alignment='center', pos=self.get_pos(0.5, 0.5, 0, -self.text_size*5)) self.create_button(box, self.start_game, 'Play again', 1.6, self.get_pos(0.5, 0.5, -self.text_size*7, self.text_size*3), 'left') self.create_button(box, self.quit, 'Exit', 1.6, self.get_pos(0.5, 0.5, self.text_size*7, self.text_size*3), 'right') return box def create_about_box(self): box = create_node(self._parentNode, 'div') create_node(box, 'rect', fillcolor='404040', fillopacity=0.75, pos=self.get_pos(0.5, 0.5, -self.text_size*15, -self.text_size*10), size=Point2D(self.text_size*30, self.text_size*23)) y = -9*self.text_size for scale, line in [ (1.6, 'Multitet'), (1, 'Ka-Ping Yee, Martin Heistermann, Ulrich von Zadow'), (1, 'http://multitetris.com/'), (1, ''), (1, 'Use one finger to move a piece.'), (1, 'Use a second finger to rotate a piece.'), (1, 'You can hold pieces in the air, or even push them up.'), (1, 'But watch out: new pieces will keep coming!'), (1, ''), (1, 'The rows are divided into sections. When a section of'), (1, 'one row is filled with blocks, those blocks disappear.'), (1, 'Keep them from piling up to the top as long as you can.'), (1, ''), (1, 'Multitet is based on libavg.')]: self.create_words(box, line, scale, pos=self.get_pos(0.5, 0.5, -self.text_size*14, y)) y += self.text_size*scale*1.2 self.create_button(box, self.hide_about_box, 'Continue', 1.6, self.get_pos(0.5, 0.5, 0, self.text_size*9.5), 'center') return box def get_pos(self, fraction_x, fraction_y, offset_x=0, offset_y=0): return Point2D(self.size.x*fraction_x + offset_x, self.size.y*fraction_y + offset_y) def _enter(self): if not self.game: self.start_game() self.game.run() def _leave(self): self.game.pause() def show(self, node): node.unlink() self._parentNode.appendChild(node) def hide(self, node): node.unlink() def show_about_box(self): self.game.pause() self.show(self.about_box) def hide_about_box(self): self.hide(self.about_box) self.game.run() def start_game(self): self.hide(self.about_box) self.hide(self.game_over_box) if self.game: self.game.destroy() self.game = Game(self.game_node, self, *LEVELS[0]) self.ticks_to_next_level = TICKS_PER_LEVEL self.set_level(1) def end_game(self): self.game.pause() self.show(self.game_over_box) def set_level(self, level): self.level = min(level, len(LEVELS)) self.game.set_difficulty(*LEVELS[self.level - 1]) # level is 1-based self.game.run() level_words = self.create_words( self._parentNode, 'Level %d ' % self.level, 3, pos=self.get_pos(0.5, 0.3), alignment='center', sensitive=False) fadeOut(level_words, 2000) for node in self.level_button_nodes: node.unlink() self.level_button_nodes = self.create_button( self._parentNode, self.advance_level, 'Level %d' % self.level, 1, Point2D(self.size.x - self.margin.x, self.margin.y - self.text_size/2), 'right') def advance_level(self): self.set_level(self.level + 1) self.ticks_to_next_level = TICKS_PER_LEVEL def onKeyDown(self, event): if event.keystring == 'l': self.advance_level() if event.keystring == 'q': self.end_game() if event.keystring == ' ': self.show_about_box() def tick(self): self.ticks_to_next_level -= 1 if self.ticks_to_next_level <= 0: self.set_level(self.level + 1) self.ticks_to_next_level = TICKS_PER_LEVEL if __name__ == '__main__': Multitet.start() multitet-1.0+svn6432.orig/multitet/media/0000775000000000000000000000000011712407026016727 5ustar rootrootmultitet-1.0+svn6432.orig/multitet/media/frozen-I.png0000664000000000000000000001120011320157567021127 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  GIDATx`a]_`Om.B@@@@x}{֠ Ps.@s K#m៏^>w/?>l g %9$@C4.!@3|ѸpgOaGGS;Pr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .Ghy;Z^?Nr(A .G|X#tMnu"j@V'"Эa tMnuϞž[u؞;Pt*@Sse 3J$}YL%"    O/3k tIENDB`multitet-1.0+svn6432.orig/multitet/media/frozen-L.png0000664000000000000000000001121211320157567021135 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  QIDATxAn@H'yy* $ Z{,tX,K?@"D @"D @V=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hH @ #ȼ>=2tO 9"hxR=$oK(.5|M6ԉRswoKhWq'gJL |♪H57_6x%F @"D @"D  ]IENDB`multitet-1.0+svn6432.orig/multitet/media/frozen-Z.png0000664000000000000000000001120111320157567021151 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  HIDATxANPpnk- ]!&K͛4v/@"D @"D Ow?7"z׏mUhi_mlХ$7vؠKٓ^GryNBp<|@ @!|]@9$->_>F5⻨H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! H=?/@:! #_eNH'ЄH &78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  KIDATxAn@Hߐ$'ze$k`XPY~"D @"D @"|!r|="z-_WM &~%=NMfK zt'yqFo𨽁h@=C 4DӅ=hH!.!F Տ2='0㫹7E@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P@y O P~]?3b78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATxjAE00pp%8n.z < ǭ9$%3JY Y,@ȓY]%j-h/DBشl+Q灐G t4!hvC@wm|v$!0 3 ozuf^IENDB`multitet-1.0+svn6432.orig/multitet/media/frozen-T.png0000664000000000000000000001120211320157567021144 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IIDATxAN@o}x@RTyl+DD @"D @"D ;֝},ˮ_j\ $zؠgI?y AϮ"}~W58CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'fی2/H7GӑNk] tQtZ78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATxMNQc]:r"240Augz50 s[Kt)Ά//_X.rτ@szqHԩT{(@ha78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  AIDATxAj1IJ%6K0ؕrKD @"D @"D`Bz^YG`D Ft:H)HΦhF":߀ '_!N^o'#|:y @pWاOXNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNxh]׊27$hQNRֆwM G9J tZ56+O] )2Lk/@ "ȴ j@"D @"D @"/ 8HIENDB`multitet-1.0+svn6432.orig/multitet/media/preview.png0000664000000000000000000005256611320164322021126 0ustar rootrootPNG  IHDR@JsBITO IDATx^m]y/}3^I`{뫸J| -RV\n|jڔ\5)H6ʗDF" ))IBjb0 133>wc|gs^묳~EZYlfg}"@ @+pW;w: @xM @ @99t  @ @] @ E @` @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @] @ E @('u @4.0jK:误lGY\ @ P lhJoLݽ{wᄏk޽էI @* 4gff*4>6^'@ @芵Oof>p/ro ?-_:Z+0 @A PkKK.*=ܳq~/~СK^[o`/ @ @ 轿+g>jyy?o֧>#l^w(ʿbW[&rfmqĉ IQWO @ @ ?g)ڙz&NWrԛο9y>>7w;y, @ XL{y?^ZZ/7~Ow EϿ ͧr} @&U~jmnyWw"@ @g۟嗯? /{E @ И@|%|_-7~G?9V>W  @ @@Epc{:+oӿ;F'WWuaD @h,bi̿~u2~=/D_qt^_صRt:v?_+_9sW @ @s=̿7}(ǟ/|g7]T>_N8gÆ?Ϋ @ @;w^{g3{_Z4p/ @ @V _ܺᷱ;ukO @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @& GW @B !"@ @hp4z  @ @ R[- @&!Ze  @(,] n:thϟ,n*\l @0/^~%r0+./O~pMU H[3j @d.phy?,.Νg;Jp @%p2p~4c) w&@W̢=糤Z6{R%-3I  @ #P'y(t|Ɯn{׏|}^UVe @ {'h>ѣ9cխC#'X['F @`W @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @] @ E @` @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @] @ E @` @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @؀ @ N;V<Ī[}FN˲eKq%@Z*3 / Uw~;yݩ  @r(߁ߵetΔU̝x%s8  @r<7>^yemXu>SS= @-= 6uKLO\K @ TF @' O\K @ p? @8.`] pMm| PW@+f< @$^KnʙVw]^ܪŇa#@ vſ޹L`DxD@  @$p2p~)Yk~p~=wb @ oiYoVkCgP& @( Rw:7E* kq @c*dM>ztLi[NhW @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @] @ E @` @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @] @ E @` @d! gf$@ @5@ @YY!  @ @@v  @ @@pmvH @] @ ,N @.pرfTfֱ -O @`Vw~{bӦ&P-I @`7774*3rZ @x ~W3 /.wws @ ]WfN'O'MN@Ne6L @`ssoWZgΣWL0@88J @" l~k"W 4 @ @}}c @ @` 1h- @ @@>ܾ  @@ .Xx׻ldӡC>@!%O @ I/[nrfw'?(lazX9 @&phy?v}dqwlo}+Q@t @) <~ \o?@J_ϝ @ o2[Zj֠|,6TI @`BN#vwǍ,e Z @ <M @h_`;_GS$p4d8e au Lh1Bu8X"@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @pL}  @ @ Z! @) W @ "@ @bW @Po9헿5@xNU7IHN@Ne6L @`H;ϖ~WŪQ={'@ 0@Jq~ì?}[U |x{ @ <鳥XulKy y  @xp3 LVx촗c=so^!  @d!˧PU7IIgSꖽ @ @t& @ @@JpJݲW @Zg3 @r9/FO8X`I @.nhqVw]߻@5,@C' @ 0GvţoVxbZ  @X[eϏmm1j @d2?H,,6K:٫ d @@^JqiOV#KY$8F;& @x?id+ ,c:f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rؐ᝝ @K+spuO @@dP\R.-`#@ 0&G_]7B= IDATmv3*>Vdxl @DLvmlf2p@Of_ @|n)S9<3 @ bg~,xؙ/{i  @{Y0mҵ| @ j @j  &@ @T|87 @$r9/k   @$ nhqV]߻@5,@C' @ 0GvţoVxbZ  @X[eϏmm1j @d2?H,,6K:٫ d @@^JqiOV#KY$8F;& @x?id+ ,c:f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rs @ @ #8f;* @rؐ᝝ @K+spuO @@dP\R.-`#@ 0&G_]7mv3*>Vdxl @DLvmlf2p@Of_ @|n)S9<3 @ bg~,xؙ/{i  @{Y0mҵ| @ j @j  &@ @TT;g @ PK@e0 @* 9&@ @Zp-.  @ @ U87 @kqL @ vξ  @ @\` @HU@NsM @Z\ @ @@po @% 2 @RS} @ @@- @ j @j  &@ @TT;g @ PK@e0 @* 9&@ @Zp-.  @ @ U87 @kqL @ vξ  @ @\` @HU@NsM @Z\ @ @@po @% 2 @RS} @ @@- @ j @j  &@ @TT;g @ PK@e0 @* 9&@ @Zp-.  @ @ U87 @kqL @ vξ  @ @\` @HU`Co @IظNsg}dXuT  }YH N`֙޴Ւˋ\sӳϞ*nǴ8g @@ ,-n/3׏U̝xpd% @vn){Ī;h?^'@==[+ @(#7|YXuۻ а7 j9 @Q$Js(|RVٛ14% 7%i @f=v'ҩG>^ -C @- w @  @<; @hH@n2 @ 0x @ @!!H @ @x #@ @ -C @- w @  @<; @hH@n2 @ 0x @ @!!H @ @x #@ @ -C @- w @  @<; @hH@n2 @ 0x @ @!!H @ @x l @@v3^\N5h( M Ш+_C .yUE^,D 0_彣ُ?^ͽ7Xuٖ/ дܴ @(SxPSיo2plڗ hA[[@$ @@M-Ս~~bխc88Z @`h*>|f|8r䴗c=so^!@ Y @/plBmMA6 @ @@pmi @+ 3 @$f @nn7c  )175N8/,n*AX c!@U{|#nvv<`ZSnM[5쳧Ī1-N c["@zC 5ͤ?Lΰ}g.; ,3\G#@ #>{~WŪޮ}pxrz$ @i۾o8yE{f ]ߵ"$@`dxdB  @&Q|OrQt:6r[UUތ!@ u8? @-GI#t;3|8> @T9E @ 7  @ @\( @H\@NO @jNF @ @@p } @& Ws2 @o @ @@5Q @ xm @ ՜"@ @h @ PM@d @$. '@'@ @jp5' @ @ q8> @T9E @ 7  @ @\( @H\@NO @jNF @ @@p } @&0 @xՕ9bubխEC HS@NovMhY/}\uUqE;3{ @c@Y/~f|ĉb˖kLOP3 Є@)tu_ss 'ĵԁ @ +N':lȍ]2LH @`|酝 @MO_7;[c°CBN5/=*>{GV$VݶOj}M@$0=K4DeP ^8VctsCcmV"@ ~o@IcvI @j5 %@ @tt{g @ PCgk`JGnn7~ʇ`7PH  жܶ  @@+eg~O_|ִmpV @ z-~pV'@>[1 @%p,yu  @@smܧmrs(pE( @ @ m8= @T+BF @i i  @ @\0 @H[@NvO @P @ @@p{ @( W2 @ @ @@E"a @ v @*  #@ @g @ PQ@e @- ?'@ @pE( @ @ m8= @T+BF @i i  @ @\0 @H[@NvO @P @ @@p{ @( W2 @ @ @@E"a @ o @@)a @px]" @ @`.:+{v''@5` @xlz{)  @V4/0=K4mՊ 0>\P^Ql='Ū[f 0.ݙeMs gݫ{+FU~dXuT $-Й:?D^(FU~Ui5Kݯ2~Pܰ8)Xuo EL_Zݔ֎tGRL̟G)Í)Wy @~߼5D^(WYx~]6jӾbխkGގn+7nP, 3A7V;7Y ~ݼ0ƪJ}8N' @$H|ӎh{r.s +}ڶ-C.*xc\ @ AiG9z{r0s  @D#7@y @# qV @" < @8B @ P @aU!@ @p(O @a0Ϊ @ @@d8r'@ @0pgU @ @  @ @@8* @ Y@  @ @ Y @, Gn @ F@  @D#7@y @# qV @" < @8B @ P @aU!@ @p(O @a0Ϊ @ @@d8r'@ @0pgU @ @ /}o[Ī尊 Ј@oǎFֱ::Zr-Dz ! @4 rhbmf SkWt5V8Ulzn*kwbjO?,vR7n"07MY(@-uWZ[%V'UI ,=7D^( U7f j1ii#OUw[hKKO<|6wM=V0S V#Xu/%@ @!!L#@ @e @ 0׆Z{Z SnAl,0||q9t}^(\>BvgtPwnK8hD35;u~P>= ! a~\Q{Z 9gIDAT峦>m+V7q(Ax0H,R{OMϚ^27+wr~zƪJ @2ykP>kzӶb児uGo?Lv0S V3 i  @$ '$[$@ @ @ @ #yO'(OT,ukFjr(SqWfVBwjsN7pOeiE΍Z0EP>ɓ;:oL::+xckשa);#nǪ;A[!jPQ˚)u{wCϚ^2~OUL來|ڧm/ZS Ϛ^Xu{;Bw>_Oi+Pf,2~߉!nr~;FW8 w͸XumeiϞ2VO4 {]~?j]Κkw~j}[~=mA1V{XڋOi X\Xߟcխ3uŌ'@ @$>>(=r7VAiG9o{rQvށiGAn(8w}ڑRԭr7VX O;sni۾p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ S'"@ @>p/ @ @ l#vrXumE9oQ[E/un +Vݹ(?(-ĹTKF(C"B`Uw8h, @HL@NaK @ ùE @ `jb{CHnUc=+VXUw+gmҧgxVKo|V`u.:/V=[9XU7s~orU7ڟby2!F!XKp[o] ƵO*| 7pSb}Q1ii@,XuG@?~mŪ}_]ŃNZ>k[#?m|犿7ڧmŪ[>,SZٵO*C8?WN=լ|9x!mO}Uc}icՍX̬n/!>m+dA.W=){;vT?#mzCV}ʠCn,b͎nkz{,cmrs3k~\u]5)*{cՍў{u;[~oc]W~UX?"Ս:un{~~cխ3uŌ'@ @$>ғ*j}QVorscxv*mBUӎRʽiG)[T<>(uU7ֿGӎ\ԭr7VXαh?FFQ\(*9e}QWns\H @HX@NyN @VF @ @@pͳu @. W2 @n @ @@u @ pl @ խ$@ @g @ P]@ne$ @$, '<['@ @pu+#  @ @ a8: @T[I @  7  @ @\H @HX@NyN @VF @ @@pͳu @. W2 @n @ @@u @ pl @ խ$@ @g @ P]@ne$ @$,!Ὧzoǎ(ٶUXQˢŹՍua<\*ycՍu=7ᇷEoc]W~F^+XS7~}/Xu\H @HX ;]ɪn,(e[t\ƺ%N-q޷(U7w8oz(U7s*(kE39uꆹ¿߉9Vݺ;w^{u;?>QgyϨTɁe٭[Lu޽kV8|Ώ[bV=R/_\k8Slfڞ狣K|' Z=f /-p~=S78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATx;NAEK8A;cz&d$!d)!'ΐϳ4"A3W}os*OwWw$4, _tsVPǟ׍nvV_:RMcnmaums| 7^@.Ġ(Z*OYxi>D(Z+OL[x5D|@E k<1`b$V yb"FH-xRĴ͐(Z^%I "QJS(6EhAke#Q͑(Zwe  Q0ʖ'J(Ah!OLe%F7O%c5 5ggk(w@e(^n_g|[_XFph48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm E48ئ@ [tHm<4m_YsWPMW+%:g?!d? '<7p|cֻt' ]}+H*򴀜}KDi!OO(E ֭Iyj <=~DB& 8DEB0zDB (pn < @z 78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  DIDATxANP6xU?ގU6[H~ tA@[?`n]Lnx}\]; Iz $P  uq"@_g2NH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gNH |qR'gN8h}\FĖ9!@#%О֚ 4({:Ri Ϛ@#龧u@"ȴ 4)2Lk@D @"D @"D | 5M*IENDB`multitet-1.0+svn6432.orig/multitet/media/frozen-F.png0000664000000000000000000001116711320157567021140 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  >IDATxAn@B ꥗(UE vIv<*[@"D @"D @X|wZ~|]qm&@Gb]'&~%=Νp_WryNB`ٶOct]C H (BG@|uB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8CuB3T'$z~^>8Cu}D}]GĖ9!@r#%Й֚ 4(g:Ri Ϛ@r#eԵ hTSd5@5E&i]h"#D @"D @"Do̗ ]`-IENDB`multitet-1.0+svn6432.orig/multitet/media/falling-I.png0000664000000000000000000001176211320157567021255 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATxKNAE+#@` r M $+(!v^mNM0#yRX4@4@4@4@4@4@4@4@4@4p/G~|.EGyfF VծkoK(e|ćy7YAݽǹ6lonz_zybaH-r 'J.PD¬yqśH-b'/PDт"O >M5Uz@ E V+C8G %O 'PDтʔ'(vDhAreC "Q j(vFhAbՒ''PDIWMyb H-dՖ'/PDBB2F-)c7h`XOj'Pd/eKN6jmdgWHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G@ gzHsr' 8G8k~_<0w+)?}Hۏ8M^l :|OſBlB! nז@GԔ@ӓljI.4bQ5$Jy= <'a䑙<#I,& <'`My&x}DBIH4J I"'X 78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATx;NAE#F,29,/L Yp X g<[CfSU}os*LWWwN&% hӊO;MgD }ۻ\6ڲgFZ_Gydz*ƪ1'6,?=]_9Bo&l,PD:F@E \mZ /!Q0Eh@"E X]; /#Q(@hs'-PDт!OLE8K6@qE +<1iV@$4WnybšH-hĄE(ZX #Q0*)OLVTaViyb%H-]5䉉},!4qg,iy/ّQ5"Yz|ȗmݏ|X忰"9T|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !ȗDr|I$G !jF?9K5ow4@?$:)mn(? 75pvۯQ\ )n5$**d\|Ci 78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATx횽jQaMS%/+FP %B$")!_`!sξo}H)qhhhhhhhhhh`I7\ˍ[9rψᢕ&.ίM{76;7ӽ4g}ٞkn_y'x{>#n {>klbeuj}SybNH-t'Z,PHDт*Ol=XDт)!Ol\DDт)%Ol[LDт))OlZTDт)-OlY\Dт֩!OlXEDтƩ%OlWMDjOMyb (ZXͩ-OlU]x E mO ybX=$:7],1 8rv>ϳ`L֥fE_wV0nIO615@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1*)R1ʄ@F""LdK1ewxũEHk@?L؉wx;n.hz%Ty~NΏB:r@ȓ=%&Uymso- uyj`ӚU!iBܓ%ГǛt+w(l"4@4@4@4@4@4@4@4@VIENDB`multitet-1.0+svn6432.orig/multitet/media/falling-F.png0000664000000000000000000001201011320157567021235 0ustar rootrootPNG  IHDRFiCCPICC ProfilexXgTS֞$@]Pz{oҥ%BHS"" (Ub邊HDQw}[d3ٳϜ}&xH@L1rtrB40NO meeU k@b_ML@@NmE^؎2`'hev6_gS60e4xX )~X&, B $1"J)Ky.%=ڻ} @)#?oEȾl=+W/[YiQ7_%ܲX?Z^ j @L(1lKNko  0* &#s[i<;HK`$֥ e2VIWPCUXutȺ ۛMϚ5OXXIXZ7wvvbqsr)w=utrĘcxq?}j|S$AhP>78 1&JHcۡ)a^jQGc96s366<'~&։s'OI8(-<"C,gfoVcٶg$l ̍?琷'o r+/]/b*... ,WVR~~JUJUf55Ϯe\rӷlnSkWY7p7޽ݍo=jmjz,&)9˷[{q哀Ozj{{??GW#׷]|B""% vKE&&[+T0QUGc{,bאT6q%e1xeioTBRJm=eb{.p ˂kבR8wwMO>el$[|ڀA x`y0RF$WW""FVGEՏf>+;W`v|'J}Nʜ )є/ӝ3$33۲NeKg<әs=W2w\m^y W.6^),ŭ% e48A=[`  r(nMMK}ZZwtLv^y׵'اJO7{z>WC=/ ~{a:;c4a57o',LL_^9Qnp~bו(G*3Ԛh):k¬;̸w~)("^-1!SxOtm:+Y ^Jv{qTT-3>(uHTMcYs@v^YT_@phY38ŤMmam{.YFT''gI.ݮGn_<z&{b[X75Oo?H@I``*,CXC$[2'y047>;|$"/)jgц+NJc ccs A|sBq'$MMZNn:u*.?u*2=8coZfKX37sHg]έ;|RlTAAE-YұK*i+U֨^wōnnSZY{_xOaNMM[Z;uzTu;q|_X=6jԁkO_,58Vz-Dis 5_%`\;Qr gpZ0)3v 0.DI;s$rp>%N]x y_ Ɗ_F؞q2_d̕{s,7V?P}pFCJ3@J{Vw~q>3asEuk;õ{9\Nt#Lpc}&b;61T,l4";,،x|'fNMNMK0:×KuۅYKʹ*讬VV_kQv+αzM͇[}c:3rg<=,yX?y0d?  ~6zuwΧ 勴_\J.#VؾAc_iFku M<@Mp~0q rp>0Tn B ^F|D U,d r%B^PqQ9QP}6iCInZAp>: zG &f\f~2VVW5"vqhΝ;ܹXvb5SK3_-), j$f(*=Ң2dppgg*V+=W^'zN_3F5ŴHOtnۙ<47:`}ٖ.c^[Gܪ<=1؛c܉'DC&HWBIkQQ1b7;g&$I^KKʊˎʉ%y\+)|u)FQʫU659o,zr.޽A>7Ϸuvuo>wzusaWGFؾ8{ѩ٫u>,|/}5&ÕH)U՗k.k&-joD!3] d[[?۽6g%05;LI<8o,g` 1+kV;!sboOߘypAy1el^@)#Wp!zB>3k[ ]@x-"̍x(HIpxP% 8(%w'oߏ=x5(փA#}f֠m{=lXldž<|h,  2>I@P EADJ "~>d!mNH78Je9!%}wZ<%HD pHYs  IDATx=NAD+ LN<r'HDEB3؟ Hv{f{ zOlJ,X?rq񶻱=jղ֖=\<ۜDJ&9?9I\5lՆa#n vv5a@qeuj<1h'$-t(^BhaZ<@/E$(^Fh{ '&$PDт*OL=X8UB@qE <1m10$WIybҢŁH-hĔC(ZZ5 #QjU(GhaUS@qE TK(ZhZe}!q_{y@1]>=ecӀ&ºEbSDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@N" HDK0+ Bq@NtzM+?])mh&ϣtux11NJ[[onis # # multitet is free software: you can redistribute and/or modify it under the # terms of the GNU General Public License as published by the Free Software # Foundation (either version 3, or, at your option, any later version). # # multitet is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. # You should have received a copy of the GNU General Public License along # with multitet. If not, see . class Grid: """A fixed-size rectangular array of cells, where a false value represents the absence of a block and anything else represents a block. Clients of this class may use characters or object references to represent blocks.""" def __init__(self, ncolumns, nrows, cells=None): self.ncolumns = ncolumns self.nrows = nrows if cells is None: self.clear() else: self.cells = [list(row) for row in cells] assert len(self.cells) == self.nrows for row in self.cells: assert len(row) == self.ncolumns def dump(self, name): for row in self.cells: line = name + ': ' for cell in row: if isinstance(cell, Piece): ch = 'P' elif isinstance(cell, AVGNode): ch = 'N' elif cell is None: ch = '-' else: ch = str(cell)[0] line += ch print line def clear(self): self.cells = [[None]*self.ncolumns for r in range(self.nrows)] def grow(self, ncolumns, nrows): """Expand to a larger size, anchoring to the bottom-left corner.""" for row in self.cells: row.extend([None]*(ncolumns - self.ncolumns)) self.cells[:0] = [[None]*ncolumns for r in range(nrows - self.nrows)] self.ncolumns = ncolumns self.nrows = nrows def get(self, (c, r)): """Get the value of the cell in column c of row r.""" return self.cells[r][c] def in_bounds(self, (c, r)): """Return True if the given position is in bounds for this Grid.""" return 0 <= c < self.ncolumns and 0 <= r < self.nrows def put(self, (c, r), value): """Get the value of the cell in column c of row r.""" self.cells[r][c] = value def put_all(self, grid, (c, r), override_value=None): """Write the blocks in the given Grid into this Grid, placing the NW corner of the given Grid at (c, r) in this Grid. If 'override_value' is specified, write it instead of the values in the given Grid.""" for (cc, rr), value in grid.get_blocks(): self.cells[r + rr][c + cc] = override_value or value def remove_value(self, value): """Remove all blocks with the given value from this Grid.""" for r in range(self.nrows): for c in range(self.ncolumns): if self.cells[r][c] == value: self.cells[r][c] = None def overlaps_any(self, grid, (c, r), ignore_value=None): """Return True if this Grid overlaps any blocks in the given Grid, when placed with its NW corner at (c, r) in this Grid. If 'ignore_value' is specified, blocks in this Grid with this value are ignored.""" for (cc, rr), value in grid.get_blocks(): value = self.cells[r + rr][c + cc] if value and value != ignore_value: return True def overlaps_all(self, grid, (c, r)): """Return True if this Grid overlaps all blocks in the given Grid, when placed with its NW corner at (c, r) in this Grid.""" for (cc, rr), value in grid.get_blocks(): if not self.cells[r + rr][c + cc]: return False return True def all_in_bounds(self, grid, (c, r)): """Return True if all blocks of the given Grid are in bounds when the NW corner of the given Grid is placed at (c, r) in this Grid.""" for (cc, rr), value in grid.get_blocks(): if not self.in_bounds((c + cc, r + rr)): return False return True def get_blocks(self): """Generate (cr, value) pairs for all the blocks in this grid.""" for r in range(self.nrows): for c in range(self.ncolumns): if self.cells[r][c] is not None: yield (c, r), self.cells[r][c] def get_rotated(self, cw_quarter_turns): """Create a new Grid object that contains a copy of this Grid, rotated clockwise by the given number of quarter turns.""" turns = cw_quarter_turns % 4 if turns == 0: return Grid(self.ncolumns, self.nrows, self.cells) if turns == 2: rotated_cells = [reversed(row) for row in reversed(self.cells)] return Grid(self.ncolumns, self.nrows, rotated_cells) if turns == 1: rotated_cells = [ [self.cells[r][c] for r in reversed(range(self.nrows))] for c in range(self.ncolumns)] return Grid(self.nrows, self.ncolumns, rotated_cells) if turns == 3: rotated_cells = [ [self.cells[r][c] for r in range(self.nrows)] for c in reversed(range(self.ncolumns))] return Grid(self.nrows, self.ncolumns, rotated_cells)