diff --git a/pyscroll/data.py b/pyscroll/data.py index 860f031..d80c50f 100644 --- a/pyscroll/data.py +++ b/pyscroll/data.py @@ -156,6 +156,7 @@ def reload_animations(self) -> None: """ self._update_time() self._animation_queue = list() + self._animated_tile = dict() self._tracked_gids = set() self._animation_map = dict() diff --git a/pyscroll/orthographic.py b/pyscroll/orthographic.py index e0fa07e..8981f6d 100644 --- a/pyscroll/orthographic.py +++ b/pyscroll/orthographic.py @@ -5,7 +5,7 @@ import time from collections.abc import Callable from itertools import chain, product -from typing import TYPE_CHECKING +from typing import Any, Callable, Optional, TYPE_CHECKING, Union import pygame from pygame import Rect, Surface @@ -16,6 +16,7 @@ if TYPE_CHECKING: from .data import PyscrollDataAdapter + log = logging.getLogger(__file__) @@ -74,6 +75,9 @@ def __init__( self.tall_sprites = tall_sprites self.sprite_damage_height = sprite_damage_height self.map_rect = None + self.blit_list_sort_key: Optional[ + Callable[[tuple[int, int, int, int, int, Surface, Optional[int]]], Any] + ] = None # internal private defaults if colorkey and alpha: @@ -216,7 +220,14 @@ def center(self, coords: Vector2D) -> None: self._tile_view.move_ip(dx, dy) self.redraw_tiles(self._buffer) - def draw(self, surface: Surface, rect: RectLike, surfaces: list[Surface] = None): + def draw( + self, + surface: Surface, + rect: RectLike, + surfaces: Optional[ + list[Union[tuple[Surface, Rect, int], tuple[Surface, Rect, int, int]]] + ] = None, + ): """ Draw the map onto a surface. @@ -399,7 +410,12 @@ def translate_rects(self, rects: list[Rect]) -> list[Rect]: return retval def _render_map( - self, surface: Surface, rect: RectLike, surfaces: list[Surface] + self, + surface: Surface, + rect: RectLike, + surfaces: Optional[ + list[Union[tuple[Surface, Rect, int], tuple[Surface, Rect, int, int]]] + ], ) -> None: """ Render the map and optional surfaces to destination surface. @@ -439,7 +455,14 @@ def _clear_surface(self, surface: Surface, area: RectLike = None) -> None: ) surface.fill(clear_color, area) - def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces) -> None: + def _draw_surfaces( + self, + surface: Surface, + offset: Vector2DInt, + surfaces: list[ + Union[tuple[Surface, Rect, int], tuple[Surface, Rect, int, int]] + ], + ) -> None: """ Draw surfaces while correcting overlapping tile layers. @@ -495,30 +518,26 @@ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces) -> Non # equal to the damaged layer, then add the entire column of # tiles to the blit_list. if not, then discard the column and # do not update the screen when the damage was done. - column = list() - is_over = False + column = [] for dl, damage_rect in sprite_damage: x, y, w, h = damage_rect tx = x // w + left ty = y // h + top # TODO: heightmap - for l in tile_layers: + for l in [l for l in tile_layers if dl <= l]: tile = get_tile(tx, ty, l) if tile: sx = x - ox sy = y - oy - if dl <= l: - is_over = True blit_op = l, 0, sx, sy, order, tile, None column.append(blit_op) order += 1 - if is_over: + if len(column): blit_list.extend(column) column.clear() - is_over = False # finally sort and do the thing - blit_list.sort() + blit_list.sort(key=self.blit_list_sort_key) draw_list2 = list() for l, priority, x, y, order, s, blend in blit_list: if blend is not None: @@ -528,6 +547,31 @@ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces) -> Non draw_list2.append(blit_op) surface.blits(draw_list2, doreturn=False) + def set_blit_list_sort_key( + self, + key: Optional[ + Callable[[tuple[int, int, int, int, int, Surface, Optional[int]]], Any] + ], + ) -> None: + """Set the key function for sorting list of blit operations on sprites + and tiles overlaying sprites in the _draw_surfaces method. The elements + in the list are 7-tuples with the following contents: + element 0 (int): layer number + element 1 (int): priority - 0 for tile, 1 for sprite + element 2 (int): x coordinate for the left corner of the surface + element 3 (int): y coordinate for the top-left corner of the surface + element 4 (int): the pre-sorted order of elements in the list + element 5 (Surface): the source image for the blit + element 6 (int): Special flags for blit operation + + The default key is None, yeilding a sort first on element 0, then 1, 2, ..., 6. + + To alternatinely sort first on layer, then bottom y coordinate, then priority, + the key function could be set to the following lambda function: + lambda x: (x[0], x[3]+x[5].get_height(), x[1]) + """ + self.blit_list_sort_key = key + def _queue_edge_tiles(self, dx: int, dy: int) -> None: """ Queue edge tiles and clear edge areas on buffer if needed.