paperdoll.py

Paperdoll System - Modular character rendering with layered sprites.

Allows combining multiple layers (head, body, feet) from a single spritesheet
into a unified character, with independent animations per layer.
Supports scaling and resizing at draw time.

class Layer

Description

Represents one visual layer of the paperdoll (e.g., head, body, feet).

Attributes:
   id (str): Unique identifier (e.g., "head").
   src_y (int): Vertical offset in the spritesheet frame (in pixels).
   height (int): Height of this layer (typically frame_height / number_of_layers).
   width (int): Width of this layer (same as frame_width).
   offset_x, offset_y (int): Extra pixel offset when drawing (for fine tuning).
   opacity (float): 0.0 to 1.0 (currently not used in rendering, but kept for future).
   visible (bool): Whether to draw this layer.
   texture_override (pygame.Surface | None): If set, use this surface instead of the main texture.

Methods
def __init__(self: Any, layer_id: str, src_y: int, height: int, width: int = 32, offset_x: int = 0, offset_y: int = 0, opacity: float = 1.0, visible: bool = True) -> Any
No documentation
def set_texture(self: Any, surface: pygame.Surface) -> None
Override the texture for this layer (e.g., equipment).
def reset_texture(self: Any) -> None
Revert to using the main spritesheet texture.

class Animation

Description

Simple frame‑based animation.

Attributes:
   name (str): Animation name (e.g., "walk").
   frames (List[int]): List of frame indices (0‑based) to play.
   duration (int): Duration per frame in milliseconds.

Methods
def __init__(self: Any, name: str, frames: List[int], duration: int, loop: Optional[bool] = False) -> Any
No documentation
def get_frame(self: Any, progress: int) -> int
Return the frame index for the given progress (auto‑loops).
def total_frames(self: Any) -> int
No documentation

class Paperdoll

Description

A modular character made of stacked layers from a single spritesheet.

Attributes:
   texture (pygame.Surface): The main spritesheet surface.
   frame_w (int): Width of each animation frame.
   frame_h (int): Height of each animation frame.
   pivot_x, pivot_y (int): Anchor point (relative to the frame) used for positioning.
   scale (float): Render scale factor (1.0 = original size).
   layers (Dict[str, Layer]): All layers, keyed by id.
   layer_order (List[str]): Order in which layers are drawn (bottom → top).
   animations (Dict[str, Animation]): All animations, keyed by name.
   current_anim (str): Currently playing animation name.
   current_frame (int): Current frame progress index (not the sprite index).
   timer (float): Accumulated time in seconds.
   speed (float): Playback speed multiplier (1.0 = normal).

Methods
def __init__(self: Any, texture: pygame.Surface, frame_w: int, frame_h: int, pivot_x: int = 0, pivot_y: int = 0) -> Any
No documentation
def add_layer(self: Any, layer: Layer) -> None
Add a layer (appended to the end of the draw order).
def get_layer(self: Any, layer_id: str) -> Optional[Layer]
Retrieve a layer by its id.
def set_layer_texture(self: Any, layer_id: str, surface: pygame.Surface) -> None
Replace the texture of a specific layer (e.g., helmet).
def set_layer_visible(self: Any, layer_id: str, visible: bool) -> None
Show or hide a layer.
def add_animation(self: Any, anim: Animation) -> None
Register an animation.
def play(self: Any, anim_name: str, reset: bool = True) -> None
Start playing an animation.

Args:
   anim_name: Name of the animation (must exist).
   reset: If True, reset frame progress to 0.
def set_scale(self: Any, scale: float) -> None
Set the render scale factor.

Args:
   scale: Multiplier (1.0 = original size). Must be > 0.
def set_size(self: Any, width: int, height: int, keep_aspect: bool = True) -> None
Resize the rendered character to the given dimensions.

This calculates the scale factor needed to fit the character into the
specified box. If keep_aspect is True, the scale is chosen to fit
entirely inside the box while preserving the original aspect ratio.
Otherwise, the scale is calculated independently for width and height
(stretching/squashing).

Args:
   width: Desired width in pixels.
   height: Desired height in pixels.
   keep_aspect: If True, preserve the original aspect ratio.
def update(self: Any, dt: float) -> None
Advance the animation timer and update the current frame.
def draw(self: Any, renderer: Any, x: int, y: int) -> None
Draw the paperdoll at the given screen position with current scale.

Args:
   renderer: An instance of OpenGLRenderer (or any renderer with blit()).
   x, y: Screen coordinates (before pivot adjustment).

Global Functions

def load_paperdoll(config_path: Union[str, Path, AtlasItem]) -> Paperdoll

Load a paperdoll from a JSON configuration file and its associated spritesheet.

The JSON must contain:
   - "texture": path to the spritesheet image.
   - "frame_width", "frame_height": dimensions of each frame.
   - "pivot": {"x", "y"} (optional, defaults to 0,0).
   - "layers": list of {"id", "src_y", "height", "offset_x", "offset_y", "opacity", "visible"}.
   - "animations": dict with animation names, each containing "frames" (list of ints) and "duration" (ms).
   - "default_animation": (optional) name of the animation to start with.

def load_paperdoll_from_atlas(config_name: str, atlas: Atlas) -> Paperdoll

Load a paperdoll from an Atlas entry that contains a JSON configuration.

The atlas item with the given name must be a JSON file.
The JSON must reference the spritesheet texture (the path can be relative to the config).
If the texture is also stored in the atlas, you can use the atlas to load it.

Back to Graphics Module