From d8c65e40b2ec986e803953fe467c3f5af3173071 Mon Sep 17 00:00:00 2001 From: Joe Lee Date: Sun, 09 Aug 2009 22:43:35 +0000 Subject: Moved bulk of animation code to gridwidget. --- diff --git a/gridwidget.py b/gridwidget.py index d6d7eb9..3936840 100644 --- a/gridwidget.py +++ b/gridwidget.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (C) 2007, Joseph C. Lee +# Copyright (C) 2007-2009, Joseph C. Lee # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ import gobject import gtk import math import random +import time import color @@ -99,6 +100,23 @@ ANIMATE_SLIDE = 3 ANIMATE_ZOOM = 4 ANIMATE_WIN = 5 +# A list of the animation stages in order, along with time on-screen (in +# seconds per tick). +_ANIM_TIME_LIST = ( + (ANIMATE_SHRINK, 0.1), + (ANIMATE_FALL, 0.1), + (ANIMATE_SLIDE, 0.1), + (ANIMATE_ZOOM, 0.1), +) +_ANIM_MODES = [x[0] for x in _ANIM_TIME_LIST] +_ANIM_TIMES = dict(_ANIM_TIME_LIST) + +# Win animation time on screen (in seconds per tick). +_WIN_ANIM_TIME = 0.04 + +# Animation timer interval (in msec) +_TIMER_INTERVAL = 20 + #import traceback #def _log_errors(func): # # A function decorator to add error logging to selected functions. @@ -134,6 +152,148 @@ class GridWidget(gtk.DrawingArea): | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.KEY_PRESS_MASK) self.set_flags(gtk.CAN_FOCUS) + + self._drawer = GridDrawer(get_size_func=self._get_size, + invalidate_rect_func=self._invalidate_rect) + + def _get_size(self): + return (self.allocation.width, self.allocation.height) + + def _invalidate_rect(self, rect): + if self.window: + self.window.invalidate_rect(rect, True) + + # NOTE: There is a lot of forwarding to the drawer here, which may benefit + # from better separation of responsibilities. + + def set_board(self, board): + self._drawer.set_board(board) + + def set_removal_block_set(self, value): + self._drawer.set_removal_block_set(value) + + def set_animation_mode(self, value): + self._drawer.set_animation_mode(value) + + def set_animation_mode(self, value): + self._drawer.set_animation_mode(value) + + def set_animation_percent(self, value): + self._drawer.set_animation_percent(value) + + def set_win_draw_flag(self, value): + self._drawer.set_win_draw_flag(value) + + def get_win_draw_flag(self): + return self._drawer.get_win_draw_flag() + + def get_win_color(self): + return self._drawer.get_win_color() + + def set_win_state(self, draw_flag, win_color): + self._drawer.set_win_state(draw_flag, win_color) + + def get_animation_length(self): + return self._drawer.get_animation_length() + + def select_center_cell(self): + self._drawer.select_center_cell() + + @_log_errors + def do_button_press_event(self, event): + # Ignore mouse clicks while animating. + if self._drawer.get_animation_mode() != ANIMATE_NONE: + return + self.grab_focus() + self._drawer.set_mouse_selection(event.x, event.y) + selected_cell = self._drawer.get_selected_cell() + if selected_cell is not None: + self.emit('piece-selected', *selected_cell) + + @_log_errors + def do_key_press_event(self, event): + action = _KEY_MAP.get(event.keyval, None) + if action == 'new': + self.emit('new-key-pressed', 0) + return True + # Ignore key presses while animating. + if self._drawer.get_animation_mode() != ANIMATE_NONE: + return False + if not self._drawer.board_is_valid(): + self._drawer.set_selected_cell(None) + return False + else: + selected_cell = self._drawer.get_selected_cell() + if selected_cell is None: + self._drawer.select_center_cell() + return True + else: + if action == 'select': + self.emit('piece-selected', *selected_cell) + return True + elif action == 'undo': + self.emit('undo-key-pressed', 0) + return True + elif action == 'redo': + self.emit('redo-key-pressed', 0) + return True + else: + offsets = {'up' : ( 0, 1), + 'down' : ( 0, -1), + 'left' : (-1, 0), + 'right' : ( 1, 0)} + if action in offsets: + offset = offsets[action] + return self._drawer.move_selected_cell(*offset) + else: + return False + + @_log_errors + def do_motion_notify_event(self, event): + if event.is_hint: + (x, y, state) = event.window.get_pointer() + else: + x = event.x + y = event.y + state = event.state + self._drawer.set_mouse_selection(x, y) + + @_log_errors + def do_expose_event(self, event): + cr = self.window.cairo_create() + cr.rectangle(event.area.x, + event.area.y, + event.area.width, + event.area.height) + cr.clip() + (width, height) = self.window.get_size() + self._drawer.draw(cr, width, height) + + @_log_errors + def do_size_allocate(self, allocation): + super(GridWidget, self).do_size_allocate(self, allocation) + self._drawer.init_board_layout(allocation.width, allocation.height) + + def start_removal_anim(self, end_anim_func, contiguous): + anim = RemovalAnim(self, end_anim_func, contiguous) + anim.start() + return anim + + def start_win_anim(self, end_anim_func): + anim = WinAnim(self, end_anim_func) + anim.start() + return anim + + +# NOTE: We separate the drawing/interaction code from the GTK widget code so +# that we can reuse the drawing in a widget that draws more on top; apparently +# GTK doesn't like overlapping widgets. + +class GridDrawer(object): + """Object to manage drawing/animation of game board.""" + + def __init__(self, get_size_func, invalidate_rect_func, *args, **kwargs): + super(GridDrawer, self).__init__(*args, **kwargs) self._board = None self._board_width = 0 self._board_height = 0 @@ -161,12 +321,16 @@ class GridWidget(gtk.DrawingArea): # Drawing offset and scale. self._board_transform = None + # Callback functions set by owner. + self._get_size_func = get_size_func + self._invalidate_rect_func = invalidate_rect_func + def set_board(self, value): self._board = value self._recalc_board_dimensions() self._recalc_contiguous_map() - self._init_board_layout(self.allocation.width, - self.allocation.height) + (width, height) = self._get_size_func() + self.init_board_layout(width, height) if self._selected_cell is not None: # If a cell is selected, clamp it to new board boundaries. (x, y) = self._selected_cell @@ -185,6 +349,9 @@ class GridWidget(gtk.DrawingArea): self._recalc_win_animation_frames() self._invalidate_board() + def get_animation_mode(self): + return self._animation_mode + def set_animation_percent(self, value): self._animation_percent = value self._recalc_animation_coords() @@ -224,18 +391,14 @@ class GridWidget(gtk.DrawingArea): for coord in contiguous: self._contiguous_map[coord] = contiguous - @_log_errors - def do_button_press_event(self, event): - # Ignore mouse clicks while animating. - if self._animation_mode != ANIMATE_NONE: - return - self.grab_focus() - self._set_mouse_selection(event.x, event.y) - if self._selected_cell is not None: - self.emit('piece-selected', *self._selected_cell) + def get_selected_cell(self): + return self._selected_cell + + def set_selected_cell(self, value): + self._selected_cell = value def select_center_cell(self): - if not self._board_is_valid(): + if not self.board_is_valid(): return if self._selected_cell is not None: self._invalidate_selection(self._selected_cell) @@ -243,62 +406,24 @@ class GridWidget(gtk.DrawingArea): self._board_height - 1) self._invalidate_selection(self._selected_cell) - @_log_errors - def do_key_press_event(self, event): - action = _KEY_MAP.get(event.keyval, None) - if action == 'new': - self.emit('new-key-pressed', 0) - return True - # Ignore key presses while animating. - if self._animation_mode != ANIMATE_NONE: - return False - if not self._board_is_valid(): - self._selected_cell = None + def move_selected_cell(self, x_offset, y_offset): + # Moves the selected cell in the direction of the given offset, + # returning True if the cell changed after clamping, False otherwise. + (x, y) = self._selected_cell + x = max(0, min(self._board_width - 1, x + x_offset)) + y = max(0, min(self._board_height - 1, y + y_offset)) + if self._selected_cell == (x, y): return False else: - if self._selected_cell is None: - self.select_center_cell() - return True - else: - if action == 'select': - self.emit('piece-selected', *self._selected_cell) - return True - elif action == 'undo': - self.emit('undo-key-pressed', 0) - return True - elif action == 'redo': - self.emit('redo-key-pressed', 0) - return True - else: - (x, y) = self._selected_cell - if action == 'up': - y = min(self._board_height - 1, y + 1) - elif action == 'down': - y = max(0, y - 1) - elif action == 'left': - x = max(0, x - 1) - elif action == 'right': - x = min(self._board_width - 1, x + 1) - if self._selected_cell != (x, y): - self._invalidate_selection(self._selected_cell) - self._selected_cell = (x, y) - self._invalidate_selection(self._selected_cell) - return True - else: - return False - - @_log_errors - def do_motion_notify_event(self, event): - if event.is_hint: - (x, y, state) = event.window.get_pointer() - else: - x = event.x - y = event.y - state = event.state - self._set_mouse_selection(x, y) + self._invalidate_selection(self._selected_cell) + self._selected_cell = (x, y) + self._invalidate_selection(self._selected_cell) + return True - def _set_mouse_selection(self, x, y): - if not self._board_is_valid(): + def set_mouse_selection(self, x, y): + # Sets the mouse selection to the block corresponding to the given x + # and y coordinates. + if not self.board_is_valid(): self._selected_cell = None return old_selection = self._selected_cell @@ -330,18 +455,16 @@ class GridWidget(gtk.DrawingArea): max_x2 = math.ceil( max(pt1[0], pt2[0])) + 1 min_y2 = math.floor(min(pt1[1], pt2[1])) - 1 max_y2 = math.ceil( max(pt1[1], pt2[1])) + 1 - if self.window: - rect = gtk.gdk.Rectangle(int(min_x2), - int(min_y2), - int(max_x2 - min_x2), - int(max_y2 - min_y2)) - self.window.invalidate_rect(rect, True) + rect = gtk.gdk.Rectangle(int(min_x2), + int(min_y2), + int(max_x2 - min_x2), + int(max_y2 - min_y2)) + self._invalidate_rect_func(rect) def _invalidate_board(self): - if self.window: - alloc = self.allocation - rect = gtk.gdk.Rectangle(0, 0, alloc.width, alloc.height) - self.window.invalidate_rect(rect, True) + (width, height) = self._get_size_func() + rect = gtk.gdk.Rectangle(0, 0, width, height) + self._invalidate_rect_func(rect) def _display_to_cell(self, x, y): # Converts from display coordinate to a cell coordinate. @@ -361,8 +484,8 @@ class GridWidget(gtk.DrawingArea): self._win_length = self._get_win_length() self._win_size = (width, height) self._win_color = r.randint(1, 5) - self._recalc_win_transform(self.allocation.width, - self.allocation.height) + (width, height) = self._get_size_func() + self._recalc_win_transform(width, height) def _get_win_tiles(self): # Returns a list of ending tile coordinates making up the smiley face, @@ -446,7 +569,7 @@ class GridWidget(gtk.DrawingArea): return (len(self._win_starts) + 8) def _recalc_game_animation_frames(self): - if not self._board_is_valid(): + if not self.board_is_valid(): self._animation_frames = {} return @@ -520,7 +643,7 @@ class GridWidget(gtk.DrawingArea): zooming_transform = self._board_transform lengths[ANIMATE_ZOOM] = 0.0 else: - (width, height) = self.window.get_size() + (width, height) = self._get_size_func() zooming_transform = _BoardTransform() zooming_transform.setup(width, height, @@ -536,7 +659,7 @@ class GridWidget(gtk.DrawingArea): if self._animation_mode == ANIMATE_WIN: self._recalc_win_animation_coords() self._invalidate_board() # XXX Limit to win animation? - elif self._animation_mode == ANIMATE_NONE or not self._board_is_valid(): + elif self._animation_mode == ANIMATE_NONE or not self.board_is_valid(): self._animation_coords = [] else: self._recalc_game_animation_coords() @@ -590,24 +713,8 @@ class GridWidget(gtk.DrawingArea): else: self._board_transform.tween(start_transform, end_transform, w) - @_log_errors - def do_expose_event(self, event): - cr = self.window.cairo_create() - cr.rectangle(event.area.x, - event.area.y, - event.area.width, - event.area.height) - cr.clip() - (width, height) = self.window.get_size() - self._draw(cr, width, height) - - @_log_errors - def do_size_allocate(self, allocation): - super(GridWidget, self).do_size_allocate(self, allocation) - self._init_board_layout(allocation.width, allocation.height) - - def _init_board_layout(self, width, height): - if not self._board_is_valid(): + def init_board_layout(self, width, height): + if not self.board_is_valid(): self._board_transform = _BoardTransform() else: self._board_transform = _BoardTransform() @@ -626,7 +733,7 @@ class GridWidget(gtk.DrawingArea): self._win_size[0], self._win_size[1]) - def _draw(self, cr, width, height): + def draw(self, cr, width, height): # Draws the widget. cr.set_source_rgb(*_BG_COLOR) @@ -634,26 +741,23 @@ class GridWidget(gtk.DrawingArea): cr.fill() cr.save() - self._board_transform.set_up_cairo(cr) if self._animation_mode == ANIMATE_NONE: - self._draw_board(cr) + if self._win_draw_flag: + self._win_transform.set_up_cairo(cr) + self._draw_win(cr) + else: + self._board_transform.set_up_cairo(cr) + self._draw_board(cr) elif self._animation_mode in (ANIMATE_SHRINK, ANIMATE_FALL, ANIMATE_SLIDE, ANIMATE_ZOOM): + self._board_transform.set_up_cairo(cr) self._animate_board(cr) - cr.restore() - - if self._win_draw_flag: - cr.save() - self._win_transform.set_up_cairo(cr) - self._draw_win(cr) - cr.restore() elif self._animation_mode == ANIMATE_WIN: - cr.save() self._win_transform.set_up_cairo(cr) self._draw_animated_win(cr) - cr.restore() + cr.restore() def _animate_board(self, cr): self._animate_blocks(cr) @@ -682,7 +786,7 @@ class GridWidget(gtk.DrawingArea): self._draw_scaled_block(cr, x, y, value, scale) def _draw_blocks(self, cr): - if not self._board_is_valid(): + if not self.board_is_valid(): return value_map = self._board.get_value_map() @@ -735,19 +839,109 @@ class GridWidget(gtk.DrawingArea): cr.fill() def _recalc_board_dimensions(self): - if self._board_is_valid(): + if self.board_is_valid(): self._board_width = self._board.width self._board_height = self._board.height else: self._board_width = 1 self._board_height = 1 - def _board_is_valid(self): + def board_is_valid(self): # Returns True if the board is set and has valid dimensions (>=1). return (self._board is not None and not self._board.is_empty()) + +class WinAnim(object): + """Manages the animation of a winning smiley.""" + def __init__(self, grid, end_anim_func): + self._grid = grid + self._end_anim_func = end_anim_func + self._start_time = 0 + self._animating = False + + def start(self): + self._start_time = time.time() + self._animating = True + self._grid.set_animation_mode(ANIMATE_WIN) + self._grid.set_animation_percent(0.0) + gobject.timeout_add(_TIMER_INTERVAL, self._timer) + + def stop(self): + if self._animating: + self._end_anim(anim_stopped=True) + + def _timer(self): + if not self._animating: + return False + delta = time.time() - self._start_time + total = _WIN_ANIM_TIME * self._grid.get_animation_length() + if total > 0: + percent = float(delta) / total + if percent < 1.0: + self._grid.set_animation_percent(percent) + return True + self._end_anim(anim_stopped=False) + return False + + def _end_anim(self, anim_stopped): + self._animating = False + self._grid.set_animation_mode(ANIMATE_NONE) + self._end_anim_func(anim_stopped=anim_stopped) + + +class RemovalAnim(object): + """Manages the animation of removing a piece.""" + def __init__(self, grid, end_anim_func, contiguous): + self._grid = grid + self._end_anim_func = end_anim_func + self._start_time = 0 + self._animation_mode = 0 + self._animating = False + self._contiguous = contiguous + + def start(self): + self._start_time = time.time() + self._animation_mode = 0 + self._grid.set_removal_block_set(self._contiguous) + self._grid.set_animation_mode(_ANIM_MODES[0]) + self._grid.set_animation_percent(0.0) + self._animating = True + gobject.timeout_add(_TIMER_INTERVAL, self._timer) + + def stop(self): + if self._animating: + self._end_anim(anim_stopped=True) + + def _timer(self): + if not self._animating: + return False + delta = time.time() - self._start_time + total = (_ANIM_TIMES[_ANIM_MODES[self._animation_mode]] + * self._grid.get_animation_length()) + if total > 0: + percent = float(delta) / total + if percent < 1.0: + self._grid.set_animation_percent(percent) + return True + self._animation_mode += 1 + if self._animation_mode >= len(_ANIM_MODES): + self._end_anim(anim_stopped=False) + return False + else: + self._grid.set_animation_mode(_ANIM_MODES[self._animation_mode]) + self._grid.set_animation_percent(0.0) + self._start_time = time.time() + return True + + def _end_anim(self, anim_stopped): + self._animating = False + self._grid.set_animation_mode(ANIMATE_NONE) + self._end_anim_func(anim_stopped=anim_stopped) + + class _BoardTransform(object): + # Represents a transformation from board space to screen space. def __init__(self): self.scale_x = 1 self.scale_y = 1 @@ -799,6 +993,7 @@ class _BoardTransform(object): y1 = int((float(y) - self.offset_y) / self.scale_y) return (x1, y1) + def _interleave(*args): # From Richard Harris' recipe: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/511480 diff --git a/implodegame.py b/implodegame.py index 17496f0..fe4229b 100644 --- a/implodegame.py +++ b/implodegame.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (C) 2007, Joseph C. Lee +# Copyright (C) 2007-2009, Joseph C. Lee # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,31 +21,13 @@ _logger = logging.getLogger('implode-activity.implodegame') from gettext import gettext as _ -import gobject import gtk import random -import time import board import boardgen import gridwidget -# A list of the animation stages in order, along with time on-screen (in -# seconds per tick). -_ANIM_TIME_LIST = ( - (gridwidget.ANIMATE_SHRINK, 0.1), - (gridwidget.ANIMATE_FALL, 0.1), - (gridwidget.ANIMATE_SLIDE, 0.1), - (gridwidget.ANIMATE_ZOOM, 0.1), -) -_ANIM_MODES = [x[0] for x in _ANIM_TIME_LIST] -_ANIM_TIMES = dict(_ANIM_TIME_LIST) - -# Win animation time on screen (in seconds per tick). -_WIN_ANIM_TIME = 0.04 - -# Animation timer interval (in msec) -_TIMER_INTERVAL = 20 class ImplodeGame(gtk.EventBox): """Gtk widget for playing the implode game.""" @@ -53,12 +35,11 @@ class ImplodeGame(gtk.EventBox): def __init__(self, *args, **kwargs): super(ImplodeGame, self).__init__(*args, **kwargs) self._animate = True - self._animation_mode = gridwidget.ANIMATE_NONE - self._start_time = 0.0 + self._current_anim = None self._board = None - self._undoStack = [] - self._redoStack = [] + self._undo_stack = [] + self._redo_stack = [] self._random = random.Random() #self._random.seed(0) @@ -68,9 +49,6 @@ class ImplodeGame(gtk.EventBox): self._seed = 0 self._fragmentation = 0 - self._animating = False - self._end_anim_func = None - self._grid = gridwidget.GridWidget() self._grid.connect('piece-selected', self._piece_selected_cb) self._grid.connect('undo-key-pressed', self._undo_key_pressed_cb) @@ -86,7 +64,7 @@ class ImplodeGame(gtk.EventBox): def new_game(self): _logger.debug('New game.') - self._finish_animation() + self._stop_animation() self._seed = self._random.randint(0, 99999) size_frag_dict = { 0: (( 8, 6), 0), @@ -97,18 +75,18 @@ class ImplodeGame(gtk.EventBox): self._reset_board() def replay_game(self): - self._finish_animation() + self._stop_animation() _logger.debug('Replay game.') self._reset_board() def undo(self): _logger.debug('Undo.') - self._finish_animation() - if len(self._undoStack) == 0: + self._stop_animation() + if len(self._undo_stack) == 0: return - self._redoStack.append(self._board) - self._board = self._undoStack.pop() + self._redo_stack.append(self._board) + self._board = self._undo_stack.pop() # Force board refresh. self._grid.set_board(self._board) @@ -116,12 +94,12 @@ class ImplodeGame(gtk.EventBox): def redo(self): _logger.debug('Redo.') - self._finish_animation() - if len(self._redoStack) == 0: + self._stop_animation() + if len(self._redo_stack) == 0: return - self._undoStack.append(self._board) - self._board = self._redoStack.pop() + self._undo_stack.append(self._board) + self._board = self._redo_stack.pop() # Force board refresh. self._grid.set_board(self._board) @@ -144,15 +122,15 @@ class ImplodeGame(gtk.EventBox): 'size' : self._size, 'fragmentation' : self._fragmentation, 'board' : encode_board(self._board), - 'undo_stack': [encode_board(b) for b in self._undoStack], - 'redo_stack': [encode_board(b) for b in self._redoStack], + 'undo_stack': [encode_board(b) for b in self._undo_stack], + 'redo_stack': [encode_board(b) for b in self._redo_stack], 'win_draw_flag': self._grid.get_win_draw_flag(), 'win_color': self._grid.get_win_color(), } def set_game_state(self, state): # Sets the game state using a dictionary of atomic subobjects. - self._finish_animation() + self._stop_animation() def decode_board(state): b = board.Board() (w, h) = (state[0], state[1]) @@ -166,8 +144,8 @@ class ImplodeGame(gtk.EventBox): self._size = state['size'] self._fragmentation = state['fragmentation'] self._board = decode_board(state['board']) - self._undoStack = [decode_board(x) for x in state['undo_stack']] - self._redoStack = [decode_board(x) for x in state['redo_stack']] + self._undo_stack = [decode_board(x) for x in state['undo_stack']] + self._redo_stack = [decode_board(x) for x in state['redo_stack']] self._grid.set_board(self._board) self._grid.set_win_state(state['win_draw_flag'], state['win_color']) @@ -178,26 +156,19 @@ class ImplodeGame(gtk.EventBox): max_size=self._size) self._grid.set_board(self._board) self._grid.set_win_draw_flag(False) - self._undoStack = [] - self._redoStack = [] + self._undo_stack = [] + self._redo_stack = [] def _piece_selected_cb(self, widget, x, y): # Handles piece selection. - self._finish_animation() + self._stop_animation() contiguous = self._board.get_contiguous(x, y) if len(contiguous) >= 3: self._contiguous = contiguous - if not self._animate: - self._remove_contiguous() + if self._animate: + self._current_anim = self._grid.start_removal_anim(self._remove_contiguous, contiguous) else: - self._start_time = time.time() - self._animation_mode = 0 - self._grid.set_removal_block_set(contiguous) - self._grid.set_animation_mode(_ANIM_MODES[0]) - self._grid.set_animation_percent(0.0) - self._animating = True - self._end_anim_func = self._end_removal_animation - gobject.timeout_add(_TIMER_INTERVAL, self._removal_timer) + self._remove_contiguous() def _undo_key_pressed_cb(self, widget, dummy): self.undo() @@ -211,16 +182,13 @@ class ImplodeGame(gtk.EventBox): if self._board.is_empty(): self.new_game() - def _finish_animation(self): - if self._end_anim_func: - temp_animate = self._animate - self._animate = False - self._end_anim_func() - self._animate = temp_animate + def _stop_animation(self): + if self._current_anim is not None: + self._current_anim.stop() - def _remove_contiguous(self): - self._redoStack = [] - self._undoStack.append(self._board.clone()) + def _remove_contiguous(self, anim_stopped=False): + self._redo_stack = [] + self._undo_stack.append(self._board.clone()) self._board.clear_pieces(self._contiguous) self._board.drop_pieces() self._board.remove_empty_columns() @@ -229,71 +197,20 @@ class ImplodeGame(gtk.EventBox): self._grid.set_board(self._board) if self._board.is_empty(): - if not self._animate: - self._init_win_state() + if self._animate and not anim_stopped: + self._current_anim = self._grid.start_win_anim(self._init_win) else: - self._start_time = time.time() - self._grid.set_animation_mode(gridwidget.ANIMATE_WIN) - self._grid.set_animation_percent(0.0) - self._animating = True - self._end_anim_func = self._end_win_animation - gobject.timeout_add(_TIMER_INTERVAL, self._win_timer) + self._init_win() else: contiguous = self._board.get_all_contiguous() if len(contiguous) == 0: - self._init_lose_state() + self._init_lose() - def _init_win_state(self): + def _init_win(self, anim_stopped=False): self._grid.set_win_draw_flag(True) # Clear the undo stack so that the undo/redo buttons do nothing after # winning. - self._undoStack = [] + self._undo_stack = [] - def _init_lose_state(self): + def _init_lose(self): pass - - def _win_timer(self): - if not self._animating: - return False - delta = time.time() - self._start_time - total = _WIN_ANIM_TIME * self._grid.get_animation_length() - if total > 0: - percent = float(delta) / total - if percent < 1.0: - self._grid.set_animation_percent(percent) - return True - self._end_win_animation() - return False - - def _end_win_animation(self): - self._animating = False - self._end_anim_func = None - self._grid.set_animation_mode(gridwidget.ANIMATE_NONE) - self._init_win_state() - - def _removal_timer(self): - if not self._animating: - return False - delta = time.time() - self._start_time - total = (_ANIM_TIMES[_ANIM_MODES[self._animation_mode]] - * self._grid.get_animation_length()) - if total > 0: - percent = float(delta) / total - if percent < 1.0: - self._grid.set_animation_percent(percent) - return True - self._animation_mode += 1 - if self._animation_mode >= len(_ANIM_MODES): - self._end_removal_animation() - return False - else: - self._grid.set_animation_mode(_ANIM_MODES[self._animation_mode]) - self._grid.set_animation_percent(0.0) - self._start_time = time.time() - return True - - def _end_removal_animation(self): - self._animating = False - self._end_anim_func = None - self._grid.set_animation_mode(gridwidget.ANIMATE_NONE) - self._remove_contiguous() -- cgit v0.9.1