Altstand nach old_version/ eingefroren (untracked). Monolith ersetzt durch main.py + Paket loader_game/. Neu: run.sh/run.command mit venv+requirements, .gitignore, docs/ANALYSE.md, 35 Tests. Secrets entfernt. Gelöscht: loader_game.py, qr_scan*.py, INSTALL_README.txt.

This commit is contained in:
Project Bridge committed 2026-11-01 10:25:13 +00:00
1 parent 6a2c891c3e
commit de550e1d4d
24 files changed
+2269 -1008

No files matched your search

+205
View File
@@ -0,0 +1,205 @@
"""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),
)