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:
1 parent
6a2c891c3e
commit
de550e1d4d
24 files changed
+2269
-1008
No files matched your search
@@ -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),
|
||||
)
|
||||
Reference in new issue
Block a user