2024-07-18 10:53:51 +02:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2024-07-18 17:21:05 +02:00
|
|
|
import os
|
2024-07-19 18:24:45 +02:00
|
|
|
from sprite_stacking_engine.settings import *
|
2024-07-18 10:53:51 +02:00
|
|
|
|
|
|
|
|
2024-09-26 17:19:19 +02:00
|
|
|
class Cache: # the engine uses a cache so it doesnt have to calculate everything from start at every frame -
|
|
|
|
# (the cache also contains all the code for rendering the objects)
|
2024-07-18 10:53:51 +02:00
|
|
|
def __init__(self):
|
2024-09-26 17:19:19 +02:00
|
|
|
self.stacked_sprite_cache = {} # dict to store the sprites in
|
|
|
|
|
|
|
|
# limit angles so it doesnt have to store a sprite for every degree because it doesnt need 360 viewing angles -
|
|
|
|
# for a smooth looking rotation (180 angles is default, it needs half the RAM and still looks smooth)
|
2024-07-18 10:53:51 +02:00
|
|
|
self.viewing_angle = 360 // NUM_ANGLES
|
2024-07-19 11:41:56 +02:00
|
|
|
self.outline_thickness = 2
|
2024-07-18 10:53:51 +02:00
|
|
|
self.get_stacked_sprite_cache()
|
|
|
|
|
|
|
|
def get_stacked_sprite_cache(self):
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 10:53:51 +02:00
|
|
|
"rotated_sprites": {}
|
|
|
|
}
|
|
|
|
|
|
|
|
attrs = SPRITE_ATTRS[object_name]
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 10:53:51 +02:00
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
for object_name in ANIMATED_SPRITE_ATTRS: # loop through all animated sprites and do the same as in the first -
|
2024-09-26 17:19:19 +02:00
|
|
|
self.stacked_sprite_cache[object_name] = { # loop but for every frame of the animation
|
2024-07-18 17:21:05 +02:00
|
|
|
"frames": {}
|
|
|
|
}
|
|
|
|
|
|
|
|
attrs = ANIMATED_SPRITE_ATTRS[object_name]
|
|
|
|
frames = self.get_frame_dict(attrs)
|
|
|
|
self.run_animation_prerender(object_name, frames, attrs)
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 10:53:51 +02:00
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 10:53:51 +02:00
|
|
|
sprite_surface = pygame.Surface([
|
|
|
|
surface.get_width(),
|
2024-07-18 17:21:05 +02:00
|
|
|
surface.get_height() + attrs["layers"] * attrs["scale"]
|
2024-07-18 10:53:51 +02:00
|
|
|
])
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
sprite_surface.fill(TRANSP_COLOR) # make just one color transparent to save the alpha channel and RAM usage
|
2024-07-18 10:53:51 +02:00
|
|
|
sprite_surface.set_colorkey(TRANSP_COLOR)
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
for i, layer in enumerate(layer_array): # blit each layer onto the sprite surface
|
2024-07-18 10:53:51 +02:00
|
|
|
layer = pygame.transform.rotate(layer, angle * self.viewing_angle)
|
|
|
|
sprite_surface.blit(layer, (0, i * attrs["scale"]))
|
|
|
|
|
|
|
|
# get outline
|
|
|
|
if outline:
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 10:53:51 +02:00
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 10:53:51 +02:00
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 17:21:05 +02:00
|
|
|
|
|
|
|
for angle in range(NUM_ANGLES):
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 17:21:05 +02:00
|
|
|
surface.get_width(),
|
|
|
|
surface.get_height() + attrs["layers"] * attrs["scale"]
|
|
|
|
])
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
for frame_name, layer_array in frames.items(): # render all frames
|
2024-07-18 17:21:05 +02:00
|
|
|
sprite_surface = surface_default
|
|
|
|
|
|
|
|
sprite_surface.fill(TRANSP_COLOR)
|
|
|
|
sprite_surface.set_colorkey(TRANSP_COLOR)
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
for i, layer in enumerate(layer_array): # blit each layer to the sprite surface
|
2024-07-18 17:21:05 +02:00
|
|
|
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)
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
if not frame_name in self.stacked_sprite_cache[object_name]["frames"]: # add the frame's subdict with -
|
|
|
|
# the rotated sprites cache
|
2024-07-18 17:21:05 +02:00
|
|
|
self.stacked_sprite_cache[object_name]["frames"][frame_name] = {"rotated_sprites": {}}
|
|
|
|
|
2024-07-19 13:38:42 +02:00
|
|
|
# save the frame's sprite
|
2024-07-18 17:21:05 +02:00
|
|
|
self.stacked_sprite_cache[object_name]["frames"][frame_name]["rotated_sprites"][angle] = image
|
|
|
|
|
|
|
|
def get_layer_array(self, attrs, path=None):
|
2024-07-19 13:38:42 +02:00
|
|
|
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
|
2024-07-18 17:21:05 +02:00
|
|
|
|
2024-07-18 10:53:51 +02:00
|
|
|
# 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()
|
2024-07-18 17:21:05 +02:00
|
|
|
sprite_width = sheet_width // attrs["layers"]
|
2024-07-18 10:53:51 +02:00
|
|
|
# new width to prevent error
|
2024-07-18 17:21:05 +02:00
|
|
|
sheet_width = sprite_width * attrs["layers"]
|
2024-07-18 10:53:51 +02:00
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
2024-07-18 17:21:05 +02:00
|
|
|
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:
|
2024-07-19 13:38:42 +02:00
|
|
|
# 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
|
2024-07-18 17:21:05 +02:00
|
|
|
frames[file.split(".")[0]] = self.get_layer_array(attrs, f"{path}/{file}")
|
|
|
|
|
|
|
|
return frames
|