diff --git a/NOTES.md b/NOTES.md index 4d08cf1..f798eef 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1739,3 +1739,41 @@ Binary-KOORDINATEN stimmen, aber der BLIT-ANKER des Originals (topleft vs zentriert) steht nicht im Binary -- wir hatten zentriert geraten. Fix: empirische Offsets MAP_DX=5/MAP_DY=4 in worldmap._tf() (dort feinjustieren, falls noch nicht exakt). + +## 26.07.2026 (16) -- Karte PIXELEXAKT: Template-Matching statt Schaetzung + +Stefans Nachmessung: immer noch daneben ("noch ein bisschen nach rechts +und weiter runter"). Diesmal richtig gemessen: KARTE-Sprites per +Template-Matching (mit Alpha-Maske) in den DOSBox-Referenzframes gesucht. +Ergebnis eindeutig (err ~10 = VGA-DAC-Rauschen, naechster Kandidat >200): + +- KARTE0 (10x7) steht DAUERHAFT, Topleft (58,162) -- in BEIDEN Blinkphasen! +- KARTE4-Kopf (14x15) blinkt DARUEBER an/aus, Topleft (53,146) (steht auf + dem Punkt). Frueherer Befund "Punkt und Kopf wechseln sich ab" war + falsch (Diff-Region war die VEREINIGUNG beider Sprites). +- KARTE3 (kleine Punkte): am Spielstart NIRGENDS auf der Karte -- nicht + freigeschaltete Level sind unsichtbar (bestaetigt die Netz-Screenshots: + dort ist das Spiel durchgespielt). + +Daraus die exakten Anker (Binary-Label (x,y)): Basis = (x, 218-y); +KARTE0-Topleft = Basis+(5,0); Kopf-Topleft = Basis+(0,-16); KARTE3 +zentriert auf dem grossen Punkt = Basis+(7,2) (einzige verbleibende +Annahme -- kleine Punkte gibt es in keiner DOS-Referenzaufnahme). + +WICHTIGER NEBENFUND -- Segment/Namens-Zuordnung war um 1 verschoben: +Das Start-Label (53,56) gehoert zu SEGMENT 0, und 21 Segmente = exakt +die 21 anwaehlbaren Level W1L1..W3L7 (3 Welten x 7). W1L0 (22. Name, +MAP existiert) hat KEINEN Kartenpunkt (Bonus-/Geheimlevel?). Also +segments[i] = names[i+1]; der "fehlende W3L7-Randfall" aus Eintrag (13) +war keiner. Cursorstart = Segment 0 = W1L1. + +Disassembly-Check (Stefans Frage "bekommst du das nicht aus dem +Disassembler?"): einziges `mov ax,218` im Binary (Offset 0xf084) ist +eine HUD-ZIFFERN-Routine (X-Positionen 186/194/202, Spriteindex = +Ziffer+500 -- notiert fuer die Punkteanzeige!). Der Karten-Anker steht +nicht als Literal im Code -> Pixelmessung war der richtige Weg. + +Verifikation: eigener Karten-Render vs. DOSBox-Frame, mittlerer +Pixelfehler 5.0 (identisch mit dem Ganzbild-Grundrauschen der +VGA-DAC-Quantisierung) in beiden Blinkphasen => pixelidentisch. +--once exit 0. diff --git a/game/worldmap.py b/game/worldmap.py index b3efd3b..f9c2504 100644 --- a/game/worldmap.py +++ b/game/worldmap.py @@ -28,21 +28,22 @@ from . import intro HEAD_SPRITE = {'TONY': 'KARTE4', 'SMACKS': 'KARTE5', 'TOUCAN': 'KARTE6', 'COCO': 'KARTE7'} STEP_SECONDS = 0.18 # Lauftempo der Kartenfigur pro Wegpunkt BLINK_SECONDS = 0.5 # Blinkrhythmus der aktiven Position -# Die Binary-Koordinaten sind y-GEFLIPPT (DOSBox-Nachmessung 25.07.2026: -# Level-1-Punkt real bei (59,153), Binary-Label (53,56) -> screen_y = 209-y; -# damit liegen die fruehen Level im Wald unten und W2/W3 in Bergen/Schloss -# oben -- deckungsgleich mit Stefans Original-Screenshots). -MAP_Y_FLIP = 209 -# Blit-Anker-Korrektur (Stefans Abgleich mit Original-Screenshots: "alle -# Punkte ein Stueck nach rechts und unten"): die Binary-Koordinaten sind -# korrekt, aber das Original ankert die Sprites nicht zentriert -- der -# Versatz ist im Binary nicht ablesbar, daher empirisch. Feinjustage hier. -MAP_DX = 5 -MAP_DY = 4 +# Die Binary-Koordinaten sind y-GEFLIPPT. Pixelexakte DOSBox-Nachmessung +# (Template-Matching der KARTE-Sprites, 26.07.2026): Binary-Label von W1L1 +# ist (53,56); im Original steht KARTE0 (10x7) mit Topleft bei (58,162) und +# der Mini-Kopf KARTE4 (14x15) blinkt DARUEBER bei Topleft (53,146) -- +# der Punkt bleibt also dauerhaft stehen, nur der Kopf geht an/aus. +# Daraus: Basis = (x_bin, 218 - y_bin), Punkt-Topleft = Basis+(5,0), +# Kopf-Topleft = Basis+(0,-16). Die Anker stehen nicht als Literal im +# Binary (Code rechnet sie indirekt), daher per Messung fixiert. +MAP_Y_FLIP = 218 +DOT_OFF = (5, 0) # KARTE0/KARTE2 (10x7), Topleft relativ zur Basis +HEAD_OFF = (0, -16) # KARTE4-7 (14x15), steht auf dem Punkt +SMALL_OFF = (7, 2) # KARTE3 (6x4), zentriert auf dem grossen Punkt def _tf(p): - return (p[0] + MAP_DX, MAP_Y_FLIP - p[1] + MAP_DY) + return (p[0], MAP_Y_FLIP - p[1]) def parse_worldmap(exe_path): @@ -123,8 +124,11 @@ class WorldMap: except Exception as exc: print(f'[worldmap] Binary-Pfaddaten nicht lesbar ({exc}) -- ' f'Fallback nur Level 1', file=sys.stderr) - self.segments = [dict(waypoints=[(113, 35)], stand=(113, 35), label=(108, 51))] - self.names = ['W1L1'] + self.segments = [dict(waypoints=[(53, 56)], stand=(53, 56), label=(53, 56))] + self.names = ['W1L0', 'W1L1'] + # 21 Segmente fuer 21 anwaehlbare Level (W1L1..W3L7, 3 Welten x 7); + # W1L0 (Name Nr. 0) hat KEINEN Kartenpunkt -> segments[i] = names[i+1]. + self.level_names = self.names[1:] # Positionen ins Screen-System transformieren (y-Flip) for seg in self.segments: seg['waypoints'] = [_tf(p) for p in seg['waypoints']] @@ -137,7 +141,7 @@ class WorldMap: self.dot_small = intro.make_border_bg_transparent(assets.pcc_surface('KARTE3')) self.heads = {c: intro.make_border_bg_transparent(assets.pcc_surface(n)) for c, n in HEAD_SPRITE.items()} - self.current = 1 if len(self.segments) > 1 else 0 # Start: Level 1 (Index 0 = W1L0) + self.current = 0 # Segment 0 = W1L1 (DOSBox-Messung: Startpunkt (53,56)) def draw(self, canvas, char, t, walk_pos=None): canvas.blit(self.karte, (0, 0)) @@ -146,23 +150,19 @@ class WorldMap: for i, seg in enumerate(self.segments): if seg['label'] is None: continue - lx, ly = seg['label'] + bx, by = seg['label'] if i == self.current and walk_pos is None: - # Original-Verhalten (Netz-Referenzbilder + DOSBox-Diff): - # an der aktuellen Position wechseln PUNKT und MINI-KOPF + # Original-Verhalten (DOSBox-Messung): der grosse Punkt + # steht dauerhaft, der Mini-Kopf blinkt DARUEBER an/aus + canvas.blit(self.dot_big_on, (bx + DOT_OFF[0], by + DOT_OFF[1])) if head_phase: - canvas.blit(head, (lx - head.get_width() // 2, ly - head.get_height() // 2)) - else: - canvas.blit(self.dot_big_on, - (lx - self.dot_big_on.get_width() // 2, - ly - self.dot_big_on.get_height() // 2)) + canvas.blit(head, (bx + HEAD_OFF[0], by + HEAD_OFF[1])) else: - dot = self.dot_small - canvas.blit(dot, (lx - dot.get_width() // 2, ly - dot.get_height() // 2)) + canvas.blit(self.dot_small, (bx + SMALL_OFF[0], by + SMALL_OFF[1])) # waehrend des Laufens: Kopf entlang des Pfads if walk_pos is not None: px, py = walk_pos - canvas.blit(head, (px - head.get_width() // 2, py - head.get_height())) + canvas.blit(head, (px + HEAD_OFF[0], py + HEAD_OFF[1])) def run_worldmap(clock, assets, menu_assets, char='TONY', state={}): @@ -198,7 +198,7 @@ def run_worldmap(clock, assets, menu_assets, char='TONY', state={}): pts = list(reversed(wm.segments[wm.current]['waypoints'])) walking = {'pts': pts, 'i': 0.0, 'target': wm.current - 1} elif event.key in (pygame.K_SPACE, pygame.K_RETURN): - return ('level', wm.names[wm.current]) + return ('level', wm.level_names[wm.current]) dt = clock.tick(60) / 1000.0 walk_pos = None