Python 114 lines
"""Inter-level loading screen.
Sits between the level menu and gameplay so the player gets one focused
beat — title, tagline, character avatar, control hints — before the
room becomes interactive. Replaces the older in-level intro card that
used to draw over a live world.
Press-or-timeout: auto-advances after ``LOADING_SCREEN_DURATION`` or on
Enter / Space / left-click / Esc. ``main.py`` gates this screen inside
``_start_level``; R-retry and pause-restart bypass it deliberately so
death-heavy levels stay snappy.
"""
import pygame
import theme
from settings import LOADING_SCREEN_DURATION
from units import CHARACTER_INFO
def _asset_folder_for(character_id):
"""Map the menu's character id (e.g. ``"c_wiz"``) to its sprite-
sheet folder name. Falls back to the Wizard's folder so a missing
id renders something instead of crashing."""
for cid, cls, _name, _tagline in CHARACTER_INFO:
if cid == character_id:
return cls.asset_folder
return "wizard"
class LoadingScreen:
"""One-shot scene shown before a level loads."""
AVATAR_SCALE = 3
def __init__(self, width, height, level_entry, character_id):
self.width = width
self.height = height
self.level_entry = level_entry
self.timer = LOADING_SCREEN_DURATION
self.skipped = False
self._dust = theme.PixelDust(width, height, seed=41)
self._title_font = theme.font(theme.FONT_TITLE)
self._tagline_font = theme.font(theme.FONT_HEADING)
self._hint_font = theme.font(theme.FONT_CAPTION)
folder = _asset_folder_for(character_id)
self._avatar_frames = theme._load_idle_frames(
folder, self.AVATAR_SCALE)
self._ticks_at_start = pygame.time.get_ticks()
@property
def done(self):
return self.skipped or self.timer <= 0
def update(self, dt):
if self.timer > 0:
self.timer = max(0.0, self.timer - dt)
def handle_input(self, event):
"""Return True when the player skipped the screen. Esc is left
alone so main.py's global Esc handler can route it (cancel back
to the level menu, rather than advancing into the level)."""
if event.type == pygame.KEYDOWN and event.key in (
pygame.K_RETURN, pygame.K_SPACE):
self.skipped = True
return True
if (event.type == pygame.MOUSEBUTTONDOWN
and event.button == 1):
self.skipped = True
return True
return False
def draw(self, screen):
screen.fill(theme.BG)
self._dust.draw(screen)
cx = self.width // 2
title_y = self.height // 3 - 30
title = (self.level_entry.title or "LEVEL").upper()
t_surf = self._title_font.render(title, True, theme.TITLE_C)
screen.blit(t_surf, t_surf.get_rect(center=(cx, title_y)))
ly = t_surf.get_rect(center=(cx, title_y)).bottom + 16
pygame.draw.line(screen, theme.LINE_C,
(cx - 170, ly), (cx + 170, ly), 2)
tagline = self.level_entry.tagline or ""
if tagline:
s_surf = self._tagline_font.render(tagline, True, theme.MUTED)
screen.blit(s_surf, s_surf.get_rect(center=(cx, ly + 50)))
# Avatar: pick one idle frame; cycle slowly so it reads as alive
# without distracting from the title.
if self._avatar_frames:
ticks = pygame.time.get_ticks() - self._ticks_at_start
idx = (ticks // 200) % len(self._avatar_frames)
frame = self._avatar_frames[idx]
screen.blit(frame, frame.get_rect(
center=(cx, self.height // 2 + 90)))
d = theme.HINT_DOT
hint = (f"WASD/Arrows move {d} Space shoot {d} "
f"Shift ability {d} E use")
h_surf = self._hint_font.render(hint, True, theme.MUTED)
screen.blit(h_surf, h_surf.get_rect(
center=(cx, self.height - 70)))
skip_hint = self._hint_font.render(
"Enter / Space / click to skip", True, theme.MUTED)
screen.blit(skip_hint, skip_hint.get_rect(
center=(cx, self.height - 36)))