Rauser-Fade-Bug (Alpha-Cutout), Musik-Laenge-Bug (real_song_end-Erkennung) und Factor5-Melodie gefixt
This commit is contained in:
+129
-25
@@ -11,15 +11,27 @@ Serie vermessen, siehe NOTES.md Abschnitt "Boot-Sequenz vermessen". Kurzfassung:
|
||||
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,
|
||||
genau ein "Bling"-Jingle, 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").
|
||||
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.
|
||||
2. kurze schwarze Pause
|
||||
3. Factor5-Studio-Logo (FACTOR5.PCC, STATISCHES Bild -- keine Animation,
|
||||
das "Geister"-Doppellogo ist Teil des Assets selbst), ~6s
|
||||
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.
|
||||
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-
|
||||
@@ -58,6 +70,7 @@ sonst loopt die Sequenz weiter wie im Original-Titelbildschirm)
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from kellogg_formats import DATContainer, load_pcc
|
||||
@@ -109,6 +122,67 @@ def indices_to_surface(width, height, indices, palette):
|
||||
return surf.convert()
|
||||
|
||||
|
||||
def make_border_bg_transparent(surface, threshold=235):
|
||||
'''Fix fuer Stefans Meldung (22.07.2026 abends): "der Rauser-Text fadet
|
||||
nicht mit aus, bleibt hart umrandet". Ursache: die Rauser-PCC-Assets haben
|
||||
KEINEN Alphakanal -- ihr papierweisser Hintergrund sitzt als hartes
|
||||
Rechteck um die eigentliche Box-Grafik (RAUSER-Schriftzug/ADVERTAINMENT/
|
||||
"!"-Box). Auf weissem Bildhintergrund faellt das nicht auf, aber sobald
|
||||
die Szene zu Grau ueberblendet, bleibt dieses Rechteck stur weiss stehen
|
||||
statt mit auszufaden. Ein v1-Versuch mit set_colorkey() wurde verworfen,
|
||||
weil er auch die WEISSEN BUCHSTABEN in RAUSER1 durchsichtig machte.
|
||||
|
||||
Fix: Flood-Fill vom Bildrand aus ueber alle "papierweissen" Pixel (alle
|
||||
Kanaele >= threshold) -- NUR die vom Rand aus zusammenhaengende Flaeche
|
||||
wird transparent gemacht. Isolierte weisse Pixel MITTEN im Motiv (z.B.
|
||||
weisse Buchstaben im RAUSER-Schriftzug) beruehren den Bildrand nicht und
|
||||
bleiben deshalb unangetastet. Ergebnis: eine Surface mit echtem
|
||||
Alphakanal, die beim Blit auf einen sich veraendernden Hintergrund
|
||||
(weiss->grau) sauber durchscheint, statt eine harte Kante zu zeigen.'''
|
||||
w, h = surface.get_size()
|
||||
surf = surface.convert_alpha()
|
||||
px = pygame.PixelArray(surf)
|
||||
|
||||
def is_bg(x, y):
|
||||
color = surf.unmap_rgb(px[x, y])
|
||||
return color.r >= threshold and color.g >= threshold and color.b >= threshold
|
||||
|
||||
seen = bytearray(w * h)
|
||||
dq = deque()
|
||||
|
||||
def consider(x, y):
|
||||
if not seen[y * w + x] and is_bg(x, y):
|
||||
seen[y * w + x] = 1
|
||||
dq.append((x, y))
|
||||
|
||||
for x in range(w):
|
||||
consider(x, 0)
|
||||
consider(x, h - 1)
|
||||
for y in range(h):
|
||||
consider(0, y)
|
||||
consider(w - 1, y)
|
||||
|
||||
while dq:
|
||||
cx, cy = dq.popleft()
|
||||
if cx + 1 < w:
|
||||
consider(cx + 1, cy)
|
||||
if cx - 1 >= 0:
|
||||
consider(cx - 1, cy)
|
||||
if cy + 1 < h:
|
||||
consider(cx, cy + 1)
|
||||
if cy - 1 >= 0:
|
||||
consider(cx, cy - 1)
|
||||
del px
|
||||
|
||||
alpha = pygame.surfarray.pixels_alpha(surf)
|
||||
for x in range(w):
|
||||
for y in range(h):
|
||||
if seen[y * w + x]:
|
||||
alpha[x, y] = 0
|
||||
del alpha
|
||||
return surf
|
||||
|
||||
|
||||
class Assets:
|
||||
def __init__(self, dat_path):
|
||||
self.container = DATContainer(dat_path)
|
||||
@@ -121,10 +195,16 @@ class Assets:
|
||||
w, h, indices, palette = self.pcc(name)
|
||||
return indices_to_surface(w, h, indices, palette)
|
||||
|
||||
def pcc_surface_scaled(self, name, scale=ASSET_DISPLAY_SCALE):
|
||||
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).'''
|
||||
Pixelvermessung als gleichmaessige 0.5x-Verkleinerung bestaetigt).
|
||||
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
|
||||
Kaesten stehen bleiben.'''
|
||||
surf = self.pcc_surface(name)
|
||||
if cut_border_bg:
|
||||
surf = make_border_bg_transparent(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)
|
||||
@@ -213,19 +293,15 @@ 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).'''
|
||||
r1 = assets.pcc_surface_scaled('RAUSER1') # "RAUSER"-Box
|
||||
r2 = assets.pcc_surface_scaled('RAUSER2') # "ADVERTAINMENT"
|
||||
r3 = assets.pcc_surface_scaled('RAUSER3') # "!" auf blauer Box
|
||||
# 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
|
||||
# blieb hier ein harter weisser Kasten stehen, der beim Fade zu Grau nicht
|
||||
# mitgefadet ist (Stefans Meldung "Text bleibt hart umrandet stehen").
|
||||
r1 = assets.pcc_surface_scaled('RAUSER1', cut_border_bg=True) # "RAUSER"-Box
|
||||
r2 = assets.pcc_surface_scaled('RAUSER2', cut_border_bg=True) # "ADVERTAINMENT"
|
||||
r3 = assets.pcc_surface_scaled('RAUSER3', cut_border_bg=True) # "!" auf blauer Box
|
||||
|
||||
# BEKANNTE UNSCHOENHEIT (bewusst NICHT "gefixt"): die PCC-Assets haben
|
||||
# weissen Hintergrund in ihrer eigenen Bounding-Box (kein Alpha-Kanal).
|
||||
# Auf weissem Grund faellt das nicht auf, nach dem Fade zu Grau sieht man
|
||||
# einen schwachen weissen Kasten um RAUSER2/RAUSER3. Ein Versuch, Weiss
|
||||
# per set_colorkey() durchsichtig zu machen, wurde wieder verworfen --
|
||||
# dadurch wurden auch die WEISSEN BUCHSTABEN in RAUSER1 durchsichtig
|
||||
# (schlimmer als der Kasten). Sauberer Fix braucht eine echte
|
||||
# Alpha-Maske pro Asset statt eines einzelnen Colorkeys; low priority,
|
||||
# siehe "Noch offen" unten.
|
||||
total_w = r1.get_width() + r3.get_width()
|
||||
total_h = r1.get_height() + r2.get_height()
|
||||
x0 = (320 - total_w) // 2
|
||||
@@ -267,7 +343,12 @@ def run_rauser(screen, clock, assets):
|
||||
sting = None
|
||||
try:
|
||||
if pygame.mixer.get_init():
|
||||
sting_wav = tfmx_audio.render_wav(assets.container, 'TITEL2', seconds=1.2)
|
||||
# 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')
|
||||
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)
|
||||
@@ -417,12 +498,30 @@ def run_once(screen, clock, assets):
|
||||
if quit_requested:
|
||||
return True
|
||||
|
||||
# 3) Factor5 (statisch)
|
||||
# 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.
|
||||
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_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)
|
||||
|
||||
def show_factor5(_t):
|
||||
blit_scaled_centered(screen, factor5, WINDOW_W, WINDOW_H)
|
||||
quit_requested = wait_or_skip(clock, 3.0, show_factor5)
|
||||
quit_requested = wait_or_skip(clock, factor5_duration, show_factor5)
|
||||
if factor5_sound is not None:
|
||||
factor5_sound.stop()
|
||||
if quit_requested:
|
||||
return True
|
||||
|
||||
@@ -436,9 +535,14 @@ def run_once(screen, clock, assets):
|
||||
# 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', seconds=60)
|
||||
titel_wav = tfmx_audio.render_wav(assets.container, 'TITEL')
|
||||
pygame.mixer.music.load(titel_wav)
|
||||
pygame.mixer.music.play(-1)
|
||||
except Exception as exc:
|
||||
|
||||
Reference in New Issue
Block a user