Levelende v1: Exit-Zone -> Zusammenfassungstafel (Original-Layout) -> Kartenfortschritt; TIME-OUT/Levelende-Tafeln bleiben bis Enter/Space stehen

This commit is contained in:
2026-07-26 12:05:32 +02:00
parent 291ba090e5
commit 892d08f316
3 changed files with 111 additions and 11 deletions
+20
View File
@@ -2089,3 +2089,23 @@ die "Absprung-Hocke" komplett entfernt (Sprung ist jetzt nur 8 hoch /
(Slot 10). Slot 17 (frontal) ist damit nirgends mehr in Gebrauch;
die sess_level-"Frontal am Levelstart"-Deutung aus Eintrag (26) war
wohl eine Fehlinterpretation der kleinen Crops.
## 26.07.2026 (35) -- Levelende v1 + Tafeln bleiben stehen
Stefans Regeln: TIME-OUT-Tafel (und Levelende-Tafel) bleiben STEHEN bis
Enter/Leertaste (_wait_panel). NEU Levelende v1:
- Exit-Zone erreicht -> Zusammenfassungstafel (SCORE/MAXIMUM/GESAMMELT/
VITAMINE/"0 X" -- nach Stefans Original-Screenshot; Werte noch 0, bis
Items gebaut sind; MAXIMUM = echte ARE-Zaehlung 151+154) -> 'completed'
-> intro.py laesst die Kartenfigur zum naechsten Punkt weiterwandern
(Punkt + Spur werden blau, Original-Regeln aus (31)/(33)).
- Exit-Zone W1L1 = SCHAETZUNG (rechtes Ende, um ARE-Objekt 194 bei
(3168,432)): exit_px=(3140,300,348,452). Echte Stelle + Aussehen des
Ausgangs von Stefan/DOSBox zu verifizieren!
- MAXIMUM-Diskrepanz: wir zaehlen 137 (151er+154er), Stefans Original-
Screen zeigte 158 -- vermutlich zaehlen ?-Box-Inhalte (71-76) und/oder
15x-Typen mit. Offen.
- ctype-Nibble-12 (44) liegt NICHT am Levelende (2 Tiles bei (91/99,17))
-- also kein Exit-Marker; Bedeutung offen. Bit 8 steckt zudem in
Standard-Boden (40=32+8) => Coco-Ast-Trigger (Bit 8) ist FALSCH und
muss neu bestimmt werden (aktuell greift Coco evtl. am Boden?? pruefen).
+9 -1
View File
@@ -990,7 +990,15 @@ def main():
elif state == 'level':
r = level_mod.run_level(clock, assets, audio_paths,
map_name=pending_map)
if r == 'map':
if r == 'completed':
# Level geschafft: Kartenfigur laeuft zum
# naechsten Punkt weiter (Punkt/Spur werden blau)
wm = map_state.get('wm')
if wm is not None and wm.current < len(wm.segments) - 1:
wm.current += 1
play_title_music()
state = 'map'
elif r == 'map':
play_title_music() # Karte/Menue laufen mit Titelmusik
state = 'map'
else:
+81 -9
View File
@@ -144,6 +144,10 @@ LEVEL_INFO = {
chars=['TONY', 'SMACKS'], # Level 1: nur die beiden (Stefan)
water_y=42 * TILE, # Wasseroberflaeche unter der Bruecke
door_px=(12 * TILE, 37 * TILE, 2 * TILE, 3 * TILE), # Haus-Tuer (x,y,w,h)
# Levelende-Zone (v1-SCHAETZUNG: rechtes Levelende um das letzte
# ARE-Objekt Typ 194 bei (3168,432) -- echte Stelle noch per
# Stefan/DOSBox zu verifizieren)
exit_px=(3140, 300, 348, 452),
),
}
WATER_TINT = (10, 20, 120, 110) # dunkelblaues Overlay
@@ -741,6 +745,65 @@ def run_character_select(clock, assets, available, current):
clock.tick(30)
def _wait_panel(clock, panel):
'''Tafel anzeigen, bis Enter/Leertaste gedrueckt wird (Stefans Regel
fuer TIME-OUT- und Levelende-Tafel). Rueckgabe: 'ok' | 'quit'.'''
pygame.event.clear()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return 'quit'
if event.type == pygame.KEYDOWN and event.key in (
pygame.K_RETURN, pygame.K_SPACE):
return 'ok'
intro._blit_centered(intro.build_letterboxed(panel))
pygame.display.flip()
clock.tick(30)
def _parse_are_objects(are_bytes, map_w_tiles, map_h_tiles):
'''Objektliste (x_px, y_px, typ) aus einer .ARE (Format: NOTES (24)).'''
import struct
d = are_bytes
sec_w = (map_w_tiles * 16 + 63) // 64
sec_h = (map_h_tiles * 16 + 63) // 64
objs = []
for sy in range(sec_h):
for sx in range(sec_w):
off = struct.unpack('>H', d[352 + (sy * sec_w + sx) * 2:
354 + (sy * sec_w + sx) * 2])[0]
if off == 0xffff:
continue
q = 352 + off
while q < len(d) - 1:
v = struct.unpack('>H', d[q:q + 2])[0]
if v == 0xffff:
if q == 352 + off:
q += 2
continue
break
typ, dx, dy = struct.unpack('>3H', d[q:q + 6])
objs.append((sx * 64 + dx, sy * 64 + dy, typ))
q += 6
return objs
def _summary_panel(assets, collected, maximum, vitamins, score):
'''Levelende-Tafel nach Stefans Original-Screenshot: SCORE, MAXIMUM/
GESAMMELT (Cerealien), VITAMINE, Bonusleben-Zeile.'''
from .menu import MenuAssets
m = MenuAssets(assets)
panel = m.menu_bg.copy()
def line(text, y):
surf = m.font_big.render(text)
panel.blit(surf, ((320 - surf.get_width()) // 2, y))
line(f'SCORE: {score}', 48)
line(f'MAXIMUM {maximum} GESAMMELT {collected}', 66)
line(f'VITAMINE {vitamins}', 84)
line('0 X', 116)
return panel
def run_level(clock, assets, audio_paths, map_name='W1L1'):
'''Level spielen. ESC -> 'map', Fenster-X -> 'quit'.'''
level = Level(assets, map_name)
@@ -808,6 +871,23 @@ def run_level(clock, assets, audio_paths, map_name='W1L1'):
vx=55.0 * player.facing, t=0.0))
keys = pygame.key.get_pressed()
# Levelende erreicht? -> Zusammenfassungstafel, dann Karte (Punkt
# wird blau, Figur wandert weiter -- intro.py behandelt 'completed')
exit_zone = info.get('exit_px')
if (dying is None and exit_zone
and pygame.Rect(exit_zone).collidepoint(player.x, player.y - 8)):
w, h = level.width, level.height
try:
objs = _parse_are_objects(
assets.container.entries[map_name + '.ARE'], w, h)
maximum = sum(1 for _, _, t in objs if t in (151, 154))
except Exception:
maximum = 0
panel = _summary_panel(assets, collected=0, maximum=maximum,
vitamins=0, score=0)
if _wait_panel(clock, panel) == 'quit':
return 'quit'
return 'completed'
time_left = info['time'] - (time.monotonic() - start)
# Zeit abgelaufen -> TIME-OUT-Tafel (Original-Screen, Stefans
# Screenshot: Menue-Rahmen + "TIME OUT"), dann Leben -1 und Respawn
@@ -817,16 +897,8 @@ def run_level(clock, assets, audio_paths, map_name='W1L1'):
panel = m.menu_bg.copy()
txt = m.font_big.render('TIME OUT')
panel.blit(txt, ((320 - txt.get_width()) // 2, 72))
end = time.monotonic() + 2.5
while time.monotonic() < end:
for event in pygame.event.get():
if event.type == pygame.QUIT:
if _wait_panel(clock, panel) == 'quit':
return 'quit'
if event.type == pygame.KEYDOWN:
end = 0
intro._blit_centered(intro.build_letterboxed(panel))
pygame.display.flip()
clock.tick(30)
lives -= 1
if lives <= 0:
return 'map'