def __timer_fade_out_layer(key, data): """ This function is invoked by a timer to perform an step of a fade out animation over a layer. - key: Timer's key. - data: Timer's data. """ layer = key[0] # Calculate the decrement based on the elapsed time now = pygame.time.get_ticks() if data[1] == None: elapsed_time = min(0, now - data[0]) else: elapsed_time = now - data[1] data[1] = now decrement = 255 * (float(elapsed_time) / data[2]) # Apply the decrement alpha = layer.get_alpha() alpha -= decrement if alpha >= 0: layer.set_alpha(alpha) else: layer.set_alpha(0) stage = data[4] stage.stop_timer(key) callback = data[3] if callback != None: callback(layer)
def cancel_wait(stage, key): """ Cancel a wait to avoid invoke the wait's function. - stage: Stage. - key: Key returned by the wait operation that must be canceled. """ stage.stop_timer(key)
def __timer_fade_in_item(key, data): """ This function is invoked by a timer to perform an step of a fade in animation over an item. - key: Timer's key. - data: Timer's data. """ item = key[0] # Calculate the increment based on the elapsed time now = pygame.time.get_ticks() if data[1] == None: elapsed_time = min(0, now - data[0]) else: elapsed_time = now - data[1] data[1] = now to_alpha = data[3] increment = to_alpha * (float(elapsed_time) / data[2]) # Apply the increment alpha = item.get_alpha() alpha += increment if alpha < to_alpha: item.set_alpha(alpha) else: item.set_alpha(to_alpha) stage = data[5] stage.stop_timer(key) callback = data[4] if callback != None: callback(item)
def start_resize(item_image, image, from_size, to_size, duration, origin_type = 1, callback = None): """ Start a resize animation on the specified ItemImage. - item: ItemImage to resize. - image: Image that is resized an assigned to the ItemImage. - from_size: Initial size specified as (width, height). - to_size: Final size specified as (width, height). - origin_type: Origin. Possible values: 0 - Left X, Left Y. 1 - Center X, Center Y. - callback: Function that is invoked with parameter item when the animation is completed. None there is no callback function. """ stage = __get_item_stage(item_image) # If a move timer is already started for the item, stop it key = (item_image, "resize") milliseconds = 20 stage.stop_timer(key) # Set the initial size resized_image = assets.Image(pygame.transform.smoothscale(image.surface, (int(from_size[0]), int(from_size[1])))) item_image.set_image(resized_image) if origin_type == 1: orgin_pos = (item_image.get_left() + item_image.get_width() / 2, item_image.get_top() + item_image.get_height() / 2) else: orgin_pos = None # Start the timer data = [None, image, duration, origin_type, orgin_pos, from_size, to_size, callback, stage] stage.start_timer(key, milliseconds, __timer_resize, data, True, True)
def stop_resize(item_image): """ Stops a resize animation started over the specified item. - item_image: Item. """ stage = __get_item_stage(item_image) stage.stop_timer((item_image, "resize"))
def stop_move(item): """ Stops a move animation started over the specified item. - item_image: Item. """ stage = __get_item_stage(item) stage.stop_timer((item, "move"))
def __timer_image_sequence(key, data): """ This function is invoked by a timer to perform an step of a image sequence animation. - key: Timer's key. - data: Timer's data. """ item = key[0] images = data[1] # Set the new image in the item if len(images) > 0: item.set_image(images[data[0]]) # Update the index data[0] += 1 if data[0] >= len(images): data[0] = 0 # Update the loops if data[2] != -1: data[2] -= 1 if data[2] < 0: # There are no more loops left, the animation must be stopped stage = data[4] stage.stop_timer(key) callback = data[3] if callback != None: callback(item)
def start_move(item, left, top, duration, acceleration = None, move_marks = None, callback = None): """ Starts an animation that moves the specified item in a straight line to the specified position. - item: Item. - left: Target left. - top: Target top. - duration: Duration in milliseconds of the move animation. - acceleration: Acceleration. It use a sinusoidal function to calculate the displacement as time pass. A pair with (maxvel_t, maxvel_dist, tension) should be passed in this parameter to control the movement, where: * maxvel_t: Value between 0 and 1 that indicate the percentage of the time of the point with the higher velocity. If you indicate 0.5 the maximum velocity will be reached in the middle of the movement. * maxvel_dist: Value between 0 and 1 that indicate the percentage of the distance that is traveled at maxvel_t. * tension: Indicate how curved is the movement, it is power that is applied to the sine function. If it is 1 the movement is a sinusoidal function. Values less than 1 indicate a movement more closer to an uniform movement, and values greater that 1 indicates a more accelerated displacement. Use None to generate a uniform move. - move_marks: An instance of MoveMarks. Define an image that is repeated to mark the path traveled. None to don't print move marks. - callback: Function that is invoked with parameter item when the animation is completed. None there is no callback function. """ stage = __get_item_stage(item) # If a move timer is already started for the item, stop it key = (item, "move") milliseconds = 20 stage.stop_timer(key) item_left = item.get_left() item_top = item.get_top() # Initialize the move marks data if move_marks == None: move_marks_data = None else: angle = math.atan2(top - item_top, left - item_left) dx_per_mark = math.cos(angle) * move_marks.separation dy_per_mark = math.sin(angle) * move_marks.separation if abs(dx_per_mark) < 1 and abs(dy_per_mark) < 1: move_marks_data = None else: mark_image_dx = item.get_width() / 2 - move_marks.mark_image.get_width() / 2 mark_image_dy = item.get_height() / 2 - move_marks.mark_image.get_height() / 2 move_marks_data = [item_left - dx_per_mark, item_top - dy_per_mark, dx_per_mark, dy_per_mark, mark_image_dx, mark_image_dy] # Start the timer data = [None, duration, item_left, item_top, left, top, acceleration, move_marks, move_marks_data, callback, stage] stage.start_timer(key, milliseconds, __timer_move, data, True, True)
def __timer_wait(key, data): """ This function is invoked by a timer to invoke a callback after a wait. - key: Timer's key. - data: Timer's data. """ stage = data[0] callback = data[1] stage.stop_timer(key) callback()
def __timer_resize(key, data): """ This function is invoked by a timer to perform an step of a resize animation. - key: Timer's key. - data: Timer's data. """ item_image = key[0] # Calculate the the elapsed time now = pygame.time.get_ticks() if data[0] == None: elapsed_time = 0 data[0] = now else: elapsed_time = now - data[0] # Get the resize data image = data[1] duration = data[2] origin_type = data[3] origin_pos = data[4] from_size = data[5] to_size = data[6] # Calculate the new size percentage = min(1, (float(elapsed_time) / duration)) new_size = (int(from_size[0] + (to_size[0] - from_size[0]) * percentage), int(from_size[1] + (to_size[1] - from_size[1]) * percentage)) # Calculate the new position if origin_type == 1: new_left = origin_pos[0] - new_size[0] / 2 new_top = origin_pos[1] - new_size[1] / 2 else: new_left = item_image.get_left() new_top = item_image.get_top() # Set the new size resized_image = assets.Image(pygame.transform.smoothscale(image.surface, new_size)) item_image.set_image(resized_image) # Update the image and the position in the item item_image.set_image(resized_image) item_image.set_left(new_left) item_image.set_top(new_top) if percentage >= 1: # The item reaches the target location, the animation should be stopped stage = data[8] stage.stop_timer(key) callback = data[7] if callback != None: callback(item_image)
def __timer_wait_locked(key, data): """ This function is invoked by a timer to invoke a callback after a locked wait. - key: Timer's key. - data: Timer's data. """ stage = data[0] callback = data[1] stage.stop_timer(key) stage.unlock_ui() if callback != None: callback()
def cancel_blind_layer(stage, layer, show): """ Cancel a blind effect over the specified layer. - stage: Stage. - layer: Layer. - show: True to show the full layer, False to hide the layer. """ key = (layer, "blind") stage.stop_timer(key) if show: layer.set_clip(None) else: layer.set_clip(pygame.Rect(0, 0, 0, 0))
def fade_out_layer(layer, duration = 500, callback = None): """ Perform a fade out animation to hide the items of a layer. - layer: Layer. - duration: Duration in milliseconds of the fade in animation. - callback: Function that is invoked with parameter layer when the animation is completed. None there is no callback function. """ stage = __get_layer_stage(layer) key = (layer, "fade") milliseconds = 20 # If a fade timer was already started for the item, stop it stage.stop_timer(key) # Start the timer data = [pygame.time.get_ticks(), None, duration, callback, stage] stage.start_timer(key, milliseconds, __timer_fade_out_layer, data, True, True)
def stop_image_sequence(item_image): """ Stops an image sequence animation started over the specified item. - item_image: Item. Return current image in the sequence. """ stage = __get_item_stage(item_image) item_data = stage.stop_timer((item_image, "image_sequence")) if item_data == None: return 0 else: return item_data[0]
def start_image_sequence(item_image, images, fps, loops = 0, callback = None): """ Starts an animation showing a sequence of images through an ItemImage. - item_image: An instance of ItemImage, the images are assigned to this item. - images: Images (must be instances of assets.Image) - fps: Frames per seconds. Define the speed of the animation. - loops: Number of times that the sequence is repeated (0 to show the sequence only once). -1 to repeat the image sequences indefinitely. - callback: Function that is invoked with parameter item_image when the animation is completed. None there is no callback function. If there is already an image sequence started over the item it is stopped before start this animation. """ stage = __get_item_stage(item_image) # If a image sequence timer was already started for the item, stop it key = (item_image, "image_sequence") stage.stop_timer(key) # Start the timer milliseconds = 1000 / fps stage.start_timer(key, milliseconds, __timer_image_sequence, [0, images, loops, callback, stage])
def fade_out_item(item, remove, duration = 500, callback = None): """ Perform a fade out animation to hide an item. - item: Item. - remove: True if the item will be removed at the end of the fade-out. False otherwise. - duration: Duration in milliseconds of the fade in animation. - callback: Function that is invoked with parameter item when the animation is completed. None there is no callback function. """ stage = __get_item_stage(item) key = (item, "fade") milliseconds = 20 # If a fade timer was already started for the item, stop it stage.stop_timer(key) # Hide the rollover if there is a rollover over the item item.hide_rollover() # Start the timer data = [pygame.time.get_ticks(), None, duration, remove, callback, stage] stage.start_timer(key, milliseconds, __timer_fade_out_item, data, True, True)
def fade_in_item(item, duration = 500, callback = None, to_alpha = 255): """ Perform a fade in animation to show an item. - item: Item. - duration: Duration in milliseconds of the fade in animation. - callback: Function that is invoked with parameter item when the animation is completed. None there is no callback function. - to_alpha: Final alpha value for the fade. """ stage = __get_item_stage(item) key = (item, "fade") milliseconds = 20 if item.get_alpha() == 255: item.set_alpha(0) # If a fade timer was already started for the item, stop it stage.stop_timer(key) # Start the timer data = [pygame.time.get_ticks(), None, duration, to_alpha, callback, stage] stage.start_timer(key, milliseconds, __timer_fade_in_item, data, True, True)
def stop_blind_layer(layer): """ If there is a blind effect being applied over the specified layer, stop the effect and set the final state in the layer. - layer: Layer. """ stage = __get_layer_stage(layer) key = (layer, "blind") data = stage.stop_timer(key) if data != None: if data[9]: # There was an effect that was locking the UI, unlock it stage.unlock_ui() direction = data[1] if direction == BlindDirection.SHOW_UP or direction == BlindDirection.SHOW_DOWN: layer.set_clip(None) elif direction == BlindDirection.HIDE_UP or direction == BlindDirection.HIDE_DOWN: clip = pygame.Rect(0, data[5], data[3], data[6]) layer.set_clip(clip)
def __timer_blind_layer(key, data): """ This function is invoked by a timer to perform an step of a layer blind animation. - key: Timer's key. - data: Timer's data. """ layer = key[0] # Calculate the elapsed time now = pygame.time.get_ticks() if data[0] == None: elapsed_time = 0 else: elapsed_time = now - data[0] data[0] = now # Calculate the displacement dy_factor = data[2] dy = elapsed_time * dy_factor # Apply the displacement end_of_blind = False direction = data[1] if direction == BlindDirection.SHOW_UP: show = True if data[6] >= data[4]: # Only mark the end of the blind and invoke the callback after # that is rendered the full screen, because otherwise a rough # effect is see in the screen if the callback takes too long end_of_blind = True else: if data[6] + dy >= data[4]: data[5] = 0 data[6] = data[4] else: data[5] -= dy data[6] += dy elif direction == BlindDirection.SHOW_DOWN: show = True if data[6] >= data[4]: end_of_blind = True else: if data[6] + dy >= data[4]: data[6] = data[4] else: data[6] += dy elif direction == BlindDirection.HIDE_UP: show = False if data[6] <= 0: end_of_blind = True else: if data[6] - dy <= 0: data[6] = 0 end_of_blind = True else: data[6] -= dy elif direction == BlindDirection.HIDE_DOWN: show = False if data[6] <= 0: end_of_blind = True else: if data[6] - dy <= 0: data[5] = 0 data[6] = 0 end_of_blind = True else: data[5] += dy data[6] -= dy # Apply the clip if end_of_blind and show: layer.set_clip(None) else: clip = pygame.Rect(0, data[5], data[3], data[6]) layer.set_clip(clip) if end_of_blind: # The blind effect reach the end stage = data[8] lock_ui = data[9] stage.stop_timer(key) stage.update_mouse() if lock_ui: stage.unlock_ui() callback = data[7] if callback != None: # Render the stage before call the callback, to avoid # long processing before update the last step stage.render() callback(layer)
def blind_layer(layer, direction, area, duration = 450, callback = None, lock_ui = True): """ Apply a blind effect to show or hide the specified layer. - layer: Layer. - direction: A value of BlindDirection that indicate the direction of the blind effect. - area: Area where the blind effect is applied. None to cross the entire screen. A smaller rectangle indicate the the blind only must cross the zone defined in the rectangle. - duration: Duration in milliseconds of the blind effect to cross the full screen. If you specify an area the duration could be smaller, because it could not cross the full screen. This behavior is defined to give consistency with the default time if you apply the effect over areas with different sizes. - callback: Function to invoke with parameter Layer when the blind effect ends. None if there is no callback function. - lock_ui: Indicates if must lock the UI while performing the effect """ stage = __get_layer_stage(layer) key = (layer, "blind") milliseconds = 20 # Create the effect data, if there is a blind effect in progress stop # the effect and apply this effect from the previous position data = stage.stop_timer(key) window_width, window_height = stage.game.get_window_size() dy_factor = float(window_height) / duration if data != None and data[9]: # There was an effect that was locking the UI, unlock it stage.unlock_ui() if data != None and data[1] == direction and data[0] != None: # There is already an effect applied over the layer with # the same direction. Replace the callback and continue # with the effect data[7] = callback else: if direction == BlindDirection.SHOW_UP or \ direction == BlindDirection.SHOW_DOWN: if direction == BlindDirection.SHOW_DOWN: if area == None: current_top = 0 else: current_top = area.top else: if area == None: current_top = window_height else: current_top = area.top + area.height current_height = 0 layer.set_clip(pygame.Rect(0, current_top, window_width, 0)) elif direction == BlindDirection.HIDE_UP or \ direction == BlindDirection.HIDE_DOWN: hidden = False if not layer.get_visible(): hidden = True else: clip = layer.get_clip() if clip != None and (clip.width == 0 or clip.height == 0): hidden = True if hidden: # It is already hidden, don't apply the effect if callback != None: callback(layer) return else: if area == None: current_top = 0 current_height = window_height else: current_top = area.top current_height = area.height layer.set_clip(None) data = [None, direction, dy_factor, window_width, window_height, current_top, current_height, callback, stage, lock_ui] if lock_ui: # Lock the UI while performing the effect stage.lock_ui() # Start the timer stage.start_timer(key, milliseconds, __timer_blind_layer, data, True, True)
def __timer_move(key, data): """ This function is invoked by a timer to perform an step of a move animation. - key: Timer's key. - data: Timer's data. """ item = key[0] # Calculate the the elapsed time now = pygame.time.get_ticks() if data[0] == None: elapsed_time = 0 data[0] = now else: elapsed_time = now - data[0] # Get the move data duration = data[1] left_from = data[2] top_from = data[3] left_to = data[4] top_to = data[5] acceleration = data[6] # Calculate the displacement percentage = min(1, (float(elapsed_time) / duration)) if acceleration == None: dx = (left_to - left_from) * percentage dy = (top_to - top_from) * percentage else: # Apply the acceleration if it was indicated maxvel_t, maxvel_dist, tension = acceleration if percentage <= maxvel_t: factor = (math.cos(percentage / maxvel_t * math.pi / 2 - math.pi) + 1) factor = math.pow(factor, tension) * maxvel_dist else: factor = (math.cos((percentage - maxvel_t) / (1 - maxvel_t) * math.pi / 2 - math.pi / 2) + 1) - 1 factor = math.pow(factor, tension) * (1 - maxvel_dist) + maxvel_dist dx = (left_to - left_from) * factor dy = (top_to - top_from) * factor # Calculate the new position new_left = left_from + dx new_top = top_from + dy # Update the position in the item item.set_left(new_left) item.set_top(new_top) # Check if must define move marks move_marks = data[7] if move_marks != None: marks_data = data[8] last_mark_left = marks_data[0] last_mark_top = marks_data[1] dx_per_mark = marks_data[2] dy_per_mark = marks_data[3] # Check if must define a new mark while ((((dx_per_mark > 0) and (last_mark_left + dx_per_mark < new_left)) or \ ((dx_per_mark < 0) and (last_mark_left + dx_per_mark > new_left))) and \ (((dy_per_mark > 0) and (last_mark_top + dy_per_mark < new_top)) or \ ((dy_per_mark < 0) and (last_mark_top + dy_per_mark > new_top)))): # Create the new mark mark_x = last_mark_left + dx_per_mark mark_y = last_mark_top + dy_per_mark mark = ItemImage(mark_x + marks_data[4], mark_y + marks_data[5], move_marks.mark_image) mark.move_mark_item = item # Add the mark layer = item.get_layer() if layer != None: index = layer.items.index(item) layer.add(mark, index) # Store the data of the last mark last_mark_left = mark_x last_mark_top = mark_y marks_data[0] = last_mark_left marks_data[1] = last_mark_top if percentage >= 1: # The item reaches the target location, the animation should be stopped stage = data[10] stage.stop_timer(key) callback = data[9] if callback != None: callback(item)