diff --git a/README.md b/README.md index ae7c222..711053e 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ map_layer = pyscroll.BufferedRenderer(map_data, map_size) # just an example for clarity. here's a made up game engine: def game_engine_draw(): - surfaces = list() + surfaces = [] for game_object in my_game_engine: # pyscroll uses normal pygame surfaces. diff --git a/apps/demo/demo-stitched.py b/apps/demo/demo-stitched.py index 2a2c76a..0ea78d6 100644 --- a/apps/demo/demo-stitched.py +++ b/apps/demo/demo-stitched.py @@ -6,6 +6,7 @@ Very basic! No animations. """ + from __future__ import annotations from pathlib import Path @@ -24,7 +25,7 @@ VIDEORESIZE, K_r, ) -from pytmx.util_pygame import load_pygame +from pytmx.util_pygame import load_pygame # type: ignore import pyscroll from pyscroll.data import MapAggregator, TiledMapData @@ -52,7 +53,7 @@ def __init__(self) -> None: self.velocity = [0, 0] self._position = [0.0, 0.0] self._old_position = self.position - self.rect = self.image.get_rect() + self.rect: pygame.Rect = self.image.get_rect() self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8) @property @@ -67,12 +68,12 @@ def update(self, dt: float) -> None: self._old_position = self._position[:] self._position[0] += self.velocity[0] * dt self._position[1] += self.velocity[1] * dt - self.rect.topleft = self._position + self.rect.topleft = (int(self._position[0]), int(self._position[1])) self.feet.midbottom = self.rect.midbottom def move_back(self, dt: float) -> None: self._position = self._old_position - self.rect.topleft = self._position + self.rect.topleft = (int(self._position[0]), int(self._position[1])) self.feet.midbottom = self.rect.midbottom @@ -99,7 +100,7 @@ def __init__(self, screen: pygame.Surface) -> None: pyscroll_data = TiledMapData(tmx_data) world_data.add_map(pyscroll_data, offset) - self.map_layer = pyscroll.BufferedRenderer( + self.map_layer = pyscroll.orthographic.BufferedRenderer( data=world_data, size=screen.get_size(), clamp_camera=True, @@ -110,7 +111,7 @@ def __init__(self, screen: pygame.Surface) -> None: # put the hero in the center of the map self.hero = Hero() self.hero.layer = 0 - self.hero.position = (400, 400) + self.hero.position = [400.0, 400.0] # add our hero to the group self.group.add(self.hero) diff --git a/apps/demo/demo.py b/apps/demo/demo.py index c0a032c..7382ba2 100644 --- a/apps/demo/demo.py +++ b/apps/demo/demo.py @@ -10,12 +10,14 @@ See the "Quest" tutorial for a more simple use with pygame sprites and groups. """ + import collections import logging +from typing import Deque import pygame from pygame.locals import * -from pytmx.util_pygame import load_pygame +from pytmx.util_pygame import load_pygame # type: ignore import pyscroll import pyscroll.data @@ -31,7 +33,7 @@ # simple wrapper to keep the screen resizeable -def init_screen(width, height): +def init_screen(width: int, height: int) -> pygame.Surface: return pygame.display.set_mode((width, height), pygame.RESIZABLE) @@ -42,7 +44,7 @@ class ScrollTest: """ - def __init__(self, filename) -> None: + def __init__(self, filename: str) -> None: # load data from pytmx tmx_data = load_pygame(filename) @@ -60,7 +62,7 @@ def __init__(self, filename) -> None: t = ["scroll demo. press escape to quit", "arrow keys move"] # save the rendered text - self.text_overlay = [f.render(i, 1, (180, 180, 0)) for i in t] + self.text_overlay = [f.render(i, True, (180, 180, 0)) for i in t] # set our initial viewpoint in the center of the map self.center = [ @@ -71,12 +73,12 @@ def __init__(self, filename) -> None: # the camera vector is used to handle camera movement self.camera_acc = [0, 0, 0] self.camera_vel = [0, 0, 0] - self.last_update_time = 0 + self.last_update_time = 0.0 # true when running self.running = False - def draw(self, surface) -> None: + def draw(self, surface: pygame.Surface) -> None: # tell the map_layer (BufferedRenderer) to draw to the surface # the draw function requires a rect to draw to. @@ -85,7 +87,7 @@ def draw(self, surface) -> None: # blit our text over the map self.draw_text(surface) - def draw_text(self, surface) -> None: + def draw_text(self, surface: pygame.Surface) -> None: y = 0 for text in self.text_overlay: surface.blit(text, (0, y)) @@ -116,27 +118,27 @@ def handle_input(self) -> None: # but is much easier to use. pressed = pygame.key.get_pressed() if pressed[K_UP]: - self.camera_acc[1] = -SCROLL_SPEED * self.last_update_time + self.camera_acc[1] = int(-SCROLL_SPEED * self.last_update_time) elif pressed[K_DOWN]: - self.camera_acc[1] = SCROLL_SPEED * self.last_update_time + self.camera_acc[1] = int(SCROLL_SPEED * self.last_update_time) else: self.camera_acc[1] = 0 if pressed[K_LEFT]: - self.camera_acc[0] = -SCROLL_SPEED * self.last_update_time + self.camera_acc[0] = int(-SCROLL_SPEED * self.last_update_time) elif pressed[K_RIGHT]: - self.camera_acc[0] = SCROLL_SPEED * self.last_update_time + self.camera_acc[0] = int(SCROLL_SPEED * self.last_update_time) else: self.camera_acc[0] = 0 - def update(self, td) -> None: + def update(self, td: float) -> None: self.last_update_time = td friction = pow(0.0001, self.last_update_time) # update the camera vector - self.camera_vel[0] += self.camera_acc[0] * td - self.camera_vel[1] += self.camera_acc[1] * td + self.camera_vel[0] += int(self.camera_acc[0] * td) + self.camera_vel[1] += int(self.camera_acc[1] * td) self.camera_vel[0] *= friction self.camera_vel[1] *= friction @@ -165,13 +167,13 @@ def update(self, td) -> None: # set the center somewhere else # in a game, you would set center to a playable character - self.map_layer.center(self.center) + self.map_layer.center((self.center[0], self.center[1])) def run(self) -> None: clock = pygame.time.Clock() self.running = True fps = 60.0 - fps_log = collections.deque(maxlen=20) + fps_log: Deque[float] = collections.deque(maxlen=20) try: while self.running: diff --git a/apps/demo/translate.py b/apps/demo/translate.py index d827fbd..cbf8c1e 100644 --- a/apps/demo/translate.py +++ b/apps/demo/translate.py @@ -3,6 +3,7 @@ incomplete """ + import pygame @@ -18,7 +19,7 @@ def run(self) -> None: r = self._map_layer.translate_point(spr.rect.topleft) pygame.draw.circle(surface, (20, 20, 20), r, 3) - spr_list = list() + spr_list = [] for spr in self.sprites(): spr_list.append(spr.rect) diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py index bfb7a29..88bc000 100644 --- a/apps/tutorial/quest.py +++ b/apps/tutorial/quest.py @@ -8,6 +8,7 @@ pip install pytmx """ + from __future__ import annotations from pathlib import Path @@ -26,7 +27,7 @@ VIDEORESIZE, K_r, ) -from pytmx.util_pygame import load_pygame +from pytmx.util_pygame import load_pygame # type: ignore import pyscroll import pyscroll.data @@ -76,7 +77,7 @@ def __init__(self) -> None: self.velocity = [0, 0] self._position = [0.0, 0.0] self._old_position = self.position - self.rect = self.image.get_rect() + self.rect: pygame.Rect = self.image.get_rect() self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8) @property @@ -91,7 +92,7 @@ def update(self, dt: float) -> None: self._old_position = self._position[:] self._position[0] += self.velocity[0] * dt self._position[1] += self.velocity[1] * dt - self.rect.topleft = self._position + self.rect.topleft = (int(self._position[0]), int(self._position[1])) self.feet.midbottom = self.rect.midbottom def move_back(self, dt: float) -> None: @@ -100,7 +101,7 @@ def move_back(self, dt: float) -> None: """ self._position = self._old_position - self.rect.topleft = self._position + self.rect.topleft = (int(self._position[0]), int(self._position[1])) self.feet.midbottom = self.rect.midbottom @@ -131,7 +132,7 @@ def __init__(self, screen: pygame.Surface) -> None: self.walls.append(pygame.Rect(obj.x, obj.y, obj.width, obj.height)) # create new renderer (camera) - self.map_layer = pyscroll.BufferedRenderer( + self.map_layer = pyscroll.orthographic.BufferedRenderer( data=pyscroll.data.TiledMapData(tmx_data), size=screen.get_size(), clamp_camera=False, @@ -147,7 +148,8 @@ def __init__(self, screen: pygame.Surface) -> None: # put the hero in the center of the map self.hero = Hero() - self.hero.position = self.map_layer.map_rect.center + _center = self.map_layer.map_rect.center + self.hero.position = [float(i) for i in _center] # add our hero to the group self.group.add(self.hero) diff --git a/pyscroll/animation.py b/pyscroll/animation.py index 504d1ab..9b3b507 100644 --- a/pyscroll/animation.py +++ b/pyscroll/animation.py @@ -1,19 +1,30 @@ from __future__ import annotations -from collections import namedtuple from collections.abc import Sequence -from typing import Union +from typing import NamedTuple, Union + +from pygame import Surface + + +class AnimationFrame(NamedTuple): + image: Surface + duration: float + -AnimationFrame = namedtuple("AnimationFrame", "image duration") TimeLike = Union[float, int] __all__ = ("AnimationFrame", "AnimationToken") class AnimationToken: - __slots__ = ["next", "positions", "frames", "index"] - - def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None: + __slots__ = ["_next", "positions", "frames", "index"] + + def __init__( + self, + positions: set[tuple[int, int, int]], + frames: Sequence[AnimationFrame], + initial_time: float = 0.0, + ) -> None: """ Constructor @@ -26,10 +37,10 @@ def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None: frames = tuple(AnimationFrame(*i) for i in frames) self.positions = positions self.frames = frames - self.next = frames[0].duration + initial_time + self._next = frames[0].duration + initial_time self.index = 0 - def advance(self, last_time: TimeLike): + def advance(self, last_time: TimeLike) -> AnimationFrame: """ Advance the frame, and set timer for next frame @@ -52,11 +63,11 @@ def advance(self, last_time: TimeLike): # set the timer for the next advance next_frame = self.frames[self.index] - self.next = next_frame.duration + last_time + self._next = next_frame.duration + last_time return next_frame def __lt__(self, other): try: - return self.next < other.next + return self._next < other._next except AttributeError: - return self.next < other + return self._next < other diff --git a/pyscroll/data.py b/pyscroll/data.py index 860f031..fbdf021 100644 --- a/pyscroll/data.py +++ b/pyscroll/data.py @@ -4,14 +4,17 @@ If you are developing your own map format, please use this as a template. Just fill in values that work for your game. """ + from __future__ import annotations import time +from collections.abc import Iterable from heapq import heappop, heappush from itertools import product +from typing import Any import pygame -from pygame import Surface +from pygame import Rect, Surface try: # optional pytmx support @@ -43,22 +46,29 @@ class PyscrollDataAdapter: # or properties. they are listed here as class # instances, but use as properties is fine, too. - tile_size = None # (int, int): size of each tile in pixels - map_size = None # (int, int): size of map in tiles - visible_tile_layers = None # list of visible layer integers + # (int, int): size of each tile in pixels + tile_size: tuple[int, int] = (0, 0) + # (int, int): size of map in tiles + map_size: tuple[int, int] = (0, 0) + # list of visible layer integers + visible_tile_layers: list[int] = [] def __init__(self) -> None: - self._last_time = None # last time map animations were updated - self._animation_queue = list() # list of animation tokens - self._animated_tile = dict() # mapping of tile substitutions when animated - self._tracked_tiles = set() # track the tiles on screen with animations + # last time map animations were updated + self._last_time = 0.0 + # list of animation tokens + self._animation_queue: list[AnimationToken] = [] + # mapping of tile substitutions when animated + self._animated_tile: dict[tuple[int, int, int], Surface] = {} + # track the tiles on screen with animations + self._tracked_tiles = set() def reload_data(self) -> None: raise NotImplementedError def process_animation_queue( self, - tile_view: RectLike, + tile_view: Rect, ) -> list[tuple[int, int, int, Surface]]: """ Given the time and the tile view, process tile changes and return them @@ -67,12 +77,12 @@ def process_animation_queue( tile_view: Rect representing tiles on the screen """ - new_tiles = list() + new_tiles = [] # verify that there are tile substitutions ready self._update_time() try: - if self._animation_queue[0].next > self._last_time: + if self._animation_queue[0]._next > self._last_time: return new_tiles # raised with the animation queue is empty (no animations at all) @@ -84,7 +94,7 @@ def process_animation_queue( get_tile_image = self.get_tile_image # test if the next scheduled tile change is ready - while self._animation_queue[0].next <= self._last_time: + while self._animation_queue[0]._next <= self._last_time: # get the next tile/frame which is ready to be changed token = heappop(self._animation_queue) @@ -131,7 +141,7 @@ def _update_time(self) -> None: """ self._last_time = time.time() * 1000 - def prepare_tiles(self, tiles: RectLike): + def prepare_tiles(self, tiles: RectLike) -> None: """ Somewhat experimental: The renderer will advise data layer of its view @@ -155,14 +165,13 @@ def reload_animations(self) -> None: """ self._update_time() - self._animation_queue = list() - self._tracked_gids = set() - self._animation_map = dict() + self._tracked_gids: set[int] = set() + self._animation_map: dict[int, AnimationToken] = {} for gid, frame_data in self.get_animations(): self._tracked_gids.add(gid) - frames = list() + frames: list[AnimationFrame] = [] for frame_gid, frame_duration in frame_data: image = self._get_tile_image_by_id(frame_gid) frames.append(AnimationFrame(image, frame_duration)) @@ -174,7 +183,7 @@ def reload_animations(self) -> None: # locations of an animation, but searching for their locations # is slow. so it will be updated as the map is drawn. - positions = set() + positions: set[tuple[int, int, int]] = set() ani = AnimationToken(positions, frames, self._last_time) self._animation_map[gid] = ani heappush(self._animation_queue, ani) @@ -221,7 +230,7 @@ def _get_tile_image(self, x: int, y: int, l: int) -> Surface: """ raise NotImplementedError - def _get_tile_image_by_id(self, id): + def _get_tile_image_by_id(self, id: int) -> Any: """ Return Image by a custom ID. @@ -245,6 +254,9 @@ def get_animations(self) -> None: Where Frames is: [ (ID, Duration), ... ] + [tuple[int, tuple[int, float]]] + [tuple[gid, tuple[frame_gid, frame_duration]]] + And ID is a reference to a tile image. This will be something accessible using _get_tile_image_by_id @@ -289,7 +301,7 @@ class TiledMapData(PyscrollDataAdapter): """ - def __init__(self, tmx) -> None: + def __init__(self, tmx: pytmx.TiledMap) -> None: super(TiledMapData, self).__init__() self.tmx = tmx self.reload_animations() @@ -308,7 +320,7 @@ def get_animations(self): yield gid, frames def convert_surfaces(self, parent: Surface, alpha: bool = False) -> None: - images = list() + images = [] for i in self.tmx.images: try: if alpha: @@ -320,11 +332,11 @@ def convert_surfaces(self, parent: Surface, alpha: bool = False) -> None: self.tmx.images = images @property - def tile_size(self): + def tile_size(self) -> tuple[int, int]: return self.tmx.tilewidth, self.tmx.tileheight @property - def map_size(self): + def map_size(self) -> tuple[int, int]: return self.tmx.width, self.tmx.height @property @@ -332,7 +344,7 @@ def visible_tile_layers(self): return self.tmx.visible_tile_layers @property - def visible_object_layers(self): + def visible_object_layers(self) -> Iterable[pytmx.TiledObjectGroup]: return ( layer for layer in self.tmx.visible_layers @@ -345,11 +357,11 @@ def _get_tile_image(self, x: int, y: int, l: int): except ValueError: return None - def _get_tile_image_by_id(self, id) -> Surface: + def _get_tile_image_by_id(self, id: int) -> Surface: return self.tmx.images[id] def get_tile_images_by_rect(self, rect: RectLike): - def rev(seq, start, stop): + def rev(seq: list[int], start: int, stop: int) -> enumerate[int]: if start < 0: start = 0 return enumerate(seq[start : stop + 1], start) @@ -397,7 +409,7 @@ def __init__(self, tile_size) -> None: super().__init__() self.tile_size = tile_size self.map_size = 0, 0 - self.maps = list() + self.maps = [] self._min_x = 0 self._min_y = 0 @@ -408,7 +420,7 @@ def _get_tile_image(self, x: int, y: int, l: int) -> Surface: """ pass - def _get_tile_image_by_id(self, id) -> None: + def _get_tile_image_by_id(self, id: int) -> None: """ Required for sprite collation - not implemented diff --git a/pyscroll/group.py b/pyscroll/group.py index c04a88d..859f5a1 100644 --- a/pyscroll/group.py +++ b/pyscroll/group.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any import pygame @@ -17,7 +17,7 @@ class PyscrollGroup(pygame.sprite.LayeredUpdates): """ - def __init__(self, map_layer: BufferedRenderer, *args, **kwargs) -> None: + def __init__(self, map_layer: BufferedRenderer, *args: Any, **kwargs: Any) -> None: pygame.sprite.LayeredUpdates.__init__(self, *args, **kwargs) self._map_layer = map_layer @@ -54,7 +54,7 @@ def draw(self, surface: pygame.surface.Surface) -> list[pygame.rect.Rect]: draw_area = surface.get_rect() view_rect = self.view - new_surfaces = list() + new_surfaces = [] spritedict = self.spritedict gl = self.get_layer_of_sprite new_surfaces_append = new_surfaces.append diff --git a/pyscroll/isometric.py b/pyscroll/isometric.py index fec5065..61b092e 100644 --- a/pyscroll/isometric.py +++ b/pyscroll/isometric.py @@ -1,20 +1,21 @@ import logging +from pyscroll.common import Vector2D, Vector2DInt from pyscroll.orthographic import BufferedRenderer log = logging.getLogger(__file__) -def vector3_to_iso(vector3): - offset = 0, 0 +def vector3_to_iso(vector3: tuple[int, int, int]) -> tuple[int, int]: + offset = (0, 0) return ( (vector3[0] - vector3[1]) + offset[0], ((vector3[0] + vector3[1]) >> 1) - vector3[2] + offset[1], ) -def vector2_to_iso(vector2): - offset = 0, 0 +def vector2_to_iso(vector2: tuple[int, int]) -> tuple[int, int]: + offset = (0, 0) return ( (vector2[0] - vector2[1]) + offset[0], ((vector2[0] + vector2[1]) >> 1) + offset[1], @@ -34,7 +35,7 @@ def _draw_surfaces(self, surface, rect, surfaces) -> None: if surfaces is not None: [(surface.blit(i[0], i[1]), i[2]) for i in surfaces] - def _initialize_buffers(self, view_size) -> None: + def _initialize_buffers(self, view_size: Vector2DInt) -> None: """Create the buffers to cache tile drawing :param view_size: (int, int): size of the draw area @@ -85,7 +86,7 @@ def _flush_tile_queue(self) -> None: iso_y = (x + y) * thh surface_blit(tile, (iso_x, iso_y)) - def center(self, coords) -> None: + def center(self, coords: Vector2D) -> None: """center the map on a "map pixel" """ x, y = [round(i, 0) for i in coords] self.view_rect.center = x, y @@ -102,6 +103,7 @@ def center(self, coords) -> None: self._y_offset = iso[1] print(self._tile_view.size) + assert self._buffer print(self._buffer.get_size()) # center the buffer on the screen @@ -134,7 +136,7 @@ def center(self, coords) -> None: # self._buffer.fill(self._clear_color) # # v = self._tile_view - # self._tile_queue = list() + # self._tile_queue = [] # for x in range(v.left, v.right): # for y in range(v.top, v.bottom): # ix, iy = vector2_to_iso((x, y)) diff --git a/pyscroll/orthographic.py b/pyscroll/orthographic.py index 19ae364..a7cdbb0 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 TYPE_CHECKING, Optional import pygame from pygame import Rect, Surface @@ -40,7 +40,7 @@ def __init__( clamp_camera: bool = True, colorkey=None, alpha: bool = False, - time_source: Callable = time.time, + time_source: Callable[[], float] = time.time, scaling_function: Callable = pygame.transform.scale, tall_sprites: int = 0, sprite_damage_height: int = 0, @@ -73,7 +73,7 @@ def __init__( self.scaling_function = scaling_function self.tall_sprites = tall_sprites self.sprite_damage_height = sprite_damage_height - self.map_rect = None + self.map_rect = Rect(0, 0, 0, 0) # internal private defaults if colorkey and alpha: @@ -87,34 +87,39 @@ def __init__( self._clear_color = None # private attributes - self._anchored_view = True # if true, map is fixed to upper left corner - self._previous_blit = ( - None # rect of the previous map blit when map edges are visible - ) - self._size = None # actual pixel size of the view, as it occupies the screen - self._redraw_cutoff = ( - None # size of dirty tile edge that will trigger full redraw - ) - self._x_offset = None # offsets are used to scroll map in sub-tile increments - self._y_offset = None - self._buffer = None # complete rendering of tilemap - self._tile_view = None # this rect represents each tile on the buffer - self._half_width = None # 'half x' attributes are used to reduce division ops. - self._half_height = None - self._tile_queue = None # tiles queued to be draw onto buffer - self._animation_queue = ( - None # heap queue of animation token; schedules tile changes - ) - self._layer_quadtree = None # used to draw tiles that overlap optional surfaces - self._zoom_buffer = None # used to speed up zoom operations + # if true, map is fixed to upper left corner + self._anchored_view = True + # rect of the previous map blit when map edges are visible + self._previous_blit = Rect(0, 0, 0, 0) + # actual pixel size of the view, as it occupies the screen + self._size: tuple[int, int] = (0, 0) + # size of dirty tile edge that will trigger full redraw + self._redraw_cutoff = 0 + # offsets are used to scroll map in sub-tile increments + self._x_offset = 0 + self._y_offset = 0 + # complete rendering of tilemap + self._buffer: Optional[Surface] = None + # this rect represents each tile on the buffer + self._tile_view = Rect(0, 0, 0, 0) + # 'half x' attributes are used to reduce division ops. + self._half_width = 0 + self._half_height = 0 + # tiles queued to be draw onto buffer + self._tile_queue = None + # heap queue of animation token; schedules tile changes + self._animation_queue = None + # used to draw tiles that overlap optional surfaces + self._layer_quadtree: Optional[FastQuadTree] = None + # used to speed up zoom operations + self._zoom_buffer = None self._zoom_level = zoom - self._real_ratio_x = ( - 1.0 # zooming slightly changes aspect ratio; this compensates - ) - self._real_ratio_y = ( - 1.0 # zooming slightly changes aspect ratio; this compensates - ) - self.view_rect = Rect(0, 0, 0, 0) # this represents the viewable map pixels + # zooming slightly changes aspect ratio; this compensates + self._real_ratio_x = 1.0 + # zooming slightly changes aspect ratio; this compensates + self._real_ratio_y = 1.0 + # this represents the viewable map pixels + self.view_rect = Rect(0, 0, 0, 0) self.set_size(size) @@ -216,7 +221,7 @@ 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: Rect, surfaces: list[Surface] = []) -> Rect: """ Draw the map onto a surface. @@ -245,6 +250,7 @@ def draw(self, surface: Surface, rect: RectLike, surfaces: list[Surface] = None) if self._zoom_level == 1.0: self._render_map(surface, rect, surfaces) else: + assert self._zoom_buffer self._render_map(self._zoom_buffer, self._zoom_buffer.get_rect(), surfaces) self.scaling_function(self._zoom_buffer, rect.size, surface) return self._previous_blit.copy() @@ -355,12 +361,12 @@ def translate_points(self, points: list[Vector2D]) -> list[Vector2DInt]: points: points to translate """ - retval = list() + retval: list[Vector2DInt] = [] append = retval.append sx, sy = self.get_center_offset() if self._zoom_level == 1.0: for c in points: - append((c[0] + sx, c[1] + sy)) + append((int(c[0]) + sx, int(c[1]) + sy)) else: rx = self._real_ratio_x ry = self._real_ratio_y @@ -376,7 +382,7 @@ def translate_rects(self, rects: list[Rect]) -> list[Rect]: rects: rects to translate """ - retval = list() + retval: list[Rect] = [] append = retval.append sx, sy = self.get_center_offset() if self._zoom_level == 1.0: @@ -399,7 +405,7 @@ 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: Rect, surfaces: list[Surface] ) -> None: """ Render the map and optional surfaces to destination surface. @@ -425,7 +431,7 @@ def _render_map( surfaces_offset = -offset[0], -offset[1] self._draw_surfaces(surface, surfaces_offset, surfaces) - def _clear_surface(self, surface: Surface, area: RectLike = None) -> None: + def _clear_surface(self, surface: Surface, area: Optional[RectLike] = None) -> None: """ Clear the surface using the right clear color. @@ -455,7 +461,7 @@ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces) -> Non get_tile = self.data.get_tile_image tile_layers = tuple(sorted(self.data.visible_tile_layers)) top_layer = tile_layers[-1] - blit_list = list() + blit_list = [] sprite_damage = set() order = 0 @@ -495,7 +501,7 @@ 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() + column = [] is_over = False for dl, damage_rect in sprite_damage: x, y, w, h = damage_rect @@ -519,7 +525,7 @@ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces) -> Non # finally sort and do the thing blit_list.sort() - draw_list2 = list() + draw_list2 = [] for l, priority, x, y, order, s, blend in blit_list: if blend is not None: blit_op = s, (x, y), None, blend @@ -614,7 +620,7 @@ def _initialize_buffers(self, view_size: Vector2DInt) -> None: """ - def make_rect(x, y) -> Rect: + def make_rect(x: int, y: int) -> Rect: return Rect((x * tw, y * th), (tw, th)) tw, th = self.data.tile_size diff --git a/pyscroll/quadtree.py b/pyscroll/quadtree.py index 3209993..b67198b 100644 --- a/pyscroll/quadtree.py +++ b/pyscroll/quadtree.py @@ -3,6 +3,7 @@ A quadtree is used with pyscroll to detect overlapping tiles. """ + from __future__ import annotations import itertools diff --git a/tests/pyscroll/test_pyscroll.py b/tests/pyscroll/test_pyscroll.py index c7f2cef..c4ebe63 100644 --- a/tests/pyscroll/test_pyscroll.py +++ b/tests/pyscroll/test_pyscroll.py @@ -13,7 +13,7 @@ class DummyDataAdapter(PyscrollDataAdapter): visible_tile_layers = [1] def get_animations(self): - return list() + return [] def get_tile_image(self, *position): return position[0] * position[1]