Intro: Endlosschleife raus, Alt+Enter-Vollbild, kelloggs.ini
- Keine Endlosschleife mehr: nach der Titelbild-Animation bleibt das fertige, voll-farbige Titelbild STEHEN und die Titelmusik loopt weiter, bis Leertaste oder ESC das Programm beendet (neue wait_for_key()). run_once() laeuft nur noch einmal; scene_title haelt am Ende auf Tastendruck (hold_forever), --once haelt stattdessen kurz (fuer headless/Tests). - ALT+ENTER schaltet zwischen Fenster- und Vollbildmodus um (SCALED-Display skaliert das 960x720-Logikbild sauber hoch). Zentrale Event-Behandlung in _process_event(); Zeichenroutinen holen die aktuelle Surface per pygame.display.get_surface(), damit das Neuerzeugen des Display-Modes beim Umschalten keinen veralteten Screen-Bezug hinterlaesst (screen-Parameter aus den Scenes entfernt). - Vollbild-Zustand wird in kelloggs.ini gemerkt (load/save_fullscreen_pref) und beim naechsten Start wiederhergestellt; ohne kelloggs.ini = Fenstermodus. kelloggs.ini ist gitignored (pro-Rechner-Laufzeitzustand). Verifiziert (headless): Config load/save, Alt+Enter-Toggle, Space/ESC, wait_for_key(Space)->quit; python -m game --once exit 0 in ~17s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7aea2fac26
commit
95e5b1edb4
@@ -12,3 +12,6 @@ game/tfmx_player/*.o
|
||||
# Python-Bytecode-Cache
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Laufzeit-Einstellungen des Remakes (z.B. Vollbild-Zustand) -- pro Rechner
|
||||
kelloggs.ini
|
||||
|
||||
@@ -51,13 +51,23 @@ sudo apt install python3-pygame python3-pil python3-numpy gcc
|
||||
## Starten
|
||||
|
||||
```
|
||||
python3 -m game # Intro in Endlosschleife (wie Original-Titelbildschirm)
|
||||
python3 -m game --once # ein Durchlauf, dann beenden
|
||||
python3 -m game # Intro; bleibt am Titelbild stehen (Musik loopt)
|
||||
python3 -m game --once # Titelbild nur kurz halten (für Tests/headless)
|
||||
```
|
||||
|
||||
Steuerung im Intro: **Leertaste** überspringt den aktuellen Screen, **ESC**
|
||||
beendet das Intro. Ohne Soundkarte (z.B. headless) läuft das Spiel stumm weiter
|
||||
statt abzustürzen.
|
||||
Ablauf: Rauser → Factor5 → Kellogg's → Titelbild-Animation; danach bleibt das
|
||||
fertige Titelbild stehen und die Titelmusik läuft in Schleife, bis eine Taste
|
||||
das Programm beendet.
|
||||
|
||||
Steuerung:
|
||||
- **Leertaste** — überspringt den aktuellen Intro-Screen (auf dem Titelbild
|
||||
beendet sie das Programm)
|
||||
- **ESC** — beendet das Programm
|
||||
- **Alt+Enter** — zwischen Fenster- und Vollbildmodus umschalten; der Zustand
|
||||
wird in `kelloggs.ini` gemerkt und beim nächsten Start wiederhergestellt
|
||||
(Standard ohne `kelloggs.ini`: Fenstermodus)
|
||||
|
||||
Ohne Soundkarte (z.B. headless) läuft das Spiel stumm weiter statt abzustürzen.
|
||||
|
||||
Liegt `PCKELL.DAT` woanders, kann der Pfad per Umgebungsvariable gesetzt werden:
|
||||
|
||||
|
||||
+167
-58
@@ -105,9 +105,14 @@ Ladebildschirm (render_audio_with_loading() -- Rendering laeuft in einem
|
||||
Thread, Hauptthread pumpt weiter Events). run_once() greift danach nur noch
|
||||
auf fertige WAV-Pfade zu, nie mehr Live-Rendering im Hauptloop.
|
||||
|
||||
Aufruf: python3 -m game [--once] (--once: nach einem Durchlauf beenden,
|
||||
sonst loopt die Sequenz weiter wie im Original-Titelbildschirm)
|
||||
Steuerung: LEERTASTE ueberspringt den aktuellen Screen, ESC beendet das Intro.
|
||||
Aufruf: python3 -m game [--once]
|
||||
Ablauf: Rauser -> Factor5 -> Kellogg's -> Titelbild-Animation; danach bleibt das
|
||||
fertige Titelbild STEHEN (Titelmusik loopt) bis Leertaste/ESC -> Programm-Ende.
|
||||
Keine Endlosschleife der ganzen Sequenz. (--once: Titelbild nur kurz halten,
|
||||
fuer Tests/headless.)
|
||||
Steuerung: LEERTASTE ueberspringt den aktuellen Screen (auf dem Titelbild
|
||||
beendet sie das Programm), ESC beendet immer, ALT+ENTER schaltet Fenster/Vollbild
|
||||
um (Zustand wird in kelloggs.ini gemerkt).
|
||||
|
||||
Stand 24.07.2026 (gegen echte DOSBox-Referenzaufnahmen abgeglichen):
|
||||
- Rauser wird direkt in Fensteraufloesung gerendert (ein Nearest-Upscale der
|
||||
@@ -121,6 +126,7 @@ import sys
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import configparser
|
||||
from collections import deque
|
||||
|
||||
from .formats import DATContainer, load_pcc
|
||||
@@ -140,6 +146,80 @@ DAT_PATH = os.environ.get('KELLOGG_DAT', os.path.join(PROJECT_ROOT, 'game', 'ori
|
||||
WINDOW_W, WINDOW_H = 960, 720
|
||||
BG_COLOR = (0, 0, 0)
|
||||
|
||||
# Vollbild-Zustand wird in kelloggs.ini gemerkt (Alt+Enter schaltet um). Liegt
|
||||
# noch keine kelloggs.ini vor, ist der Default Fenstermodus.
|
||||
CONFIG_PATH = os.environ.get('KELLOGG_CONFIG', os.path.join(PROJECT_ROOT, 'kelloggs.ini'))
|
||||
_fullscreen = False # aktueller Zustand; in main() aus der INI geladen
|
||||
|
||||
|
||||
def load_fullscreen_pref():
|
||||
'''Liest den gewuenschten Vollbild-Zustand aus kelloggs.ini. Fehlt die
|
||||
Datei, ist der Default Fenstermodus (False).'''
|
||||
cfg = configparser.ConfigParser()
|
||||
try:
|
||||
cfg.read(CONFIG_PATH)
|
||||
return cfg.getboolean('display', 'fullscreen', fallback=False)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def save_fullscreen_pref(fullscreen):
|
||||
'''Schreibt den aktuellen Vollbild-Zustand nach kelloggs.ini.'''
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg['display'] = {'fullscreen': 'true' if fullscreen else 'false'}
|
||||
try:
|
||||
with open(CONFIG_PATH, 'w') as f:
|
||||
cfg.write(f)
|
||||
except Exception as exc:
|
||||
print(f'[intro] konnte {CONFIG_PATH} nicht schreiben: {exc}', file=sys.stderr)
|
||||
|
||||
|
||||
def _display_flags(fullscreen):
|
||||
# SCALED: das 960x720-Logikbild wird auf Fenster- bzw. Bildschirmgroesse
|
||||
# skaliert -- so ist Vollbild ein sauberes Hochskalieren statt eines
|
||||
# winzigen 960x720-Bildes mit schwarzem Rand.
|
||||
flags = pygame.SCALED
|
||||
if fullscreen:
|
||||
flags |= pygame.FULLSCREEN
|
||||
return flags
|
||||
|
||||
|
||||
def toggle_fullscreen():
|
||||
'''Alt+Enter: zwischen Fenster und Vollbild umschalten und den Zustand in
|
||||
kelloggs.ini merken (fuers naechste Starten). Die Zeichenroutinen holen die
|
||||
aktuelle Surface jeweils per pygame.display.get_surface(), daher ist das
|
||||
Neuerzeugen des Display-Modes hier unkritisch.'''
|
||||
global _fullscreen
|
||||
_fullscreen = not _fullscreen
|
||||
try:
|
||||
pygame.display.set_mode((WINDOW_W, WINDOW_H), _display_flags(_fullscreen))
|
||||
except Exception as exc:
|
||||
print(f'[intro] Vollbild-Umschalten fehlgeschlagen: {exc}', file=sys.stderr)
|
||||
save_fullscreen_pref(_fullscreen)
|
||||
|
||||
|
||||
def _process_event(event):
|
||||
'''Gemeinsame Event-Behandlung. Rueckgabe 'quit'/'skip'/None. Alt+Enter
|
||||
(Vollbild-Toggle) wird hier abgefangen und zaehlt NICHT als skip/quit.'''
|
||||
if event.type == pygame.QUIT:
|
||||
return 'quit'
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_RETURN and (event.mod & pygame.KMOD_ALT):
|
||||
toggle_fullscreen()
|
||||
return None
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
return 'quit'
|
||||
if event.key == pygame.K_SPACE:
|
||||
return 'skip'
|
||||
return None
|
||||
|
||||
|
||||
def _blit_centered(surface):
|
||||
'''Zeichnet `surface` zentriert/skaliert auf die AKTUELLE Display-Surface
|
||||
(per get_surface(), damit ein Vollbild-Umschalten mitten in einer Szene
|
||||
keinen veralteten Screen-Bezug hinterlaesst).'''
|
||||
blit_scaled_centered(pygame.display.get_surface(), surface, WINDOW_W, WINDOW_H)
|
||||
|
||||
# "praesentiert"-Text-Faelle in KELLOGGS.PCC (siehe Docstring)
|
||||
PRAESENTIERT_FADE_INDICES = [34, 42, 43, 44, 45]
|
||||
PRAESENTIERT_FADE_SECONDS = 2.5
|
||||
@@ -356,19 +436,30 @@ def wait_or_skip(clock, seconds, screen_update_fn, fps=30):
|
||||
pygame.display.flip()
|
||||
return None
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
return 'quit'
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
return 'quit'
|
||||
if event.key == pygame.K_SPACE:
|
||||
return 'skip'
|
||||
r = _process_event(event)
|
||||
if r:
|
||||
return r
|
||||
screen_update_fn(t)
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
|
||||
|
||||
def render_audio_with_loading(screen, clock, container, name, song_index, label):
|
||||
def wait_for_key(clock, draw_fn, fps=30):
|
||||
'''Zeigt das Standbild (draw_fn(1.0)) und wartet UNENDLICH, bis der User
|
||||
Leertaste oder ESC drueckt (oder das Fenster schliesst). Alt+Enter schaltet
|
||||
dabei weiter Vollbild um. Die geloopte Titelmusik laeuft die ganze Zeit
|
||||
weiter. Gibt immer 'quit' zurueck -- danach ist das Programm zu Ende.'''
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
r = _process_event(event)
|
||||
if r in ('quit', 'skip'): # auf dem letzten Screen beendet beides
|
||||
return 'quit'
|
||||
draw_fn(1.0)
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
|
||||
|
||||
def render_audio_with_loading(clock, container, name, song_index, label):
|
||||
'''Rendert einen TFMX-Song per tfmx_audio.render_wav in einem Hintergrund-
|
||||
Thread, waehrend der Hauptthread weiter Events pumpt und einen simplen
|
||||
Ladebildschirm zeichnet.
|
||||
@@ -418,12 +509,16 @@ def render_audio_with_loading(screen, clock, container, name, song_index, label)
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
return None, True
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
return None, True
|
||||
if event.key == pygame.K_RETURN and (event.mod & pygame.KMOD_ALT):
|
||||
toggle_fullscreen()
|
||||
now = time.monotonic()
|
||||
if now - last_dot_tick > 0.4:
|
||||
dots = (dots + 1) % 4
|
||||
last_dot_tick = now
|
||||
screen = pygame.display.get_surface()
|
||||
screen.fill(BG_COLOR)
|
||||
if font is not None:
|
||||
text = font.render(f'Lade {label}{"." * dots}', True, (200, 200, 200))
|
||||
@@ -433,12 +528,12 @@ def render_audio_with_loading(screen, clock, container, name, song_index, label)
|
||||
|
||||
thread.join()
|
||||
if 'error' in result:
|
||||
print(f'[intro_sequence] {label} konnte nicht gerendert werden: {result["error"]}', file=sys.stderr)
|
||||
print(f'[intro] {label} konnte nicht gerendert werden: {result["error"]}', file=sys.stderr)
|
||||
return None, False
|
||||
return result.get('path'), False
|
||||
|
||||
|
||||
def preload_all_audio(screen, clock, assets):
|
||||
def preload_all_audio(clock, assets):
|
||||
'''Rendert VOR der eigentlichen Sequenz alle drei benoetigten TFMX-Songs
|
||||
einmalig (siehe render_audio_with_loading-Docstring) -- danach ist jeder
|
||||
run_once()-Durchlauf (auch der allererste!) garantiert freeze-frei, weil
|
||||
@@ -455,7 +550,7 @@ def preload_all_audio(screen, clock, assets):
|
||||
audio_paths = {}
|
||||
for key, label in jobs:
|
||||
name, song_index = key
|
||||
path, quit_requested = render_audio_with_loading(screen, clock, assets.container, name, song_index, label)
|
||||
path, quit_requested = render_audio_with_loading(clock, assets.container, name, song_index, label)
|
||||
audio_paths[key] = path
|
||||
if quit_requested:
|
||||
return audio_paths, True
|
||||
@@ -557,15 +652,17 @@ def render_rauser_slide(canvas, parts, slide_t):
|
||||
canvas.blit(r3, (r3_final_x, r3_y))
|
||||
|
||||
|
||||
def _draw_rauser(screen, parts, bg_color, slide_t):
|
||||
def _draw_rauser(parts, bg_color, slide_t):
|
||||
'''Zeichnet einen Rauser-Frame direkt in Fensteraufloesung: Hintergrund
|
||||
fuellen, dann die (transparent-hinterlegten) Logo-Teile an ihrer Slide-
|
||||
Position druebersetzen.'''
|
||||
Position druebersetzen. Zielt immer auf die aktuelle Display-Surface
|
||||
(get_surface), damit ein Vollbild-Umschalten nichts kaputt macht.'''
|
||||
screen = pygame.display.get_surface()
|
||||
screen.fill(bg_color)
|
||||
render_rauser_slide(screen, parts, slide_t)
|
||||
|
||||
|
||||
def scene_rauser(screen, clock, assets, audio_paths):
|
||||
def scene_rauser(clock, assets, audio_paths):
|
||||
'''Rauser-Karte: weisser Grund, Slide-in, Sound-Sting bei Ankunft, Fade zu
|
||||
grauem Grund, kurz halten. Rueckgabe 'quit'/'skip'/None (siehe wait_or_skip).
|
||||
Wird jetzt DIREKT in Fensteraufloesung gerendert (kein 320er-Zwischen-
|
||||
@@ -587,7 +684,7 @@ def scene_rauser(screen, clock, assets, audio_paths):
|
||||
sting_played = [False]
|
||||
|
||||
def show_slide(t):
|
||||
_draw_rauser(screen, parts, RAUSER_WHITE, t)
|
||||
_draw_rauser(parts, RAUSER_WHITE, t)
|
||||
if t >= 1.0 and not sting_played[0]:
|
||||
sting_played[0] = True
|
||||
if sting is not None:
|
||||
@@ -602,19 +699,19 @@ def scene_rauser(screen, clock, assets, audio_paths):
|
||||
sting_played[0] = True
|
||||
|
||||
r = wait_or_skip(clock, RAUSER_HOLD_SECONDS,
|
||||
lambda _t: _draw_rauser(screen, parts, RAUSER_WHITE, 1.0))
|
||||
lambda _t: _draw_rauser(parts, RAUSER_WHITE, 1.0))
|
||||
if r:
|
||||
return r
|
||||
|
||||
def show_fade_grey(t):
|
||||
bg = tuple(int(RAUSER_WHITE[i] + (RAUSER_GREY[i] - RAUSER_WHITE[i]) * t) for i in range(3))
|
||||
_draw_rauser(screen, parts, bg, 1.0)
|
||||
_draw_rauser(parts, bg, 1.0)
|
||||
r = wait_or_skip(clock, RAUSER_FADE_TO_GREY_SECONDS, show_fade_grey)
|
||||
if r:
|
||||
return r
|
||||
|
||||
return wait_or_skip(clock, RAUSER_GREY_HOLD_SECONDS,
|
||||
lambda _t: _draw_rauser(screen, parts, RAUSER_GREY, 1.0))
|
||||
lambda _t: _draw_rauser(parts, RAUSER_GREY, 1.0))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -711,12 +808,12 @@ def render_title_wipe_frame(quads, t):
|
||||
return canvas
|
||||
|
||||
|
||||
def scene_pause(screen, clock, seconds):
|
||||
def scene_pause(clock, seconds):
|
||||
'''Kurze schwarze Pause zwischen zwei Screens.'''
|
||||
return wait_or_skip(clock, seconds, lambda _t: screen.fill(BG_COLOR))
|
||||
return wait_or_skip(clock, seconds, lambda _t: pygame.display.get_surface().fill(BG_COLOR))
|
||||
|
||||
|
||||
def scene_factor5(screen, clock, assets, audio_paths):
|
||||
def scene_factor5(clock, assets, audio_paths):
|
||||
'''Factor5-Studio-Logo (statisches Bild) + TITEL2-Song 1 ("Brausen").
|
||||
KORREKTUR (Stefan 24.07.2026): der Screen wird EXAKT so lange gezeigt wie
|
||||
der Sound spielt -- ist er zu Ende, geht es zu Kellogg's weiter. Ohne
|
||||
@@ -734,13 +831,13 @@ def scene_factor5(screen, clock, assets, audio_paths):
|
||||
snd.play()
|
||||
except Exception as exc:
|
||||
print(f'[intro] Factor5-Sound konnte nicht geladen werden: {exc}', file=sys.stderr)
|
||||
return wait_or_skip(clock, hold,
|
||||
lambda _t: blit_scaled_centered(screen, factor5, WINDOW_W, WINDOW_H))
|
||||
return wait_or_skip(clock, hold, lambda _t: _blit_centered(factor5))
|
||||
|
||||
|
||||
def scene_kelloggs(screen, clock, assets, audio_paths):
|
||||
def scene_kelloggs(clock, assets, audio_paths):
|
||||
'''Kellogg's-Markenlogo; darunter faedet "praesentiert" per Palettenfade
|
||||
ein. Hier setzt auch die Titelmusik (TITEL Song 0) geloopt ein.'''
|
||||
ein. Hier setzt auch die Titelmusik (TITEL Song 0) geloopt ein und laeuft
|
||||
dann bis zum Ende des Programms durch (siehe scene_title).'''
|
||||
titel_path = audio_paths.get(('TITEL', None))
|
||||
if pygame.mixer.get_init() and titel_path:
|
||||
try:
|
||||
@@ -753,7 +850,7 @@ def scene_kelloggs(screen, clock, assets, audio_paths):
|
||||
|
||||
def show_kelloggs(t):
|
||||
frame = build_kelloggs_frame(kw, kh, kidx, kpal, t)
|
||||
blit_scaled_centered(screen, build_letterboxed(frame), WINDOW_W, WINDOW_H)
|
||||
_blit_centered(build_letterboxed(frame))
|
||||
r = wait_or_skip(clock, PRAESENTIERT_FADE_SECONDS, show_kelloggs)
|
||||
if r:
|
||||
return r
|
||||
@@ -761,33 +858,40 @@ def scene_kelloggs(screen, clock, assets, audio_paths):
|
||||
return wait_or_skip(clock, 1.0, lambda _t: show_kelloggs(1.0))
|
||||
|
||||
|
||||
def scene_title(screen, clock, assets):
|
||||
def scene_title(clock, assets, hold_forever=True):
|
||||
'''Titelbild -- 2x2-Quadranten-Reveal, Scanlines loesen sich von der Mitte
|
||||
auf (siehe render_title_wipe_frame), danach das fertige Bild halten.'''
|
||||
auf. Danach bleibt das fertige, voll-farbige Titelbild STEHEN (die
|
||||
Titelmusik loopt weiter), bis der User Leertaste/ESC drueckt -- dann endet
|
||||
das Programm. hold_forever=False (fuer --once/Tests) haelt stattdessen nur
|
||||
kurz und kehrt zurueck, statt auf eine Taste zu warten.'''
|
||||
quads = build_title_quadrants(assets)
|
||||
r = wait_or_skip(clock, TITLE_WIPE_SECONDS,
|
||||
lambda t: blit_scaled_centered(screen, render_title_wipe_frame(quads, t), WINDOW_W, WINDOW_H))
|
||||
if r:
|
||||
return r
|
||||
lambda t: _blit_centered(render_title_wipe_frame(quads, t)))
|
||||
if r == 'quit':
|
||||
return 'quit'
|
||||
# 'skip' waehrend der Wipe-Animation -> direkt zum fertigen Titelbild
|
||||
title_img = build_title_image(quads)
|
||||
return wait_or_skip(clock, 3.0,
|
||||
lambda _t: blit_scaled_centered(screen, title_img, WINDOW_W, WINDOW_H))
|
||||
draw = lambda _t: _blit_centered(title_img)
|
||||
if hold_forever:
|
||||
return wait_for_key(clock, draw)
|
||||
return wait_or_skip(clock, 3.0, draw)
|
||||
|
||||
|
||||
def run_once(screen, clock, assets, audio_paths):
|
||||
'''Spielt die komplette Intro-Sequenz einmal ab. LEERTASTE ueberspringt den
|
||||
jeweils aktuellen Screen (die Scene liefert dann 'skip' -> es geht sofort
|
||||
zur naechsten Scene), ESC/Fenster-X beendet das Intro komplett. Gibt True
|
||||
zurueck, wenn der User ganz beenden will (dann bricht auch die
|
||||
Endlosschleife in main() ab).'''
|
||||
def run_once(clock, assets, audio_paths, hold_title=True):
|
||||
'''Spielt die komplette Intro-Sequenz EINMAL ab (keine Endlosschleife mehr).
|
||||
LEERTASTE ueberspringt den jeweils aktuellen Screen (die Scene liefert dann
|
||||
'skip' -> naechste Scene), ESC/Fenster-X beendet das Intro. Der letzte
|
||||
Screen (Titelbild) bleibt bei hold_title=True stehen und die Titelmusik
|
||||
loopt, bis Leertaste/ESC das Programm beendet. hold_title=False (--once)
|
||||
haelt das Titelbild nur kurz -- fuer automatisierte/headless Laeufe.'''
|
||||
scenes = [
|
||||
lambda: scene_rauser(screen, clock, assets, audio_paths),
|
||||
lambda: scene_pause(screen, clock, 0.5),
|
||||
lambda: scene_factor5(screen, clock, assets, audio_paths),
|
||||
lambda: scene_pause(screen, clock, 0.5),
|
||||
lambda: scene_kelloggs(screen, clock, assets, audio_paths),
|
||||
lambda: scene_pause(screen, clock, 0.5),
|
||||
lambda: scene_title(screen, clock, assets),
|
||||
lambda: scene_rauser(clock, assets, audio_paths),
|
||||
lambda: scene_pause(clock, 0.5),
|
||||
lambda: scene_factor5(clock, assets, audio_paths),
|
||||
lambda: scene_pause(clock, 0.5),
|
||||
lambda: scene_kelloggs(clock, assets, audio_paths),
|
||||
lambda: scene_pause(clock, 0.5),
|
||||
lambda: scene_title(clock, assets, hold_forever=hold_title),
|
||||
]
|
||||
for scene in scenes:
|
||||
r = scene()
|
||||
@@ -802,6 +906,7 @@ def run_once(screen, clock, assets, audio_paths):
|
||||
|
||||
|
||||
def main():
|
||||
global _fullscreen
|
||||
once = '--once' in sys.argv
|
||||
pygame.mixer.pre_init(44100, -16, 2, 512)
|
||||
pygame.init()
|
||||
@@ -810,8 +915,12 @@ def main():
|
||||
except pygame.error as exc:
|
||||
# Kein Audio-Device vorhanden (z.B. headless VM/Container ohne
|
||||
# Soundkarte) -- Sequenz laeuft stumm weiter statt abzustuerzen.
|
||||
print(f'[intro_sequence] Audio nicht verfuegbar, laeuft stumm: {exc}', file=sys.stderr)
|
||||
screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
|
||||
print(f'[intro] Audio nicht verfuegbar, laeuft stumm: {exc}', file=sys.stderr)
|
||||
|
||||
# Vollbild-Zustand aus kelloggs.ini (Default Fenster). Alt+Enter schaltet zur
|
||||
# Laufzeit um und speichert den neuen Zustand wieder dorthin.
|
||||
_fullscreen = load_fullscreen_pref()
|
||||
pygame.display.set_mode((WINDOW_W, WINDOW_H), _display_flags(_fullscreen))
|
||||
pygame.display.set_caption("Tony & Friends in Kellogg's Land -- Intro (Remake)")
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
@@ -819,14 +928,14 @@ def main():
|
||||
|
||||
# Alle TFMX-Songs EINMALIG vor der eigentlichen Sequenz rendern (mit
|
||||
# sichtbarem, responsivem Ladebildschirm) -- siehe render_audio_with_loading
|
||||
# zum Hang-Bug, den das behebt. Danach greifen alle run_once()-Durchlaeufe
|
||||
# nur noch auf fertige WAV-Pfade zu, nie mehr live-render im Hauptloop.
|
||||
audio_paths, quit_requested = preload_all_audio(screen, clock, assets)
|
||||
# zum Hang-Bug, den das behebt. Danach greift die Sequenz nur noch auf
|
||||
# fertige WAV-Pfade zu, nie mehr live-render im Hauptloop.
|
||||
audio_paths, quit_requested = preload_all_audio(clock, assets)
|
||||
|
||||
# EIN Durchlauf (keine Endlosschleife): endet am stehenden Titelbild, das
|
||||
# bis Leertaste/ESC gehalten wird (Titelmusik loopt). --once haelt nur kurz.
|
||||
if not quit_requested:
|
||||
quit_requested = run_once(screen, clock, assets, audio_paths)
|
||||
while not quit_requested and not once:
|
||||
quit_requested = run_once(screen, clock, assets, audio_paths)
|
||||
run_once(clock, assets, audio_paths, hold_title=not once)
|
||||
|
||||
if pygame.mixer.get_init():
|
||||
pygame.mixer.music.stop()
|
||||
|
||||
Reference in New Issue
Block a user