Rauser-Aufloesung wirklich gefixt (0.5x Nearest-Neighbor statt native 1:1 oder smoothscale) + Factor5-Sound korrigiert (TITEL2-Song1 statt ONGAME2, Multi-Song-Fund)

Pixelvermessung von Stefans Referenz-Screenshot widerlegt die letzte
"native Groesse ist richtig"-Annahme -- das Logo ist im Original klein
(~34% Bildbreite), nicht bildschirmfuellend. Der eigentliche Bloedsinn war
smoothscale() (bilinear, verwaescht Pixel-Art), nicht die 0.5x-Groesse
selbst. Fix: 0.5x beibehalten, pygame.transform.scale() (Nearest-Neighbor)
statt smoothscale().

Factor5 spielte bisher faelschlich ONGAME2 (~123s Level-Musik) ab. Fund:
TFX-Module buendeln mehrere Songs (v_songs[]) -- TITEL2 hat 2 (Song0 =
2.04s Rauser-Sting, Song1 = 2.40s), TITEL 3, ONGAME2 sogar 14. TITEL2-Song1
passt laenge-maessig exakt zu "nur ein bisschen laenger als der
Rauser-Sound" -- jetzt der Factor5-Sound, feste 5s Anzeigedauer.
This commit is contained in:
ARIA
2026-07-22 22:12:01 +00:00
parent 094aca0e1e
commit af2ead172f
3 changed files with 228 additions and 103 deletions
+72
View File
@@ -1118,3 +1118,75 @@ Logo weitergeht.
Committet + gepusht. Naechster Schritt weiterhin offen: echtes Gameplay
(Tony auf `W1L0` bewegen).
## 22.07.2026, Nacht (3): Rauser-Aufloesung wirklich gefixt + Factor5-Sound korrigiert (Multi-Song-Fund)
Stefan hatte zwei weitere Punkte gemeldet: "Rauser-Intro-Aufloesung ist immer
noch schlecht" (mit Referenz-Screenshot: `RAUSER!`-Logo klein, mittig, viel
weisser Rand drumrum) und "beim Factor5 hast du einen Level-Sound genommen,
der echte Factor5-Sound ist ein kurzes Brausen, nur wenig laenger als der
Rauser-Sound, Bild ca. 5s eingeblendet".
**Wichtiger Fund beim Reinschauen:** Zwischen den Chat-Nachrichten war im
Hintergrund (autonomer Weiterarbeits-Timer) schon ein erster Loesungsversuch
fuer beides auf der VM entstanden, aber weder ins Git-Repo synced noch
committet/gepusht -- der Chat-Verlauf hatte den Stand nicht wiedergegeben.
Ich hab den VM-Stand zuerst gegen den Repo-Stand geprueft (`diff`), den fuer
Factor5 bereits korrekten Teil uebernommen, und den Rauser-Aufloesungs-Fix
selbst nochmal nachgerechnet, weil er in der ersten Fassung (native 1.0x
Groesse) tatsaechlich einen NEUEN Bug enthielt.
**1) Rauser-Aufloesung -- diesmal wirklich pixelvermessen statt geraten:**
Stefans Referenz-Screenshot per Bounding-Box analysiert (PIL, Python auf der
VM): das Logo nimmt dort nur **34.5% der Bildbreite / 16.6% der Bildhoehe**
ein (223x83px auf einem 646x500px-Screenshot). Das widerlegt die vorige
Annahme "native Groesse (ASSET_DISPLAY_SCALE=1.0) ist richtig" klar -- bei
nativer Groesse waeren RAUSER1+RAUSER3 zusammen 198px breit auf einer
320px-Leinwand (62%), fast doppelt so viel wie die Referenz zeigt. Die
URSPRUENGLICHE 0.5x-Skalierung war groessenmaessig also die ganze Zeit
richtig -- der eigentliche Bug war die Skalierungs-METHODE:
`pygame.transform.smoothscale()` ist bilinear und verwaescht harte
Pixel-Kanten von Pixel-Art beim Verkleinern sichtbar. Fix: `smoothscale()`
durch `pygame.transform.scale()` ersetzt (Nearest-Neighbor, kein
Weichzeichnen) in `Assets.pcc_surface_scaled()` UND `pcc_surface_half_height()`
(Titelbild-Quadranten, gleiches Prinzip), `ASSET_DISPLAY_SCALE` bleibt 0.5.
Verifiziert per eigener Bounding-Box-Nachmessung des neuen Renders: **33.0%
Breite / 13.6% Hoehe** -- nah an Stefans Referenz (34.5%/16.6%), klar besser
als der 62%-Fehlgriff der vorigen (native) Version. Frisch gerenderter
Vergleichs-Screenshot:
[FILE: /shared/uploads/aria_rauser_scale05_check.png]
**2) Factor5-Sound -- Multi-Song-Fund geklaert die Zuordnung:**
Ein einzelnes TFX-Modul kann laut `tfmx.h` (`v_songs[]`/`v_songs_count`)
MEHRERE Songs buendeln, ausgewaehlt per `start_song`-Index -- das erklaert
Stefans Vermutung "das Spiel hat noch wesentlich mehr Lieder". Debug-Rendering
aller Songs in allen 3 Modulen ergab:
- `TITEL.TFX` -> 3 Songs (song0/1 lang >15s, song2 ~8.0s)
- `TITEL2.TFX` -> 2 Songs (song0 = 2.04s, song1 = 2.40s)
- `ONGAME2.TFX` -> 14(!) Songs (mehrere >15s-Tracks, vermutlich echte
Level-/Gameplay-Musik -- song3/11/12 sehr kurz 0.16-1.36s, song2/13
~10.3s; noch nicht im Detail zugeordnet, relevant fuer spaetere
Gameplay-Musik)
Die vorige Zuordnung (ONGAME2 komplett = "Factor5-Melodie") war schlicht
falsch -- vermutlich echte Level-Musik, nicht zum Studio-Logo passend (daher
Stefans "Level-Sound"-Eindruck). TITEL2-Song1 (2.40s) passt dagegen exakt zu
"nur ein bisschen laenger als der Rauser-Sound" (Rauser-Sting = TITEL2-Song0
= 2.04s, Differenz nur 0.36s). `tools/tfmx_audio.py` bekam dafuer einen
optionalen `song_index`-Parameter (durchgereicht an `render_tfmx` als
5. CLI-Arg), `run_once()` in `intro_sequence.py` spielt jetzt TITEL2-Song1
beim Factor5-Logo ab, Anzeigedauer ist fest auf `FACTOR5_HOLD_SECONDS = 5.0`
gesetzt (statt an eine -- falsche -- Songlaenge gekoppelt).
Verifiziert: `TITEL2.wav` (Song0) 2.04s, `TITEL2_song1.wav` (Song1) 2.40s,
beide mit gesunder Amplitude (kein Stille-Bug), unterschiedliche Dateigroesse
(89984 vs. 105856 Samples -- also wirklich unterschiedlicher Inhalt, keine
zufaellige Doublette).
**Ehrlicher Vorbehalt (wichtig):** Ich kann Audio nicht selbst abhoeren --
die Zuordnung TITEL2-Song1 = "Brausen" ist eine Indizien-Schlussfolgerung aus
der Songlaenge (passt sehr gut zu Stefans Beschreibung), NICHT durch
Reinhoeren verifiziert. Falls es beim echten Testen doch nicht wie ein
Brausen klingt: melden, dann probieren wir als naechste Kandidaten TITEL-Song2
(~8.0s) oder einen der kurzen ONGAME2-Songs (3/11/12).
Beide Fixes committet + gepusht. Naechster Schritt weiterhin offen: echtes
Gameplay (Tony auf `W1L0` bewegen).
+114 -79
View File
@@ -10,49 +10,76 @@ Serie vermessen, siehe NOTES.md Abschnitt "Boot-Sequenz vermessen". Kurzfassung:
Grund. RAUSER1 ("RAUSER"-Box) faehrt von LINKS ein, RAUSER3 (blaue Box
mit "!") faehrt von OBEN ein, RAUSER2 ("ADVERTAINMENT") steht von Anfang
an fest. Sobald beide Teile ihre Endposition erreicht haben, spielt ein
kurzer Sound-Sting (TITEL2.TFX/.SAM -- das kuerzeste der 3 Musikstuecke,
ein "Bling"-Jingle von ~2s natuerlicher Laenge, kein Loop). Danach faedet
der WEISSE Hintergrund kurz zu GRAU um (NICHT weiss -- war ein Fehler in
v1!), haelt kurz, dann schwarze Pause. WICHTIG: alle Rauser-Assets werden
nur bei **halber Pixel-Groesse** (0.5x) ihrer PCC-Nativaufloesung
gezeichnet -- bei 1:1 sind sie in der echten DOSBox-Aufnahme klar zu groß
(per Pixel-Vermessung bestaetigt, siehe NOTES.md "Rauser/Titelbild-
Korrektur"). KORREKTUR (22.07.2026 abends): die Assets haben keinen
Alphakanal, ihr papierweisser Hintergrund sass als harter Kasten um die
Box-Grafik und fadete beim Uebergang zu Grau NICHT mit (Stefans Meldung:
"Text bleibt hart umrandet stehen"). Fix: make_border_bg_transparent()
macht per Flood-Fill vom Bildrand aus genau diesen Aussenbereich
transparent, isolierte weisse Buchstaben MITTEN im Motiv bleiben
unangetastet.
kurzer Sound-Sting (TITEL2-Song 0 -- 2.04s natuerliche Laenge, siehe
tfmx_audio-Docstring zu den mehreren Songs pro Modul). Danach faedet der
WEISSE Hintergrund kurz zu GRAU um, haelt kurz, dann schwarze Pause.
KORREKTUR-GESCHICHTE (Aufloesungs-Bug, zwei Anlaeufe):
v1: Rauser-Assets bei 0.5x per smoothscale verkleinert -- Ergebnis war
verwaschen/unscharf (Stefans erste Meldung "Aufloesung ist
schlecht").
v2 (falscher Fix): Annahme war, die Verkleinerung selbst sei der
Fehler -- also ASSET_DISPLAY_SCALE auf 1.0 (native Groesse ohne
jede Skalierung) gesetzt. Sah in DIESEM Container scharf aus, war
aber ein NEUER Bug: bei nativer Groesse (RAUSER1+RAUSER3 = 198px
Breite) ist das Logo auf der 320px-Leinwand riesig -- Stefan hat
nie "zu gross" gemeldet, weil das schon in v1 unter dem Verwaschen
unterging.
v3 (echter Fix, 22.07.2026 spaet nachts): Stefans Referenz-Screenshot
per Bounding-Box vermessen (nicht nur behauptet) -- das Logo nimmt
dort nur ~34% der Bildbreite ein (~110px auf 320px-Leinwand). Die
urspruengliche 0.5x-GROESSE war also die ganze Zeit richtig
(182+16=198px * 0.5 ~= 99px, passt zur Referenz); der eigentliche
Fehler war die SKALIERUNGS-METHODE: pygame.transform.smoothscale()
ist bilinear und verwaschet harte Pixel-Kanten von Pixel-Art beim
Verkleinern. Fix: ASSET_DISPLAY_SCALE bleibt 0.5, aber
pygame.transform.scale() (Nearest-Neighbor, kein Weichzeichnen)
statt smoothscale() -- siehe Assets.pcc_surface_scaled(). Per
Bounding-Box-Nachmessung des eigenen Renders bestaetigt: 33.0%
Breite / 13.6% Hoehe vs. Referenz 34.5% / 16.6% -- nah dran, nicht
mehr der 62%-Breite-Fehlgriff von v2.
2. kurze schwarze Pause
3. Factor5-Studio-Logo (FACTOR5.PCC, STATISCHES Bild -- keine Animation,
das "Geister"-Doppellogo ist Teil des Assets selbst) mit einer EIGENEN
Melodie (ONGAME2.TFX/.SAM, der bis 22.07.2026 abends noch keinem Screen
zugeordnete dritte TFMX-Track -- Stefans Hinweis: "bei factor 5 gibts
auch noch ne melodie"). Der Screen bleibt exakt so lange stehen wie das
Stueck einmal komplett durchspielt (natuerliche Laenge per render_tfmx
erkannt, ~123s), nicht mehr eine feste Dauer.
das "Geister"-Doppellogo ist Teil des Assets selbst). KORREKTUR
(22.07.2026, spaeter Abend -- Stefans Meldung: "beim factor 5 hast ein
Level-Sound genommen ... der factor 5 Sound ist so ein Brausen, geht nur
ein bisschen laenger als der Rauser-Sound, das Bild wird ca. 5 Sekunden
eingeblendet"): die vorige Version hat faelschlich ONGAME2-Song 0
(~123s!) als "Factor5-Melodie" benutzt -- das war schlicht falsch
zugeordnet, vermutlich echte Level-/Gameplay-Musik (ONGAME2.TFX buendelt
laut Stichprobe sogar 14 verschiedene Songs, dazu mehr in NOTES.md).
Per Debug-Rendering aller Songs in TITEL/TITEL2/ONGAME2 gefunden:
TITEL2-Song 1 ist ein kurzer, eigenstaendiger Sound von 2.40s natuerlicher
Laenge -- passt exakt zu "nur ein bisschen laenger als der Rauser-Sound"
(Rauser-Sting = TITEL2-Song 0 = 2.04s). Das ist jetzt der Factor5-Sound.
WICHTIG (ehrlich, nicht nur behauptet): ich kann Audio nicht selbst
abhoeren, die Zuordnung ist eine Indizien-Schlussfolgerung aus der
Songlaenge (passt sehr gut), nicht durch Reinhoeren verifiziert -- falls
es beim Testen doch nicht "brausen" klingt, bitte melden, dann probieren
wir TITEL-Song 2 (~8.0s) oder einen der kurzen ONGAME2-Songs (3/11/12).
Der Screen wird jetzt fuer eine FESTE Dauer von ~5s gezeigt (statt vorher
an die -- falsche -- Songlaenge gekoppelt), der Sound spielt einmal ab
und laeuft ggf. vor Screen-Ende aus (er ist ja nur 2.4s lang).
4. kurze schwarze Pause
5. Kellogg's-Markenlogo (KELLOGGS.PCC) erscheint SOFORT komplett; darunter
faedet der Text "praesentiert" per Palette-Fade von blass nach saettigt-
rot ein (~3s).
6. kurze schwarze Pause
7. Titelbild "TONY & FRIENDS in Kellogg's Land" -- **KORREKTUR (22.07.2026,
spaeter Abend): das Bild ist NICHT 320x480!** Die 4 Quadranten KELL256A
7. Titelbild "TONY & FRIENDS in Kellogg's Land" -- die 4 Quadranten KELL256A
(oben links) / B (oben rechts) / C (unten links) / D (unten rechts) sind
zwar je 160x240 PCC-Nativaufloesung, werden aber -- exakt wie die
Rauser-Assets -- nur bei **halber Groesse** (160x120) in ein 2x2-Raster
gezeichnet: A|B oben, C|D unten -> 320x240 Gesamtbild. Per Pixel-
Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert (nicht nur
behauptet). Die alte v1-Annahme (volle 240px-Hoehe je Quadrant, 480px
Gesamthoehe) war der Grund fuer Stefans "gestaucht/zu gross"-Meldung.
Reveal-Effekt: obere Haelfte (A|B) waechst von oben (Hoehe 0->120),
untere Haelfte (C|D) waechst von unten (Hoehe 0->120) -- treffen sich in
der Mitte. ZUSAETZLICH (neuer Fund, per Screenshot-Serie entdeckt):
waehrend dieser Reveal-Phase ist die untere Haelfte erst graustichig/
entsaettigt MIT Scanlines ueberzogen: die Scanlines/Graustufen loesen
sich danach in einer zweiten Phase zu vollen Farben auf, bis das Bild
komplett scharf/farbig ist (das ist Stefans "Screenshot 4/5/6"-Effekt).
je 160x240 PCC-Nativaufloesung, werden aber nur bei **halber Groesse**
(160x120) in ein 2x2-Raster gezeichnet: A|B oben, C|D unten -> 320x240
Gesamtbild. Per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme
verifiziert. Reveal-Effekt: obere Haelfte (A|B) waechst von oben (Hoehe
0->120), untere Haelfte (C|D) waechst von unten (Hoehe 0->120) -- treffen
sich in der Mitte. Waehrend dieser Reveal-Phase ist die untere Haelfte
erst graustichig/entsaettigt MIT Scanlines ueberzogen, die sich danach in
einer zweiten Phase zu vollen Farben aufloesen (Stefans "Screenshot
4/5/6"-Effekt). HINWEIS: diese Quadranten nutzen denselben
Downscale-Mechanismus (smoothscale halbe Hoehe) wie die Rauser-Assets
vor dem obigen Fix -- hier bisher KEINE Bildschaerfe-Beschwerde von
Stefan, deshalb unangetastet gelassen. Falls das Titelbild auch mal als
"verwaschen" gemeldet wird: gleicher Fix-Ansatz (native Groesse zeichnen,
nur einmal ganzzahlig hochskalieren) anwendbar.
Alle Nicht-BOB/ICO/MAP-Assets werden per kellogg_formats.load_pcc() (Pillows
PCX-Decoder) aus PCKELL.DAT gelesen -- kein eigener RLE-Code mehr (siehe
@@ -94,9 +121,17 @@ BG_COLOR = (0, 0, 0)
PRAESENTIERT_FADE_INDICES = [34, 42, 43, 44, 45]
PRAESENTIERT_FADE_SECONDS = 2.5
# Alle Rauser- und Titelbild-PCC-Assets werden bei HALBER Nativgroesse
# gezeichnet (siehe Docstring-Korrektur) -- per Pixelvermessung gegen eine
# echte DOSBox-Aufnahme bestaetigter Skalierungsfaktor.
# KORREKTUR #2 (22.07.2026 spaet nachts, Stefans Referenz-Screenshot
# vermessen): 1.0 (native Groesse) war KEIN Aufloesungs-Fix, sondern ein
# neuer Bug -- ich hatte die Blur-Ursache falsch diagnostiziert. Per
# Pixel-Vermessung von Stefans Screenshot (bbox der Logo-Flaeche gegen
# Canvas-Groesse): das Logo nimmt im Original nur ~34% der Bildbreite ein
# (~110px auf einer 320px-Leinwand) -- bei nativer Groesse (182+16=198px)
# ist es fast 2x zu breit. Die vorige 0.5x-Skalierung war GROESSENMAESSIG
# schon richtig; der eigentliche Blur kam von pygame.transform.smoothscale
# (bilineares Resampling verwaschet harte Pixel-Kanten bei Pixel-Art). Fix:
# 0.5x beibehalten, aber mit pygame.transform.scale (Nearest-Neighbor, kein
# Weichzeichnen) statt smoothscale skalieren -- siehe pcc_surface_scaled().
ASSET_DISPLAY_SCALE = 0.5
# Rauser-Timing (siehe Docstring)
@@ -107,6 +142,10 @@ RAUSER_GREY_HOLD_SECONDS = 0.3
RAUSER_GREY = (73, 73, 73)
RAUSER_WHITE = (255, 255, 255)
# Factor5-Timing (siehe Docstring-Korrektur -- Stefans Meldung 22.07.2026
# abends): feste Anzeigedauer statt (falsch) an eine 123s-Melodie gekoppelt.
FACTOR5_HOLD_SECONDS = 5.0
# Titelbild-Timing (siehe Docstring)
TITLE_HEIGHT_REVEAL_FRACTION = 0.55 # Anteil der Wipe-Dauer fuer das Hoehenwachstum
TITLE_WIPE_SECONDS = 2.3
@@ -196,8 +235,15 @@ class Assets:
return indices_to_surface(w, h, indices, palette)
def pcc_surface_scaled(self, name, scale=ASSET_DISPLAY_SCALE, cut_border_bg=False):
'''Skaliert BEIDE Achsen um scale -- fuer die Rauser-Assets (dort per
Pixelvermessung als gleichmaessige 0.5x-Verkleinerung bestaetigt).
'''Skaliert BEIDE Achsen um scale. KORREKTUR #2 (22.07.2026 spaet
nachts, siehe Docstring-Korrektur ganz oben): scale ist wieder 0.5
(per Pixelvermessung von Stefans Referenz-Screenshot bestaetigt --
das Logo ist im Original klein, nicht bildschirmfuellend). Der
eigentliche Blur-Fix ist pygame.transform.scale statt smoothscale:
scale() ist Nearest-Neighbor (keine Weichzeichnung), smoothscale()
war bilinear und hat bei 0.5x harte Pixel-Kanten der Pixel-Art
verwaschen -- DAS war Stefans "Aufloesung ist schlecht"-Bug, nicht
die Groesse selbst.
cut_border_bg=True macht den vom Bildrand aus erreichbaren papierweissen
Hintergrund transparent (siehe make_border_bg_transparent) -- fuer die
Rauser-Teile, damit sie beim Fade zu Grau nicht als harte weisse
@@ -205,22 +251,24 @@ class Assets:
surf = self.pcc_surface(name)
if cut_border_bg:
surf = make_border_bg_transparent(surf)
if scale == 1.0:
return surf
w, h = surf.get_size()
new_size = (max(1, round(w * scale)), max(1, round(h * scale)))
return pygame.transform.smoothscale(surf, new_size)
return pygame.transform.scale(surf, new_size)
def pcc_surface_half_height(self, name):
'''Skaliert NUR die Hoehe um 0.5, Breite bleibt nativ -- fuer die
Titelbild-Quadranten KELL256A/B/C/D (160x240 -> 160x120). ACHTUNG:
das ist KEINE gleichmaessige Skalierung wie bei den Rauser-Assets --
per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert
(siehe Docstring-Korrektur ganz oben in dieser Datei). Wuerde man hier
stattdessen die Breite mitskalieren (wie bei den Rauser-Assets),
entsteht genau der "gestaucht/zu schmal"-Bug, den Stefan gemeldet hat.'''
(siehe Docstring ganz oben). Nearest-Neighbor (pygame.transform.scale)
statt smoothscale aus dem gleichen Grund wie bei den Rauser-Assets
(siehe pcc_surface_scaled) -- konsistent scharf.'''
surf = self.pcc_surface(name)
w, h = surf.get_size()
new_size = (w, max(1, round(h * 0.5)))
return pygame.transform.smoothscale(surf, new_size)
return pygame.transform.scale(surf, new_size)
def fit_scale(src_w, src_h, max_w, max_h):
@@ -290,9 +338,10 @@ def build_kelloggs_frame(w, h, indices, palette, fade_t):
# ---------------------------------------------------------------------------
def build_rauser_parts(assets):
'''Liefert die drei skalierten Rauser-Teile + ihre Endpositionen auf einer
320x240-Leinwand (RAUSER1 links, RAUSER3 rechts daneben, RAUSER2 darunter
mittig -- alle bei ASSET_DISPLAY_SCALE, siehe Docstring).'''
'''Liefert die drei (bei 0.5x Nearest-Neighbor-Groesse, siehe Docstring-
Korrektur v3) Rauser-Teile + ihre Endpositionen auf einer 320x240-
Leinwand (RAUSER1 links, RAUSER3 rechts daneben, RAUSER2 darunter
mittig).'''
# cut_border_bg=True (Fix 22.07.2026 abends): macht den papierweissen
# Hintergrund um jede Box transparent (nur die vom Bildrand aus
# zusammenhaengende Flaeche, siehe make_border_bg_transparent) -- vorher
@@ -343,12 +392,9 @@ def run_rauser(screen, clock, assets):
sting = None
try:
if pygame.mixer.get_init():
# KEIN fester seconds-Cap mehr (Fix 22.07.2026 abends, siehe
# tfmx_audio.render_wav-Docstring) -- render_tfmx erkennt das
# natuerliche Ende von TITEL2 selbst (~2s Jingle) und schneidet
# dort ab. Der alte harte 1.2s-Cap konnte den Sting theoretisch
# noch VOR seinem echten Ende kappen.
sting_wav = tfmx_audio.render_wav(assets.container, 'TITEL2')
# TITEL2 Song 0 -- der kuerzere der beiden TITEL2-Songs (~2.04s),
# Song 1 ist der neu zugeordnete Factor5-Sound (siehe Docstring).
sting_wav = tfmx_audio.render_wav(assets.container, 'TITEL2', song_index=0)
sting = pygame.mixer.Sound(sting_wav)
except Exception as exc:
print(f'[intro_sequence] Rauser-Sound-Sting konnte nicht geladen werden: {exc}', file=sys.stderr)
@@ -408,9 +454,9 @@ def run_rauser(screen, clock, assets):
# ---------------------------------------------------------------------------
def build_title_quadrants(assets):
'''Liefert die 4 Titelbild-Quadranten bei ASSET_DISPLAY_SCALE (160x120
statt 160x240 nativ) -- A/B oben, C/D unten. Per Pixel-Rekonstruktion
gegen eine echte DOSBox-Aufnahme verifiziert.'''
'''Liefert die 4 Titelbild-Quadranten bei halber Hoehe (160x120 statt
160x240 nativ) -- A/B oben, C/D unten. Per Pixel-Rekonstruktion gegen
eine echte DOSBox-Aufnahme verifiziert.'''
return {
'A': assets.pcc_surface_half_height('KELL256A'),
'B': assets.pcc_surface_half_height('KELL256B'),
@@ -498,30 +544,24 @@ def run_once(screen, clock, assets):
if quit_requested:
return True
# 3) Factor5 (statisches Bild, ABER mit eigener Melodie -- ONGAME2, der
# dritte/letzte bisher nicht zugeordnete TFMX-Track, siehe Stefans
# Meldung 22.07.2026 abends: "bei factor 5 gibts auch noch ne melodie
# solange das lied ist factor 5 anzeigen und dann zum kellogs
# praesentiert". Der Screen bleibt jetzt exakt so lange stehen wie das
# Stueck einmal komplett durchspielt (~123s natuerliche Laenge, siehe
# tfmx_audio.render_wav-Docstring), statt einer festen 3.0s-Dauer.
# 3) Factor5 (statisches Bild). KORREKTUR (22.07.2026 abends, siehe
# Docstring): NICHT mehr ONGAME2 (das war falsch zugeordnete Level-
# Musik), sondern TITEL2-Song 1 -- ein kurzer "Brausen"-Sound (~2.4s),
# der zur Beschreibung "nur ein bisschen laenger als der Rauser-Sound"
# passt. Screen-Dauer ist jetzt FEST (~5s), nicht mehr an die (falsche)
# 123s-Songlaenge gekoppelt.
factor5 = build_letterboxed(assets.pcc_surface('FACTOR5'))
factor5_duration = 3.0 # Fallback falls kein Audio verfuegbar
factor5_sound = None
try:
if pygame.mixer.get_init():
factor5_wav = tfmx_audio.render_wav(assets.container, 'ONGAME2')
factor5_wav = tfmx_audio.render_wav(assets.container, 'TITEL2', song_index=1)
factor5_sound = pygame.mixer.Sound(factor5_wav)
factor5_duration = factor5_sound.get_length()
factor5_sound.play()
except Exception as exc:
print(f'[intro_sequence] Factor5-Melodie konnte nicht geladen werden: {exc}', file=sys.stderr)
print(f'[intro_sequence] Factor5-Sound konnte nicht geladen werden: {exc}', file=sys.stderr)
def show_factor5(_t):
blit_scaled_centered(screen, factor5, WINDOW_W, WINDOW_H)
quit_requested = wait_or_skip(clock, factor5_duration, show_factor5)
if factor5_sound is not None:
factor5_sound.stop()
quit_requested = wait_or_skip(clock, FACTOR5_HOLD_SECONDS, show_factor5)
if quit_requested:
return True
@@ -531,15 +571,10 @@ def run_once(screen, clock, assets):
return True
# 5) Kellogg's-Logo + "praesentiert"-Palettenfade
# Titelmusik (TITEL.TFX/.SAM) setzt hier ein und laeuft geloopt weiter
# bis zum Ende der Sequenz (Original hat vermutlich durchgehende
# Titelmusik (TITEL Song 0, ~109s) setzt hier ein und laeuft geloopt
# weiter bis zum Ende der Sequenz (Original hat vermutlich durchgehende
# Titelmusik ueber Logo+Titelbild+Karte -- noch nicht per Referenz-
# Sichtung bestaetigt, aber plausibelste Annahme fuers erste v1).
# KEIN fester seconds-Cap mehr (Fix 22.07.2026 abends): TITEL ist
# tatsaechlich ~109s lang -- der alte 60s-Cap (plus ein liegen
# gebliebener 8s-Debug-Cache) hat das Stueck weit vor seinem echten Ende
# abgeschnitten. Das war Stefans "Musik ist zu kurz, da fehlt der
# Rest"-Bug. render_tfmx erkennt den natuerlichen Loop-Punkt jetzt selbst.
try:
if pygame.mixer.get_init():
titel_wav = tfmx_audio.render_wav(assets.container, 'TITEL')
+42 -24
View File
@@ -17,6 +17,23 @@ Weg statt TFMX in Python neu zu erfinden.
Gerenderte WAVs sind urheberrechtlich das Original-Spielmaterial (Musik),
werden daher NICHT ins Git-Repo committed (wie raw/ und extracted_dat/) --
liegen nur lokal im Cache-Verzeichnis.
MEHRERE SONGS PRO MODUL (Fund 22.07.2026 abends, Stefans Hinweis "das Spiel
hat noch wesentlich mehr Lieder"): ein einzelnes TFX-Modul kann intern
MEHRERE Songs buendeln (tfmx.h's v_songs[]/v_songs_count, ausgewaehlt per
start_song-Index). Stichprobe per Debug-Tool ergab:
TITEL.TFX -> 3 Songs (song0/1 lang/loopend >15s, song2 ~8.0s)
TITEL2.TFX -> 2 Songs (song0 ~2.04s = Rauser-Bling, song1 ~2.40s = Factor5-
Brausen -- Laenge passt zu Stefans "nur ein bisschen laenger
als der Rauser-Sound")
ONGAME2.TFX -> 14 Songs (>15s-Tracks vermutlich Level-Musik, song3/11/12
sehr kurz (0.16-1.36s) vermutlich Jingles/Stinger, song2/13
~10.3s). Noch nicht im Detail zugeordnet -- fuer die
spaetere Gameplay-Musik relevant, siehe NOTES.md.
render_wav() nimmt jetzt optional `song_index` entgegen und reicht ihn als
5. CLI-Arg an render_tfmx durch. Der Cache-Dateiname enthaelt den Song-Index
NUR wenn er nicht der Default (0/None) ist, damit bereits vorhandene Caches
fuer Song 0 (z.B. TITEL.wav) gueltig bleiben.
"""
import os
import subprocess
@@ -35,30 +52,30 @@ def _ensure_binary():
subprocess.run(['bash', os.path.join(_PLAYER_DIR, 'build.sh')], check=True)
def render_wav(container, name, seconds=180, force=False):
def render_wav(container, name, seconds=180, force=False, song_index=None):
"""container: kellogg_formats.DATContainer; name: z.B. 'TITEL' (ohne Extension).
Gibt den Pfad zur gerenderten (gecachten) WAV-Datei zurueck.
song_index: optional -- welcher der ggf. mehreren in <name>.TFX gebuendelten
Songs gerendert werden soll (siehe Modul-Docstring). None/0 = Default-
Verhalten wie bisher (erster/einziger Song). Gibt den Pfad zur gerenderten
(gecachten) WAV-Datei zurueck.
WICHTIG (Fix 22.07.2026 abends): `seconds` ist nur noch eine Sicherheits-
OBERGRENZE, keine Ziel-Laenge! render_tfmx erkennt das natuerliche Song-
Ende/den Loop-Punkt selbst (tfmx.h real_song_end) und schneidet dort ab --
z.B. ist TITEL tatsaechlich ~109s lang, ONGAME2 ~123s, TITEL2 (der Rauser-
Sting) nur ~2s. Vorher wurde hart bei `seconds` abgeschnitten, was TITEL
(mit frueher genutzten 60s) VOR dem echten Songende kappte -- das war der
Bug hinter "Musik ist zu kurz, da fehlt der Rest". Der Default hier (180s)
ist bewusst grosszuegig ueber die laengsten bekannten Stuecke hinaus
gewaehlt, damit nichts abgeschnitten wird; im Normalfall stoppt render_tfmx
laengst vorher von selbst.
z.B. ist TITEL (song 0) tatsaechlich ~109s lang, ONGAME2 (song 0) ~123s,
TITEL2 song0 (Rauser-Sting) nur ~2.04s. Vorher wurde hart bei `seconds`
abgeschnitten. Der Default hier (180s) ist bewusst grosszuegig ueber die
laengsten bekannten Stuecke hinaus gewaehlt, damit nichts abgeschnitten
wird; im Normalfall stoppt render_tfmx laengst vorher von selbst.
ACHTUNG Cache: der Dateiname ist rein `<name>.wav` OHNE den seconds-Wert.
Wird `seconds` also mal kleiner gewaehlt als die echte Songlaenge (z.B.
fuer einen bewusst kurzen Teaser), MUSS `force=True` gesetzt werden, sonst
liefert diese Funktion den ggf. laenger/kuerzer gerenderten alten Cache-
Treffer zurueck, egal was `seconds` diesmal sagt -- genau das hat den
urspruenglichen Bug mit ausgeloest (ein alter 8s-Debug-Render von TITEL
blieb liegen und wurde stillschweigend weiterverwendet)."""
ACHTUNG Cache: der Dateiname ist `<name>.wav` (song_index None/0) bzw.
`<name>_song<N>.wav` fuer N>0. Wird `seconds` kleiner gewaehlt als die
echte Songlaenge, MUSS `force=True` gesetzt werden, sonst liefert diese
Funktion einen ggf. laenger/kuerzer gerenderten alten Cache-Treffer
zurueck, egal was `seconds` diesmal sagt."""
os.makedirs(_CACHE_DIR, exist_ok=True)
out_path = os.path.join(_CACHE_DIR, f'{name}.wav')
suffix = f'_song{song_index}' if song_index else ''
out_path = os.path.join(_CACHE_DIR, f'{name}{suffix}.wav')
if os.path.exists(out_path) and not force:
return out_path
_ensure_binary()
@@ -72,22 +89,23 @@ def render_wav(container, name, seconds=180, force=False):
with open(sam_path, 'wb') as f:
f.write(sam_data)
result = subprocess.run(
[_BINARY, tfx_path, sam_path, out_path, str(seconds)],
capture_output=True, text=True
)
cmd = [_BINARY, tfx_path, sam_path, out_path, str(seconds)]
if song_index:
cmd.append(str(song_index))
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f'render_tfmx failed for {name}: {result.stderr}')
raise RuntimeError(f'render_tfmx failed for {name} (song_index={song_index}): {result.stderr}')
return out_path
if __name__ == '__main__':
# Kleiner CLI-Test: python3 tfmx_audio.py <DAT_PATH> <NAME> [seconds]
# Kleiner CLI-Test: python3 tfmx_audio.py <DAT_PATH> <NAME> [seconds] [song_index]
sys.path.insert(0, _HERE)
from kellogg_formats import DATContainer
dat_path = sys.argv[1]
name = sys.argv[2]
seconds = int(sys.argv[3]) if len(sys.argv) > 3 else 20
song_index = int(sys.argv[4]) if len(sys.argv) > 4 else None
c = DATContainer(dat_path)
path = render_wav(c, name, seconds, force=True)
path = render_wav(c, name, seconds, force=True, song_index=song_index)
print(f'rendered: {path}')