#!/usr/bin/python3 import os from random import randrange from settings import * class Cache: def __init__(self): self.stacked_sprite_cache = {} self.viewing_angle = 360 // NUM_ANGLES self.outline_thickness = 2 self.get_stacked_sprite_cache() def get_stacked_sprite_cache(self): for object_name in SPRITE_ATTRS: # loop through static sprites and create a dict with a dict inside for the - self.stacked_sprite_cache[object_name] = { # stacked sprites of each rotation "rotated_sprites": {} } attrs = SPRITE_ATTRS[object_name] layer_array = self.get_layer_array(attrs) # get a list of all sprites self.run_prerender(object_name, layer_array, attrs) # render the sprites into one 3d looking sprite for object_name in ANIMATED_SPRITE_ATTRS: # loop through all animated sprites and do the same as in the first - self.stacked_sprite_cache[object_name] = { # loop but for every frame "frames": {} } attrs = ANIMATED_SPRITE_ATTRS[object_name] frames = self.get_frame_dict(attrs) self.run_animation_prerender(object_name, frames, attrs) def run_prerender(self, object_name, layer_array, attrs): # prerender static sprite outline = attrs.get("outline", OUTLINE) # load the outline attribute of the sprite or use the default if the - # attribute does not exist for angle in range(NUM_ANGLES): # prerender each angle surface = pygame.Surface(layer_array[0].get_size()) # this code lump calculates the size of the sprite - surface = pygame.transform.rotate(surface, angle * self.viewing_angle) # surface and creates it sprite_surface = pygame.Surface([ surface.get_width(), surface.get_height() + attrs["layers"] * attrs["scale"] ]) sprite_surface.fill(TRANSP_COLOR) # make just one color transparent to save the alpha channel and RAM usage sprite_surface.set_colorkey(TRANSP_COLOR) for i, layer in enumerate(layer_array): # blit each layer onto the sprite surface layer = pygame.transform.rotate(layer, angle * self.viewing_angle) sprite_surface.blit(layer, (0, i * attrs["scale"])) # get outline if outline: outline_coords = pygame.mask.from_surface(sprite_surface).outline() # get the coordinates of the - # outline pygame.draw.polygon(sprite_surface, "black", outline_coords, self.outline_thickness) # draw the - # outline image = pygame.transform.flip(sprite_surface, True, True) # mirror the image self.stacked_sprite_cache[object_name]["rotated_sprites"][angle] = image # add the surface to the cache def run_animation_prerender(self, object_name, frames, attrs): # does the same as the run_prerender function but - outline = attrs.get("outline", OUTLINE) # for every frame of the animated sprite for angle in range(NUM_ANGLES): surface = pygame.Surface(list(frames.items())[0][1][0].get_size()) # empty sprite surface gets generated - surface = pygame.transform.rotate(surface, angle * self.viewing_angle) # here as a default to improve - surface_default = pygame.Surface([ # start time surface.get_width(), surface.get_height() + attrs["layers"] * attrs["scale"] ]) for frame_name, layer_array in frames.items(): # render all frames sprite_surface = surface_default sprite_surface.fill(TRANSP_COLOR) sprite_surface.set_colorkey(TRANSP_COLOR) for i, layer in enumerate(layer_array): # blit each layer to the sprite surface layer = pygame.transform.rotate(layer, angle * self.viewing_angle) sprite_surface.blit(layer, (0, i * attrs["scale"])) # get outline if outline: outline_coords = pygame.mask.from_surface(sprite_surface).outline() pygame.draw.polygon(sprite_surface, "black", outline_coords, self.outline_thickness) image = pygame.transform.flip(sprite_surface, True, True) if not frame_name in self.stacked_sprite_cache[object_name]["frames"]: # add the frame's subdict with - # the rotated sprites cache self.stacked_sprite_cache[object_name]["frames"][frame_name] = {"rotated_sprites": {}} # save the frame's sprite self.stacked_sprite_cache[object_name]["frames"][frame_name]["rotated_sprites"][angle] = image def get_layer_array(self, attrs, path=None): if not path is None: # use path parameter if it is an animated sprite because animated sprites have a path to - attrs["path"] = path # a folder instead of a path to just one image file as attribute # load sprite sheet sprite_sheet = pygame.image.load(attrs["path"]).convert_alpha() # scaling sprite_sheet = pygame.transform.scale(sprite_sheet, vec2(sprite_sheet.get_size()) * attrs["scale"]) sheet_width, sheet_height = sprite_sheet.get_size() sprite_width = sheet_width // attrs["layers"] # new width to prevent error sheet_width = sprite_width * attrs["layers"] # get sprites layer_array = [] for x in range(0, sheet_width, sprite_width): sprite = sprite_sheet.subsurface((x, 0, sprite_width, sheet_height)) layer_array.append(sprite) return layer_array def get_frame_dict(self, attrs): frames = {} for path, folders, files in os.walk(attrs["path"]): # loop through all frames and add them to the sheet list for file in files: # use the name of the file without the file format specifier (the part before the dot) as the frame - # name and store the layer array frames[file.split(".")[0]] = self.get_layer_array(attrs, f"{path}/{file}") return frames