Files
Habegger_loader_game/loader_game/scene.py
T

206 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Zeichenroutinen – alles, was Pixel auf den Bildschirm bringt."""
from __future__ import annotations
import pygame
from . import config
from .case import Case
from .sequences import CaseSpec
# --------------------------------------------------------------------------
# Hintergrund-Bänder
# --------------------------------------------------------------------------
def draw_top_bar(surface: pygame.Surface, assets, percent: float) -> None:
surface.fill(
config.COLOR_TOP_BAR, (0, 0, config.SCREEN_WIDTH, config.TOP_BAR_HEIGHT)
)
font = assets.font
title = font.render(config.TITLE_TEXT, True, config.COLOR_WHITE)
surface.blit(title, (40, config.TOP_BAR_HEIGHT // 2 - title.get_height() // 2))
_draw_progress_bar(surface, assets, percent)
def _draw_progress_bar(surface: pygame.Surface, assets, percent: float) -> None:
bar_width = config.PROGRESS_BAR_WIDTH
bar_height = config.PROGRESS_BAR_HEIGHT
x = config.SCREEN_WIDTH - bar_width - config.PROGRESS_BAR_RIGHT_MARGIN
y = config.TOP_BAR_HEIGHT // 2 - bar_height // 2
gap = config.PROGRESS_BAR_GAP
segment_width = (bar_width - (config.PROGRESS_BAR_SEGMENTS - 1) * gap) / config.PROGRESS_BAR_SEGMENTS
red = min(255, int(percent * 2.55))
green = max(0, 255 - int(percent * 2.55))
color = (red, green, 0)
for index in range(config.PROGRESS_BAR_SEGMENTS):
segment_x = x + index * (segment_width + gap)
rect = (segment_x, y, segment_width, bar_height)
pygame.draw.rect(surface, config.COLOR_PROGRESS_TRACK, rect, border_radius=config.PROGRESS_BAR_RADIUS)
if percent >= (index + 1) * 20:
pygame.draw.rect(surface, color, rect, border_radius=config.PROGRESS_BAR_RADIUS)
text = assets.font.render(f"{int(percent)}%", True, config.COLOR_WHITE)
surface.blit(text, (x + bar_width + 10, y))
def draw_background(surface: pygame.Surface, assets) -> None:
if assets.background is not None:
surface.blit(assets.background, (0, config.TOP_BAR_HEIGHT))
else:
pygame.draw.rect(
surface,
config.COLOR_BACKGROUND,
(0, config.TOP_BAR_HEIGHT, config.SCREEN_WIDTH, config.GAME_AREA_HEIGHT),
)
def draw_bottom_bar(surface: pygame.Surface, assets, upcoming: list[CaseSpec]) -> None:
surface.fill(
config.COLOR_BOTTOM_BAR,
(0, config.TOP_BAR_HEIGHT + config.GAME_AREA_HEIGHT, config.SCREEN_WIDTH, config.BOTTOM_BAR_HEIGHT),
)
top = config.SCREEN_HEIGHT - config.BOTTOM_BAR_HEIGHT
label = (assets.font_small or assets.font).render(config.NEXT_LABEL, True, config.COLOR_WHITE)
surface.blit(label, (40, top + 12))
preview_x = 40
for index, spec in enumerate(upcoming[: config.NEXT_PREVIEW_COUNT]):
image = _preview_surface(assets, spec)
height = image.get_height() if image is not None else 0
y = config.SCREEN_HEIGHT - config.NEXT_PREVIEW_MARGIN - height
if image is None:
width, height = _spec_size(spec)
y = config.SCREEN_HEIGHT - config.NEXT_PREVIEW_MARGIN - height
pygame.draw.rect(
surface,
config.CASE_COLORS[index % len(config.CASE_COLORS)],
(preview_x, y, width, height),
)
else:
width = image.get_width()
surface.blit(image, (preview_x, y))
preview_x += max(width, 60) + 20
_PREVIEW_CACHE: dict[tuple[int, int], pygame.Surface | None] = {}
def _preview_surface(assets, spec: CaseSpec) -> pygame.Surface | None:
"""Vorschau-Grafik in verkleinertem Massstab (gecacht)."""
key = (spec.units_w, spec.units_h)
if key in _PREVIEW_CACHE:
return _PREVIEW_CACHE[key]
image = assets.case_surface(spec.units_w, spec.units_h) if assets is not None else None
if image is not None:
size = (
max(1, int(image.get_width() * config.NEXT_PREVIEW_SCALE)),
max(1, int(image.get_height() * config.NEXT_PREVIEW_SCALE)),
)
image = pygame.transform.smoothscale(image, size)
_PREVIEW_CACHE[key] = image
return image
# --------------------------------------------------------------------------
# Auflieger
# --------------------------------------------------------------------------
def draw_trailer(surface: pygame.Surface, offset: tuple[int, int] = (0, 0)) -> None:
pygame.draw.rect(
surface,
config.COLOR_TRAILER,
(
config.TRAILER_X + offset[0],
config.TRAILER_Y + offset[1],
config.TRAILER_WIDTH,
config.TRAILER_HEIGHT,
),
)
def draw_wall(surface: pygame.Surface, wall_x: float, offset: tuple[int, int] = (0, 0)) -> None:
pygame.draw.line(
surface,
config.COLOR_WALL,
(wall_x + offset[0], config.TRAILER_Y + offset[1]),
(wall_x + offset[0], config.TRAILER_BOTTOM + offset[1]),
3,
)
# --------------------------------------------------------------------------
# Koffer
# --------------------------------------------------------------------------
def _spec_size(spec: CaseSpec) -> tuple[int, int]:
return (
int(spec.units_w * config.UNIT_LENGTH),
int(spec.units_h * config.UNIT_HEIGHT),
)
def draw_case(surface: pygame.Surface, assets, case: Case, angle: float = 0.0) -> None:
x, y = int(case.x), int(case.y)
width, height = int(case.width), int(case.height)
image = assets.case_surface(case.units_w, case.units_h) if assets is not None else None
if image is None:
pygame.draw.rect(surface, case.color, (x, y, width, height))
return
if angle:
rotated = pygame.transform.rotate(image, angle)
surface.blit(rotated, rotated.get_rect(bottomleft=(x, y + height)))
else:
surface.blit(image, (x, y))
# --------------------------------------------------------------------------
# Hinweise und Overlays
# --------------------------------------------------------------------------
def draw_hint(surface: pygame.Surface, assets, stack_possible: bool) -> None:
if stack_possible:
text, color = config.HINT_STACK, config.COLOR_HINT_STACK
else:
text, color = config.HINT_SNAP, config.COLOR_HINT_SNAP
rendered = assets.font.render(text, True, color)
surface.blit(
rendered,
(
config.SCREEN_WIDTH - config.SCREEN_WIDTH // 3 - rendered.get_width() // 2,
config.SCREEN_HEIGHT - config.BOTTOM_BAR_HEIGHT // 2 - rendered.get_height() // 2,
),
)
def draw_overlay_lines(
surface: pygame.Surface,
assets,
lines: list[str],
*,
status: str = "",
status_color: tuple[int, int, int] = config.COLOR_ERROR,
) -> None:
overlay = pygame.Surface((config.SCREEN_WIDTH, config.SCREEN_HEIGHT))
overlay.set_alpha(220)
overlay.fill((0, 0, 0))
surface.blit(overlay, (0, 0))
center_y = config.SCREEN_HEIGHT // 2
line_gap = 16
total_height = 0
rendered: list[pygame.Surface] = []
for line in lines:
image = assets.font.render(line, True, config.COLOR_WHITE)
rendered.append(image)
total_height += image.get_height() + line_gap
y = center_y - total_height // 2
for image in rendered:
surface.blit(image, (config.SCREEN_WIDTH // 2 - image.get_width() // 2, y))
y += image.get_height() + line_gap
if status:
small = assets.font_small.render(status, True, status_color)
surface.blit(
small,
(config.SCREEN_WIDTH // 2 - small.get_width() // 2, y + 20),
)