# ============================================================================
#  SCHATTEN DER ZERBROCHENEN KRONE
#  Ein 2D-Soulslike inspiriert von Elden Ring, gebaut mit Pyxel 2.9
#
#  Steuerung:
#    Pfeiltasten / WASD : Bewegen
#    J / SPACE          : Angriff
#    K / SHIFT          : Ausweichen (Dash, kurzzeitig unverwundbar)
#    L / CTRL           : Blocken (verbraucht Ausdauer)
#    H                  : Heiltrank trinken
#    E                  : Interagieren (Speicherpunkt / Händler / Tür)
#    I                  : Inventar / Stats
#    ESC                : Beenden
#
#  Ablauf:
#    Verfallenes Dorf -> Nebelwald -> Katakomben -> Schwarze Festung
# ============================================================================

import pyxel
import math
import random
import json
import os

# ============================================================================
#  GRUNDKONSTANTEN
# ============================================================================

SCREEN_W = 256
SCREEN_H = 192
FPS = 60
TILE = 16                 # Tile-Größe in Pixeln
VIEW_TILES_X = SCREEN_W // TILE
VIEW_TILES_Y = SCREEN_H // TILE

# Spielzustände
STATE_MENU       = 0
STATE_PLAYING    = 1
STATE_DIALOG     = 2
STATE_INVENTORY  = 3
STATE_GAME_OVER  = 4
STATE_VICTORY    = 5
STATE_LEVEL_UP   = 6
STATE_MERCHANT   = 7

# Facings
FACE_DOWN  = 0
FACE_LEFT  = 1
FACE_RIGHT = 2
FACE_UP    = 3

# Gebiets-IDs
AREA_VILLAGE  = 0
AREA_FOREST   = 1
AREA_CATACOMB = 2
AREA_FORTRESS = 3

AREA_NAMES = {
    AREA_VILLAGE:  "Verfallenes Dorf",
    AREA_FOREST:   "Nebelhöhle",
    AREA_CATACOMB: "Knochengarten",
    AREA_FORTRESS: "Schwarze Festung",
}

# ============================================================================
#  SPRITE-DATEN
#  Wir zeichnen Sprites als 16x16 Pixel-Arrays direkt im Code.
#  '.' = transparent, andere Zeichen = Pyxel-Farbindex (0-15, 'g'=16 = Spezial)
#  Pyxel-Palette 0..15:
#    0=schwarz, 1=dunkelblau, 2=dunkelrot, 3=dunkelgrün, 4=braun, 5=dunkelgrau,
#    6=hellgrau, 7=weiß, 8=rot, 9=orange, 10=gelb, 11=hellgrün, 12=hellblau,
#    13=violett, 14=pink, 15=beige
# ============================================================================

COLOR_MAP = {
    '.': None, '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5,
    '6': 6, '7': 7, '8': 8, '9': 9, 'a': 10, 'b': 11, 'c': 12,
    'd': 13, 'e': 14, 'f': 15,
}

# ----- HERO (schwarzer Ritter) - 4 Richtungen x 2 Frames --------------------
# Ganz in schwarz mit dunkelgrauer Rüstung (5), silbernen Highlights (6/7),
# Kettenhemd-Mantel (0/1), rotes Auge durch den Visier-Schlitz (2)
# Farb-Legende: 0=schwarz, 1=dunkelblau (Schatten), 5=grau, 6=hellgrau,
#               7=weiß (Highlight), 2=rot (Augenschlitz), 4=Griff-Braun
#
# HERO_DOWN: Held von vorne gesehen, Schwert vor dem Körper in beiden Händen
HERO_DOWN_1 = [
    ".....000000.....",
    "....05555550....",
    "...0566666650...",
    "...0565555650...",
    "...0567227650...",   # Visierschlitz mit rotem Auge
    "...0565555650...",
    "...0566666650...",
    "..05011111150...",   # Kettenhemd-Hals
    "..051000000150..",
    "..05100560010...",   # Brustplatte mit Schwertgriff
    "..05100770010...",   # Schwertklinge
    "..01100770011...",
    "..01100770011...",
    "..010.0770.010..",   # Kettenhemd-Mantel faellt
    "..010.0000.010..",
    "...0..0000..0...",
]
HERO_DOWN_2 = [   # leichtes Schwanken / Atmen
    ".....000000.....",
    "....05555550....",
    "...0566666650...",
    "...0565555650...",
    "...0567227650...",
    "...0565555650...",
    "...0566666650...",
    "..05011111150...",
    "..051000000150..",
    "..05100560010...",
    "..05100770010...",
    "..01100770011...",
    "...1100770011...",
    "...10.0770.010..",
    "...10.0000.010..",
    "....0.0000..0...",
]
# HERO_UP: Ruecken, kein Visier sichtbar, Umhang-Sicht
HERO_UP_1 = [
    ".....000000.....",
    "....05555550....",
    "...0566666650...",
    "...0555555550...",   # Ruecken des Helms (glatt)
    "...0555555550...",
    "...0566666650...",
    "...0555555550...",
    "..05011111150...",
    "..051000000150..",
    "..01100000011...",   # Umhang hinten
    "..01100000011...",
    "..01100000011...",
    "..01100000011...",
    "..010.0000.010..",
    "..010.0000.010..",
    "...0..0000..0...",
]
HERO_UP_2 = [
    ".....000000.....",
    "....05555550....",
    "...0566666650...",
    "...0555555550...",
    "...0555555550...",
    "...0566666650...",
    "...0555555550...",
    "..05011111150...",
    "..051000000150..",
    "..01100000011...",
    "..01100000011...",
    "..01100000011...",
    "...1100000011...",
    "...10.0000.010..",
    "...10.0000.010..",
    "....0.0000..0...",
]
# HERO_RIGHT: Ritter laeuft nach rechts, Schwert vor dem Koerper
HERO_RIGHT_1 = [
    "....000000......",
    "...05555550.....",
    "..056666650.....",
    "..056555650.....",
    "..056722650.....",   # Visier nach rechts, Auge sichtbar
    "..056555650.....",
    "..056666650.....",
    "...05111150.....",
    "...0510067700...",   # Griff + Klinge horizontal/schraeg
    "...0510067700...",
    "...0110067700...",
    "...01100000.....",
    "...01100........",
    "...010.0........",
    "...010..0.......",
    "....0...........",
]
HERO_RIGHT_2 = [
    "....000000......",
    "...05555550.....",
    "..056666650.....",
    "..056555650.....",
    "..056722650.....",
    "..056555650.....",
    "..056666650.....",
    "...05111150.....",
    "...0510067700...",
    "...0110067700...",
    "...0110067700...",
    "...0100000......",
    "...0100.........",
    "...010..0.......",
    "...0...0........",
    "...0............",
]
# HERO_LEFT: gespiegelt
HERO_LEFT_1 = [
    "......000000....",
    ".....05555550...",
    ".....056666650..",
    ".....056555650..",
    ".....056227650..",   # Visier nach links
    ".....056555650..",
    ".....056666650..",
    ".....05111150...",
    "...0077005010...",
    "...0077005010...",
    "...0077006110...",
    ".....00000110...",
    "........00110...",
    "........0.010...",
    ".......0..010...",
    "...........0....",
]
HERO_LEFT_2 = [
    "......000000....",
    ".....05555550...",
    ".....056666650..",
    ".....056555650..",
    ".....056227650..",
    ".....056555650..",
    ".....056666650..",
    ".....05111150...",
    "...0077005010...",
    "...0077006110...",
    "...0077006110...",
    "....00000011....",
    ".........0010...",
    ".......0..010...",
    "........0...0...",
    "............0...",
]

# ----- GEGNER ---------------------------------------------------------------
# Verfluchter Soldat - grünlich-faul, mit Speer
ENEMY_SOLDIER = [
    "....33333333....",
    "...322222223....",
    "...322222223...0",
    "...3222222230..0",
    "...3225555230..0",
    "...3225335230..0",
    "...3223333230..0",
    "..03a4444443a..0",
    "..0a344444443a.0",
    "..0a3a4a4a4a3a.0",
    "...a344444443a..",
    "...a3a4a4a4a3...",
    "...044.....44...",
    "...044.....44...",
    "...055.....55...",
    "...055.....55...",
]
# Skelett - weiße Knochen
ENEMY_SKELETON = [
    ".....07770......",
    "....077777......",
    "....070707......",
    "....077777......",
    ".....0707.......",
    "....0077770.....",
    "...07077770.....",
    "...07077770.....",
    "....0077770.....",
    "....0077770.....",
    ".....06660......",
    ".....0707.......",
    ".....077........",
    "....077.77......",
    "....07...7......",
    "....00...0......",
]
# Waldbestie - dunkelgrün mit Zähnen
ENEMY_BEAST = [
    "................",
    "...3.......3....",
    "..333.....333...",
    ".33333...33333..",
    ".3333333333333..",
    ".333778778333...",
    ".3337070707333..",
    ".33377777777.3..",
    "..337777777733..",
    "...333777333....",
    "....33333333....",
    "...33.3333.33...",
    "...33.3333.33...",
    "...33..33..33...",
    "...44..33..44...",
    "...44..44..44...",
]
# Schattenmagier - violett mit Kapuze
ENEMY_MAGE = [
    ".....dddd.......",
    "....dddddd......",
    "...dddddddd.....",
    "...dd0000dd.....",
    "...d000000d.....",
    "...d0e00e0d.....",
    "...d000000d.....",
    "..ddd0000ddd....",
    "..ddddddddd.....",
    ".dddddddddd.....",
    ".dd...dd...d....",
    "..d...dd....d...",
    "..dd..dd....d...",
    "...d..dd...dd...",
    "...ddddddddd....",
    "....dd...dd.....",
]
# Ritter der Ruinen (Elite) - großer Ritter in grauer Rüstung
ENEMY_KNIGHT = [
    "...55555555.....",
    "..5666666665....",
    "..5666006665....",
    "..5660990665...7",
    "..5660990665..77",
    "..5666666665.77.",
    "..5668888665.7..",
    "..5668888665....",
    ".5556666665.....",
    ".566666666655...",
    ".5662266666655..",
    ".5662266666655..",
    "..55222225500...",
    "..55.....55.....",
    "..55.....55.....",
    "..66.....66.....",
]
# Nebelwolf (Elite) - groß, grau-blau
ENEMY_WOLF = [
    "................",
    "...5.......5....",
    "..5c5.....5c5...",
    ".5ccc5...5ccc5..",
    ".5ccccc5ccccc5..",
    ".5ccc7ccc7ccc5..",
    ".5cc707c707cc...",
    ".5cccccccccc.5..",
    "..5ccccccccc55..",
    "...555ccc555....",
    "....555555......",
    "...55.5555.55...",
    "...55.5555.55...",
    "...55..55..55...",
    "...55..55..55...",
    "...66..66..66...",
]
# Katakombenwächter (Elite) - riesiger Skelettkrieger
ENEMY_GUARDIAN = [
    "...07777770.....",
    "..0777777770....",
    "..0708888070....",
    "..0700000070...7",
    "..0770770770..77",
    "..0777777770.77.",
    "..0777777770.7..",
    ".00777777770....",
    ".055556666550...",
    ".5555555555555..",
    ".555266666625...",
    ".552222222225...",
    "..55.....55.....",
    "..55.....55.....",
    "..55.....55.....",
    "..66.....66.....",
]
# BOSS 1 - Verfluchter Wächter (Katakomben)
BOSS_CURSED = [
    "...dd777777dd...",
    "..dd77777777dd..",
    "..d7000000007d..",
    "..d7022002207d..",
    "..d7022002207d..",
    "..d70000000007.d",
    "..d70888888807.d",
    "..d77d7777d77d..",
    ".ddddddddddddd..",
    ".d2dddddddddd2d.",
    ".d22dddddddd22d.",
    ".d222dd22dd222d.",
    "..d2dd2222dd2d..",
    "..00.......00...",
    "..00.......00...",
    "..55.......55...",
]
# BOSS 2 - Nebelkönig (Nebelwald) - nebliger Geist mit Krone
BOSS_FOGKING = [
    "...8a88a88a8....",
    "..8aaaaaaaaa8...",
    ".cccccccccccc...",
    ".ccc000000ccc...",
    ".ccc022220ccc...",
    ".ccc022220ccc...",
    ".ccc000000ccc...",
    "..ccccccccccc...",
    ".cccccccccccc.c.",
    "cccc.cccc.ccccc.",
    "cccc.cccc.cccc..",
    ".ccc.cccc.cccc..",
    "..cc.cccc.ccc...",
    "...c.cccc.cc....",
    "....ccccccc.....",
    "....cc...cc.....",
]
# BOSS 3 - König ohne Krone (ENDBOSS)
BOSS_KING = [
    "...aa8aa8aa.....",
    "..aaa8aa8aaa....",
    "..aaa88888aa....",
    ".dddddddddddd...",
    ".ddd000000ddd...",
    ".ddd088880ddd...",
    ".ddd082280ddd...",
    ".ddd088880ddd...",
    ".ddd000000ddd..d",
    ".ddddddddddddd.d",
    ".d222dddddd222dd",
    ".d22ddddddddd22d",
    ".dd2222dd2222dd.",
    "..dd222222222d..",
    "..00.......00...",
    "..55.......55...",
]

# ----- ITEMS ----------------------------------------------------------------
ITEM_POTION = [
    "....00000000....",
    "....088888880...",
    "....088000880...",
    "....088080880...",
    "....088080880...",
    "....088888880...",
    "....022222220...",
    "....02eeeeee20..",
    "....02eeeeee20..",
    "....022eeee220..",
    "....022eeee220..",
    "....0222222220..",
    "....022222220...",
    ".....00000000...",
    "................",
    "................",
]
ITEM_SWORD = [
    "................",
    "...........0....",
    "..........077...",
    ".........0776...",
    "........07760...",
    ".......07760....",
    "......07760.....",
    ".....07760......",
    "....07760.......",
    "...07760........",
    "..04460.........",
    ".044400.........",
    "..400...........",
    "..40............",
    "................",
    "................",
]
ITEM_ARMOR = [
    "................",
    "....555555......",
    "...55555555.....",
    "..5566555555....",
    "..5556665555....",
    ".55566666666....",
    ".55666666665....",
    ".55666996665....",
    ".55666996665....",
    ".55666666665....",
    ".55566666655....",
    ".5556666665.....",
    "..555566655.....",
    "...5555555......",
    "....55555.......",
    "................",
]
ITEM_RING = [
    "................",
    "................",
    ".....0aaa0......",
    "....0a000a0.....",
    "...0a0aea0a0....",
    "...0a0eee0a0....",
    "...0a0aea0a0....",
    "....0a000a0.....",
    ".....0aaa0......",
    "................",
    "................",
    "................",
    "................",
    "................",
    "................",
    "................",
]
ITEM_CROWN = [
    "................",
    ".a....a.a....a..",
    ".ae..ae.ea..ea..",
    ".aae.aeea.eaa...",
    ".aaaeaaaaeaaa...",
    ".aaaaaaaaaaaa...",
    ".aeeeeeeeeeea...",
    ".aaaaaaaaaaaa...",
    "................",
    "................",
    "................",
    "................",
    "................",
    "................",
    "................",
    "................",
]

# ----- TILES (Umgebung) -----------------------------------------------------
# Gras
TILE_GRASS = [
    "3333333333333333",
    "3b333333333b3333",
    "333333b333333333",
    "3333333333333333",
    "3333b33333333b33",
    "3333333333333333",
    "33333333b3333333",
    "b333333333333333",
    "3333333b33333333",
    "3333333333b33333",
    "333b333333333333",
    "3333333333333333",
    "33333b33333333b3",
    "3333333333333333",
    "3b33333333333333",
    "3333333333333333",
]
# Stein / Pfad
TILE_STONE = [
    "5555555555555555",
    "5666666566666665",
    "5666666566666665",
    "5666666566666665",
    "5555555555555555",
    "6665666666666566",
    "6665666666666566",
    "6665666666666566",
    "6665666666666566",
    "5555555555555555",
    "5666656666566665",
    "5666656666566665",
    "5666656666566665",
    "5666656666566665",
    "5555555555555555",
    "5555555555555555",
]
# Wand
TILE_WALL = [
    "0000000000000000",
    "0555555555555550",
    "0566666666666650",
    "0566555556655660",
    "0565666666665660",
    "0566555555555660",
    "0566566666556560",
    "0566566666556560",
    "0566566666556560",
    "0566566666556560",
    "0566555555555660",
    "0565666666665660",
    "0566666666666560",
    "0555555555555550",
    "0555555555555550",
    "0000000000000000",
]
# Baum
TILE_TREE = [
    "................",
    "......333.......",
    ".....33333......",
    "....3333333.....",
    "...333333333....",
    "..33333333333...",
    "..33b333b3333...",
    "..333333333b3...",
    "..3333b333333...",
    "...333333333....",
    "....3333333.....",
    ".....44444......",
    ".....44444......",
    ".....44444......",
    "....4444444.....",
    "................",
]
# Wasser
TILE_WATER = [
    "cccccccccccccccc",
    "c1cccc1ccc1ccccc",
    "cc1cccc1cccc1ccc",
    "ccc1ccccc1ccc1cc",
    "cccc1cccc1cc1ccc",
    "c1ccc1cc1ccc1ccc",
    "cc1ccccc1ccccccc",
    "ccc1c1cccc1cc1cc",
    "cccc1ccccc1cccc1",
    "c1cccc1cccc1cccc",
    "cc1cccc1cccc1ccc",
    "ccc1ccccc1cccccc",
    "cccc1c1ccc1cc1cc",
    "c1ccccc1ccccc1cc",
    "cc1cc1cccc1ccccc",
    "ccccccccccccccc.",
]
# Wald-Boden (dunkler)
TILE_FOREST = [
    "3333333333333333",
    "3433333343333333",
    "3343333333443333",
    "3334433343333443",
    "3333333333333333",
    "4333334334333433",
    "3333333333333333",
    "3343333333433334",
    "3333344333333333",
    "3333333333343333",
    "3343333334333333",
    "3333333333333344",
    "3333443333333333",
    "4333333343333333",
    "3333333333334333",
    "3333333333333333",
]
# Katakomben-Boden (dunkler Stein)
TILE_DUNGEON = [
    "5555555555555555",
    "5050050555055050",
    "5000050555050005",
    "5555555555555555",
    "5050550505550055",
    "5500550505500055",
    "5555555555555555",
    "5055055500550505",
    "5055055500550505",
    "5555555555555555",
    "5500555055505500",
    "5005550555055050",
    "5555555555555555",
    "5050055005500050",
    "5050055005500050",
    "5555555555555555",
]
# Schwarze Festung
TILE_FORTRESS = [
    "0000000000000000",
    "0222222222222220",
    "0211111111111120",
    "0212222222222120",
    "0212111111112120",
    "0212122222212120",
    "0212122002212120",
    "0212122002212120",
    "0212122222212120",
    "0212111111112120",
    "0212222222222120",
    "0211111111111120",
    "0222222222222220",
    "0000000000000000",
    "0000000000000000",
    "0000000000000000",
]

# Speicherpunkt (Gnadenpunkt)
TILE_SAVEPOINT = [
    "................",
    "......00..0.....",
    ".....0aa00a0....",
    "....0aaaaaaa0...",
    "....0a8888a.....",
    ".....088880.....",
    "......0880......",
    "......0880......",
    "......0880......",
    "......0990......",
    "....0099990.....",
    "...009999990....",
    "..00999999900...",
    "..00000000000...",
    "................",
    "................",
]

# Tür
TILE_DOOR = [
    "4444444444444444",
    "4222222222222224",
    "4244444444444424",
    "4244000440004424",
    "4244000440004424",
    "4244000440004424",
    "4244444444444424",
    "4244000440004424",
    "4244000440004424",
    "4244000440004424",
    "4244444444444424",
    "4244000440004424",
    "4244000440004424",
    "4244444444444424",
    "4222222222222224",
    "4444444444444444",
]

# --- NEUE WELT-DEKO ---------------------------------------------------------
# HAUS-KACHELN: Ein Haus besteht aus 3x3 Tiles (48x48 Pixel insgesamt).
# Layout mit Map-Zeichen:
#   h1 h2 h3    <- Dachreihe
#   h4 h5 h6    <- Wandreihe (h5 kann Tuer h7 sein)
#   h8 h9 hA    <- Sockelreihe

# DACH links
TILE_ROOF_L = [
    ".............222",
    "............2222",
    "...........22222",
    "..........228222",
    ".........2288222",
    "........22882222",
    ".......228822222",
    "......2288222222",
    ".....22882222222",
    "....228822222222",
    "...2288222222222",
    "..22882222222222",
    ".228822222222222",
    "2288222222222222",
    "4444444444444444",
    "6666666666666666",
]
TILE_ROOF_M = [
    "2222222222222222",
    "2222222222222222",
    "2882288228822882",
    "8228822882288228",
    "2822282228222822",
    "2222222222222222",
    "8228822882288228",
    "2882288228822882",
    "2222222222222222",
    "2882288228822882",
    "8228822882288228",
    "2822282228222822",
    "2222222222222222",
    "8228822882288228",
    "4444444444444444",
    "6666666666666666",
]
TILE_ROOF_R = [
    "222.............",
    "2222............",
    "22222...........",
    "222822..........",
    "2228822.........",
    "22228822........",
    "222228822.......",
    "2222228822......",
    "22222228822.....",
    "222222228822....",
    "2222222228822...",
    "22222222228822..",
    "222222222228822.",
    "2222222222228822",
    "4444444444444444",
    "6666666666666666",
]
# WAND mit Fenster links
TILE_WALL_WINDOW_L = [
    "6666666666666666",
    "6666666666666666",
    "6666000000006666",
    "666cccccccccc666",
    "66cccccccccccc66",
    "66c0cccccccc0c66",
    "66c0cc0cc0ccc0c6",
    "66c0000000000006",
    "66c0cc0cc0ccc0c6",
    "66c0cccccccc0c66",
    "66cccccccccccc66",
    "666cccccccccc666",
    "6666000000006666",
    "6666666666666666",
    "6566666666666566",
    "6666666666666666",
]
# WAND leer
TILE_WALL_M = [
    "6666666666666666",
    "6666666666666666",
    "6566666666666566",
    "6666666666666666",
    "6666656666666666",
    "6666666666566666",
    "6666666666666666",
    "6566666666666666",
    "6666666666666566",
    "6666666666666666",
    "6665666666666666",
    "6666666666566666",
    "6666666666666666",
    "6566666666666666",
    "6666666666666566",
    "6666666666666666",
]
TILE_WALL_WINDOW_R = TILE_WALL_WINDOW_L

# EINGANGSTUER
TILE_HOUSE_DOOR = [
    "6666666666666666",
    "6666666666666666",
    "6660000000000666",
    "6664444444444066",
    "6664666666664066",
    "6664686666664066",
    "6664686666664066",
    "6664666666a64066",
    "6664666666a64066",
    "6664666666664066",
    "6664666666664066",
    "6664666666664066",
    "6664666666664066",
    "6664666666664066",
    "5554444444444055",
    "5555555555555555",
]
# SOCKEL (unterer Rand)
TILE_BASE_L = [
    "6666666666666666",
    "6566666666666666",
    "6666666666666566",
    "6666666666666666",
    "5555555555555555",
    "5454545454545454",
    "5545454545454545",
    "5454545454545454",
    "5545454545454545",
    "3333333333333333",
    "3333333333333333",
    "3b33333333333333",
    "3333333b33333333",
    "3333333333333333",
    "3333333333b33333",
    "3333333333333333",
]
TILE_BASE_M = [
    "6666666666666666",
    "6666666566666666",
    "6666666666666666",
    "6666666666666666",
    "5555555555555555",
    "4545454545454545",
    "5454545454545454",
    "4545454545454545",
    "5454545454545454",
    "3333333333333333",
    "3333333333333333",
    "3333b33333333333",
    "3333333333333333",
    "3333333333b33333",
    "3b33333333333333",
    "3333333333333333",
]
TILE_BASE_R = [
    "6666666666666666",
    "6666666666666566",
    "6566666666666666",
    "6666666666666666",
    "5555555555555555",
    "5454545454545454",
    "5545454545454545",
    "5454545454545454",
    "5545454545454545",
    "3333333333333333",
    "3333333333333333",
    "3333333333333b33",
    "333333333b333333",
    "3333333333333333",
    "3333b33333333333",
    "3333333333333333",
]

# Zaun
TILE_FENCE = [
    "................",
    "................",
    "..4....4....4...",
    ".444..444..444..",
    ".444..444..444..",
    "44444444444444..",
    ".444..444..444..",
    ".444..444..444..",
    ".444..444..444..",
    "44444444444444..",
    ".444..444..444..",
    ".444..444..444..",
    ".444..444..444..",
    "..4....4....4...",
    "................",
    "................",
]
TILE_LANTERN = [
    "................",
    "......555.......",
    ".....55555......",
    "....0000000.....",
    "....0aaaaa0.....",
    "....0a999a0.....",
    "....0aaaaa0.....",
    "....0000000.....",
    ".......5........",
    ".......5........",
    "......444.......",
    "......444.......",
    "......444.......",
    "......444.......",
    "......444.......",
    "......444.......",
]
TILE_GRAVE = [
    "................",
    "................",
    "....5555555.....",
    "...666666665....",
    "..5666666665....",
    "..5666556665....",
    "..5665005665....",
    "..5665555665....",
    "..5666556665....",
    "..5666556665....",
    "..5666666665....",
    "..5666666665....",
    "..5555555555....",
    "..0000000000....",
    "................",
    "................",
]
TILE_ROCK = [
    "................",
    "................",
    "......5555......",
    ".....566655.....",
    "....56666655....",
    "...5666666665...",
    "..566666666665..",
    "..566555555665..",
    "..566656656665..",
    "..566555555665..",
    "...56666666665..",
    "....555555555...",
    "....0000000.....",
    "................",
    "................",
    "................",
]
TILE_CAMPFIRE_1 = [
    "................",
    "................",
    "........a.......",
    ".......aa9......",
    "......a999a.....",
    ".....a99888a....",
    ".....a99882a....",
    "....a9988822a...",
    "....a9888222a...",
    "....a8222222a...",
    ".....22222a.....",
    "....4444444.....",
    "...444444444....",
    "....5555555.....",
    "................",
    "................",
]
TILE_CAMPFIRE_2 = [
    "................",
    "................",
    ".......a........",
    "......99aa......",
    "......9999a.....",
    ".....a9988a.....",
    "....a99988aa....",
    "....a998822a....",
    "....a988222a....",
    ".....a22222a....",
    ".....a2222a.....",
    "....4444444.....",
    "...444444444....",
    "....5555555.....",
    "................",
    "................",
]
TILE_CAVE_FLOOR = [
    "1111111111111111",
    "1551515155151515",
    "1515551515551515",
    "1155555155555555",
    "1515551515551515",
    "1551515155151515",
    "1111111111111111",
    "1515155155155155",
    "1155115555155155",
    "1515551555555155",
    "1111111111111111",
    "1515555155515555",
    "1555555555515555",
    "1515551551555155",
    "1111111111111111",
    "1555555555555555",
]
TILE_CAVE_WALL = [
    "0000000000000000",
    "0155555555555510",
    "0551551551551550",
    "0555555555555550",
    "0515055500555550",
    "0555005000055550",
    "0555005500055550",
    "0555500000055550",
    "0555500050555550",
    "0555555555555550",
    "0551551551551550",
    "0515051505150550",
    "0555555555555550",
    "0155555555555510",
    "0015555555555100",
    "0000000000000000",
]
TILE_CRYSTAL = [
    "................",
    "................",
    ".......cc.......",
    "......cccc......",
    ".....cc77cc.....",
    "....cc7777cc....",
    "...cc777777cc...",
    "...c77777777c...",
    "...cc77777cc....",
    "....cc777cc.....",
    ".....ccccc......",
    "......ccc.......",
    ".......c........",
    "................",
    "................",
    "................",
]
TILE_DEAD_TREE = [
    "................",
    "......0.........",
    "...0..00..0.....",
    "...000000000....",
    "....0.000.0.....",
    "....0.000.00....",
    "....000000.0....",
    ".....00000......",
    "......000.......",
    "......000.......",
    "......000.......",
    "......000.......",
    ".....00000......",
    "....0000000.....",
    "....4444444.....",
    "................",
]
TILE_PUMPKIN = [
    "................",
    "................",
    "........3.......",
    ".......33.......",
    "......333.......",
    "....9999999.....",
    "...999999999....",
    "..99000909009...",
    "..99000909009...",
    "..99999999999...",
    "..99900099999...",
    "..99000000099...",
    "...9900099990...",
    "....99999990....",
    ".....00000......",
    "................",
]
TILE_PORTAL_1 = [
    "................",
    "....ddddddd.....",
    "...daaaaaaad....",
    "..daa11111aad...",
    "..da1111111ad...",
    "..da1111111ad...",
    "..da111c111ad...",
    "..da11ccc11ad...",
    "..da111c111ad...",
    "..da1111111ad...",
    "..da1111111ad...",
    "..daa11111aad...",
    "...daaaaaaad....",
    "....ddddddd.....",
    "................",
    "................",
]
TILE_PORTAL_2 = [
    "................",
    "....aaaaaaa.....",
    "...add11111da...",
    "..ad11cccc11da..",
    "..a11ccccccc1a..",
    "..a1cccccccca...",
    "..a1cccccccca...",
    "..a1cccccccca...",
    "..a1cccccccca...",
    "..a1cccccccca...",
    "..a11ccccccc1a..",
    "..ad11cccc11da..",
    "...add11111da...",
    "....aaaaaaa.....",
    "................",
    "................",
]
TILE_PORTAL_LOCKED = [
    "................",
    "....5555555.....",
    "...555555555....",
    "..55000000055...",
    "..5000000000d...",
    "..5000880000d...",
    "..5008888000d...",
    "..5008080800d...",
    "..5008888000d...",
    "..5000080000d...",
    "..5000000000d...",
    "..55000000055...",
    "...555555555....",
    "....5555555.....",
    "................",
    "................",
]
# Indoor-Tiles (Tavernen-Innenraum)
TILE_WOOD_FLOOR = [
    "4444444444444444",
    "4444444444444444",
    "5555555555555555",
    "4444444444444444",
    "4444444444444444",
    "4444444444444444",
    "5555555555555555",
    "4444444444444444",
    "4444444444444444",
    "4444444444444444",
    "5555555555555555",
    "4444444444444444",
    "4444444444444444",
    "4444444444444444",
    "5555555555555555",
    "4444444444444444",
]
TILE_WOOD_WALL = [
    "4444444444444444",
    "4555555555555554",
    "4666666666666664",
    "4666666666666664",
    "4555555555555554",
    "4666666666666664",
    "4666666666666664",
    "4555555555555554",
    "4666666666666664",
    "4666666666666664",
    "4555555555555554",
    "4666666666666664",
    "4666666666666664",
    "4555555555555554",
    "4444444444444444",
    "4444444444444444",
]
TILE_TABLE = [
    "................",
    "................",
    ".44444444444444.",
    ".4aaaaaaaaaaaa4.",
    ".4a8444444448a4.",
    ".4aa44444444a44.",
    ".44444444444444.",
    ".44444444444444.",
    "..4...4....4..4.",
    "..4...4....4..4.",
    "..4...4....4..4.",
    "..4...4....4..4.",
    "..4...4....4..4.",
    "..4...4....4..4.",
    "................",
    "................",
]
TILE_FIREPLACE = [
    "0000000000000000",
    "0555555555555550",
    "0500000000000050",
    "050aaaaaaaa00050",
    "050a9999999a0050",
    "050a9888888a0050",
    "050a9888822a0050",
    "050a9882222a0050",
    "050a8222222a0050",
    "0500444444400050",
    "0500444444400050",
    "0555555555555550",
    "0666666666666660",
    "0555555555555550",
    "0000000000000000",
    "................",
]
TILE_CARPET = [
    "2222222222222222",
    "2888888888888882",
    "2822aaaaaaaa2282",
    "2828aaaaaaaa8282",
    "2828aa8888aa8282",
    "2828a888888a8282",
    "2828a8aaaa8a8282",
    "2828a8aaaa8a8282",
    "2828a8aaaa8a8282",
    "2828a888888a8282",
    "2828aa8888aa8282",
    "2828aaaaaaaa8282",
    "2822aaaaaaaa2282",
    "2888888888888882",
    "2222222222222222",
    "2222222222222222",
]

NPC_MERCHANT = [
    "....000000000...",
    "...099999990....",
    "...08888888.....",
    "...09988899.....",
    "...0990009......",
    "...09aaaa90.....",
    "..04444444440...",
    "..0e4444444e0...",
    "..04e44444e40...",
    "..04ee444ee40...",
    "..04eee4eee40...",
    "...04444444.....",
    "....055055......",
    "....055055......",
    "....066066......",
    "....066066......",
]
# Alter Weiser (in der Taverne)
NPC_ELDER = [
    "....777777......",
    "...77777777.....",
    "..77766667770...",
    "..77d00dd077....",
    "..77d20dd027....",
    "..777066670.....",
    "..77766667770...",
    "..777766677770..",
    ".7777777777777..",
    ".d7777777777d7..",
    ".d7777777777d7..",
    ".d7d7777777d77..",
    ".d7d77d77d7d77..",
    "..d7777d77d77...",
    "..d7.d77d777....",
    "..55.dd7.dd.....",
]
# Katze (schlaeft in der Taverne)
NPC_CAT = [
    "................",
    "................",
    "....5....5......",
    "...55....55.....",
    "..555555555.....",
    ".5555555555.....",
    ".55a5555a55.....",
    ".555555555......",
    ".55a5555a5......",
    ".5555555.5......",
    ".55555555.......",
    "..55555.........",
    "..55.5..........",
    "..5..5..........",
    "................",
    "................",
]

# ============================================================================
#  SPRITE RENDERER
#  Zeichnet die oben definierten Pixel-Arrays als Sprites auf den Bildschirm.
#  Wird EINMAL beim Start in einen internen Puffer "gebrannt" (pyxel.images)
#  -> daher schnelles blt() zur Laufzeit.
# ============================================================================

class SpriteRegistry:
    """Packt alle Sprite-Arrays in Pyxel's Image-Banks fuer schnelles Blitten."""

    def __init__(self):
        # Wir benutzen Image Bank 0. Pyxel hat 3 Banks a 256x256.
        # Layout: Jedes Sprite 16x16, 16 pro Reihe -> 16x16 = 256 Sprites moeglich.
        self.slots = {}
        self._next_slot = 0
        self._register_all()

    def _burn(self, name, pixels):
        """Brennt ein Pixel-Array in die Image Bank 0."""
        slot = self._next_slot
        self._next_slot += 1
        x = (slot % 16) * 16
        y = (slot // 16) * 16
        img = pyxel.images[0]
        for py, row in enumerate(pixels):
            for px, ch in enumerate(row):
                if ch == '.':
                    # transparent = farbe 0 mit colkey bei blt
                    img.pset(x + px, y + py, 0)
                else:
                    col = COLOR_MAP.get(ch, 0)
                    img.pset(x + px, y + py, col if col is not None else 0)
        self.slots[name] = (x, y)
        return (x, y)

    def _register_all(self):
        # Held
        self._burn("hero_down_1",  HERO_DOWN_1)
        self._burn("hero_down_2",  HERO_DOWN_2)
        self._burn("hero_up_1",    HERO_UP_1)
        self._burn("hero_up_2",    HERO_UP_2)
        self._burn("hero_right_1", HERO_RIGHT_1)
        self._burn("hero_right_2", HERO_RIGHT_2)
        self._burn("hero_left_1",  HERO_LEFT_1)
        self._burn("hero_left_2",  HERO_LEFT_2)
        # Gegner
        self._burn("enemy_soldier",  ENEMY_SOLDIER)
        self._burn("enemy_skeleton", ENEMY_SKELETON)
        self._burn("enemy_beast",    ENEMY_BEAST)
        self._burn("enemy_mage",     ENEMY_MAGE)
        self._burn("enemy_knight",   ENEMY_KNIGHT)
        self._burn("enemy_wolf",     ENEMY_WOLF)
        self._burn("enemy_guardian", ENEMY_GUARDIAN)
        # Bosse
        self._burn("boss_cursed",  BOSS_CURSED)
        self._burn("boss_fogking", BOSS_FOGKING)
        self._burn("boss_king",    BOSS_KING)
        # Items
        self._burn("item_potion", ITEM_POTION)
        self._burn("item_sword",  ITEM_SWORD)
        self._burn("item_armor",  ITEM_ARMOR)
        self._burn("item_ring",   ITEM_RING)
        self._burn("item_crown",  ITEM_CROWN)
        # Tiles
        self._burn("tile_grass",    TILE_GRASS)
        self._burn("tile_stone",    TILE_STONE)
        self._burn("tile_wall",     TILE_WALL)
        self._burn("tile_tree",     TILE_TREE)
        self._burn("tile_water",    TILE_WATER)
        self._burn("tile_forest",   TILE_FOREST)
        self._burn("tile_dungeon",  TILE_DUNGEON)
        self._burn("tile_fortress", TILE_FORTRESS)
        self._burn("tile_save",     TILE_SAVEPOINT)
        self._burn("tile_door",     TILE_DOOR)
        # Neue Welt-Deko (Haus-Kacheln als richtige 3x3-Struktur)
        self._burn("tile_roof_l",     TILE_ROOF_L)
        self._burn("tile_roof_m",     TILE_ROOF_M)
        self._burn("tile_roof_r",     TILE_ROOF_R)
        self._burn("tile_wall_wl",    TILE_WALL_WINDOW_L)
        self._burn("tile_wall_m",     TILE_WALL_M)
        self._burn("tile_wall_wr",    TILE_WALL_WINDOW_R)
        self._burn("tile_house_door", TILE_HOUSE_DOOR)
        self._burn("tile_base_l",     TILE_BASE_L)
        self._burn("tile_base_m",     TILE_BASE_M)
        self._burn("tile_base_r",     TILE_BASE_R)
        self._burn("tile_fence",      TILE_FENCE)
        self._burn("tile_lantern",    TILE_LANTERN)
        self._burn("tile_grave",      TILE_GRAVE)
        self._burn("tile_rock",       TILE_ROCK)
        self._burn("tile_campfire_1", TILE_CAMPFIRE_1)
        self._burn("tile_campfire_2", TILE_CAMPFIRE_2)
        self._burn("tile_cave_floor", TILE_CAVE_FLOOR)
        self._burn("tile_cave_wall",  TILE_CAVE_WALL)
        self._burn("tile_crystal",    TILE_CRYSTAL)
        self._burn("tile_dead_tree",  TILE_DEAD_TREE)
        self._burn("tile_pumpkin",    TILE_PUMPKIN)
        self._burn("tile_portal_1",   TILE_PORTAL_1)
        self._burn("tile_portal_2",   TILE_PORTAL_2)
        self._burn("tile_portal_lck", TILE_PORTAL_LOCKED)
        # Indoor-Tiles
        self._burn("tile_wood_floor", TILE_WOOD_FLOOR)
        self._burn("tile_wood_wall",  TILE_WOOD_WALL)
        self._burn("tile_table",      TILE_TABLE)
        self._burn("tile_fireplace",  TILE_FIREPLACE)
        self._burn("tile_carpet",     TILE_CARPET)
        # NPCs
        self._burn("npc_merchant", NPC_MERCHANT)
        self._burn("npc_elder",    NPC_ELDER)
        self._burn("npc_cat",      NPC_CAT)

    def blt(self, name, x, y, w=16, h=16):
        sx, sy = self.slots[name]
        pyxel.blt(x, y, 0, sx, sy, w, h, 0)  # colkey 0 = schwarz ist transparent? -> wir nutzen -1

    def blt_solid(self, name, x, y, w=16, h=16):
        """Variante ohne Transparenz - fuer Tiles."""
        sx, sy = self.slots[name]
        pyxel.blt(x, y, 0, sx, sy, w, h)

    def blt_alpha(self, name, x, y, w=16, h=16, colkey=0):
        sx, sy = self.slots[name]
        pyxel.blt(x, y, 0, sx, sy, w, h, colkey)


# ============================================================================
#  DATEN: WAFFEN, RINGE, HAENDLER-ANGEBOTE
# ============================================================================

WEAPONS = {
    "rostiges_schwert": {"name": "Rostiges Schwert", "dmg": 8,  "sprite": "item_sword"},
    "soldatenklinge":   {"name": "Soldatenklinge",   "dmg": 14, "sprite": "item_sword"},
    "waldklinge":       {"name": "Waldklinge",       "dmg": 20, "sprite": "item_sword"},
    "waechterklinge":   {"name": "Waechterklinge",   "dmg": 28, "sprite": "item_sword"},
    "kronschwert":      {"name": "Kronschwert",      "dmg": 40, "sprite": "item_sword"},
}

ARMORS = {
    "lumpen":       {"name": "Lumpen",        "def": 2,  "sprite": "item_armor"},
    "lederrustung": {"name": "Lederruestung", "def": 6,  "sprite": "item_armor"},
    "stahlpanzer":  {"name": "Stahlpanzer",   "def": 12, "sprite": "item_armor"},
    "geisterplatte":{"name": "Geisterplatte", "def": 20, "sprite": "item_armor"},
}

RINGS = {
    "kraftring":   {"name": "Ring der Kraft",    "effect": "dmg+5",     "sprite": "item_ring"},
    "lebensring":  {"name": "Ring des Lebens",   "effect": "hp+30",     "sprite": "item_ring"},
    "hauchring":   {"name": "Ring des Hauches",  "effect": "stam+20",   "sprite": "item_ring"},
    "wallring":    {"name": "Ring der Mauer",    "effect": "def+5",     "sprite": "item_ring"},
}

# ============================================================================
#  WELT / KARTEN
#  Tile-Codes:
#    '.' = begehbar
#    '#' = Wand (blockiert)
#    'T' = Baum (blockiert)
#    't' = toter Halloween-Baum
#    '~' = Wasser (blockiert)
#    'S' = Speicherpunkt
#    'D' = Portal zum naechsten Gebiet (haelt automatisch)
#    'M' = Haendler
#    'E' = normaler Gegner-Spawn
#    'X' = Elite-Gegner-Spawn
#    'B' = Boss-Spawn
#    'C' = Truhe
#    'H' = Haus (blockiert)
#    'F' = Zaun (blockiert)
#    'L' = Laterne (blockiert, leuchtet)
#    'g' = Grabstein (blockiert)
#    'r' = Felsen (blockiert)
#    'f' = Lagerfeuer (blockiert, waermt)
#    'k' = Kristall (blockiert, leuchtet)
#    'p' = Kuerbis (blockiert)
# ============================================================================

# --- VERFALLENES DORF -----------------------------------------------------
# Gemuetliches Startgebiet mit Haeusern, Zaeunen, Laternen, Lagerfeuer
MAP_VILLAGE = [
    "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT",
    "T..............................T",
    "T....123....TT..........r......T",
    "T....456....TT.................T",
    "T....7h9....TT.................T",
    "T...........TT.........TTT.....T",
    "T..L........TT...E.....TTT.....T",
    "T.........................L....T",
    "T............f.................T",
    "T..S...................C......DT",
    "T..............................T",
    "T........123.......E...........T",
    "T........456..............L....T",
    "T........7h9.......r...........T",
    "T....E.........................T",
    "T.............TTT......FF......T",
    "T....r........TTT......FF......T",
    "T.............TTT..............T",
    "T..........E...................T",
    "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT",
]

# --- NEBELHOEHLE -----------------------------------------------------------
MAP_FOREST = [
    "################################",
    "#..............................#",
    "#....r................k........#",
    "#..k.....E..........r..........#",
    "D..............................#",
    "#......r........r.......C......#",
    "#..............................#",
    "#........E.................r...#",
    "#.S..k.........................#",
    "#......r.............B.........#",
    "#..............................#",
    "#.....k.....................X..#",
    "#..............................#",
    "#....E..r.........r............#",
    "#..............................#",
    "#..........r.......r..k........#",
    "#........X.....................#",
    "#..........r..................D#",
    "#...............E.....k........#",
    "################################",
]

# --- KNOCHENGARTEN - KOMPLETT OFFENE MAP OHNE INNENWAENDE -----------------
# Nur Aussenwände. Alle Deko ist explizit als einzelne Tiles platziert mit
# Laufkorridoren von mindestens 2 Tiles Breite ringsum.
MAP_CATACOMB = [
    "################################",
    "#..............................#",
    "#..g...g.......................#",
    "#..g...g.....t.........g...g...#",
    "D...........................g..#",
    "#..S...........p...............#",
    "#......................g.......#",
    "#....E.........................#",
    "#........t.....................#",
    "#..............................#",
    "#..............B...............#",
    "#........C.....................#",
    "#..........................X...#",
    "#.....p.........t..............#",
    "#..............................#",
    "#..E..................p........#",
    "#....................t.........#",
    "#.....g..g..........g...g......#",
    "#........................E....D#",
    "################################",
]

# --- SCHWARZE FESTUNG ----------------------------------------------------
MAP_FORTRESS = [
    "################################",
    "#..............................#",
    "#..S...........................#",
    "#.....r................r.......#",
    "#......X........X..............#",
    "#..............................#",
    "#.....r.................r......#",
    "#..########..##########........#",
    "#..#..................#........#",
    "D..#..................#..C.....#",
    "#..#........B.........#........#",
    "#..#..................#........#",
    "#..#..................#....X...#",
    "#..########..##########........#",
    "#.....r.................r......#",
    "#......X.......X...............#",
    "#..............................#",
    "#.....r.................r......#",
    "#..............................#",
    "################################",
]

# --- INDOOR-MAPS (Tavernen-Innenräume) ---
# Legende: W=Wand  .=Boden  c=Teppich  t=Tisch  F=Kamin
#          M=Händler  e=Alter  K=Katze  X=Ausgang
MAP_TAVERN_BIG = [
    "WWWWWWWWWWWWWWWW",
    "W..............W",
    "W..F...........W",
    "W..............W",
    "W......ccc.....W",
    "W......cMc.....W",
    "W......ccc.....W",
    "W..............W",
    "W..t.....K...e.W",
    "W..............W",
    "W..............W",
    "W.......X......W",
    "WWWWWWWWWWWWWWWW",
]
MAP_TAVERN_SMALL = [
    "WWWWWWWWWWWW",
    "W..........W",
    "W.F........W",
    "W..........W",
    "W...ccc....W",
    "W...cMc....W",
    "W...ccc....W",
    "W..........W",
    "W...t..K...W",
    "W..........W",
    "W.....X....W",
    "WWWWWWWWWWWW",
]

# ============================================================================
#  WELT-MANAGER
# ============================================================================

class World:
    def __init__(self):
        self.maps = {
            AREA_VILLAGE:  MAP_VILLAGE,
            AREA_FOREST:   MAP_FOREST,
            AREA_CATACOMB: MAP_CATACOMB,
            AREA_FORTRESS: MAP_FORTRESS,
        }
        self.current_area = AREA_VILLAGE
        self.defeated_bosses = set()
        self.looted_chests = set()
        self.defeated_enemies = set()
        self.cleared_areas = set()
        self.progression = {
            AREA_VILLAGE:  AREA_FOREST,
            AREA_FOREST:   AREA_CATACOMB,
            AREA_CATACOMB: AREA_FORTRESS,
        }
        # Indoor-Tracking
        self.in_indoor = False
        self.indoor_map_id = None
        self.outdoor_return_area = AREA_VILLAGE
        self.outdoor_return_pos = (64, 144)

    def current_map(self):
        if self.in_indoor:
            return MAP_TAVERN_BIG if self.indoor_map_id == "big" else MAP_TAVERN_SMALL
        return self.maps[self.current_area]

    def map_size(self):
        m = self.current_map()
        return len(m[0]), len(m)

    def is_blocked(self, tx, ty):
        mw, mh = self.map_size()
        if tx < 0 or ty < 0 or tx >= mw or ty >= mh:
            return True
        ch = self.current_map()[ty][tx]
        # Indoor: andere Regeln
        if self.in_indoor:
            return ch in ('W', 't', 'F')
        # Aussen: alle Deko und Haus-Wandkacheln blockieren, h ist die Tuer (begehbar)
        return ch in ('#', 'T', 't', '~', 'F', 'L', 'g', 'r', 'f', 'k', 'p',
                      '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a')

    def get_tile(self, tx, ty):
        mw, mh = self.map_size()
        if tx < 0 or ty < 0 or tx >= mw or ty >= mh:
            return '#'
        return self.current_map()[ty][tx]

    def theme_floor_sprite(self):
        if self.in_indoor:
            return "tile_wood_floor"
        if self.current_area == AREA_VILLAGE:
            return "tile_grass"
        if self.current_area == AREA_FOREST:
            return "tile_cave_floor"     # Hoehlen-Boden statt Wald
        if self.current_area == AREA_CATACOMB:
            return "tile_dungeon"
        if self.current_area == AREA_FORTRESS:
            return "tile_fortress"
        return "tile_grass"

    def is_portal_open(self, from_area):
        """Portal zu naechstem Gebiet offen? Nur wenn aktuelles Gebiet geclearet."""
        return from_area in self.cleared_areas


# ============================================================================
#  ENTITIES
# ============================================================================

class Entity:
    def __init__(self, x, y):
        self.x = float(x)          # Pixel-Koordinaten (float fuer smoothe Bewegung)
        self.y = float(y)
        self.w = 12
        self.h = 14
        self.vx = 0.0
        self.vy = 0.0
        self.facing = FACE_DOWN
        self.alive = True
        self.hp = 10
        self.max_hp = 10
        self.anim_timer = 0
        self.hit_flash = 0  # Frames verbleibend in denen Entity blinkt

    def rect(self):
        return (self.x - self.w/2, self.y - self.h/2, self.w, self.h)

    def collides_rect(self, other):
        ax, ay, aw, ah = self.rect()
        bx, by, bw, bh = other.rect()
        return not (ax + aw < bx or bx + bw < ax or ay + ah < by or by + bh < ay)

    def distance_to(self, other):
        return math.hypot(self.x - other.x, self.y - other.y)


class Player(Entity):
    def __init__(self, x, y):
        super().__init__(x, y)
        # Stats
        self.level       = 1
        self.xp          = 0
        self.xp_to_next  = 30
        self.max_hp      = 100
        self.hp          = self.max_hp
        self.max_stamina = 100
        self.stamina     = self.max_stamina
        self.base_dmg    = 10
        self.base_def    = 2
        # Ausruestung
        self.weapon  = "rostiges_schwert"
        self.armor   = "lumpen"
        self.ring    = None
        # Inventar
        self.potions     = 3
        self.max_potions = 5
        self.gold        = 0
        self.inventory   = []  # Liste von (type, id) z.B. ("weapon","soldatenklinge")
        # Kampf-State
        self.attack_timer    = 0   # Frames verbleibend in Angriff (Hitbox aktiv)
        self.attack_cooldown = 0
        self.dash_timer      = 0   # Frames verbleibend im Dash (unverwundbar)
        self.dash_cooldown   = 0
        self.blocking        = False
        self.invuln          = 0
        # Bewegung
        self.speed = 1.5
        # Besitzt-Krone?
        self.has_crown = False

    # ---- derived stats ----
    def damage(self):
        d = self.base_dmg + WEAPONS[self.weapon]["dmg"]
        if self.ring == "kraftring":
            d += 5
        return d

    def defense(self):
        d = self.base_def + ARMORS[self.armor]["def"]
        if self.ring == "wallring":
            d += 5
        return d

    def total_max_hp(self):
        bonus = 30 if self.ring == "lebensring" else 0
        return self.max_hp + bonus

    def total_max_stamina(self):
        bonus = 20 if self.ring == "hauchring" else 0
        return self.max_stamina + bonus

    # ---- actions ----
    def can_attack(self):
        return self.attack_cooldown <= 0 and self.stamina >= 15 and self.dash_timer <= 0

    def attack(self):
        self.attack_timer    = 12
        self.attack_cooldown = 20
        self.stamina        -= 15

    def can_dash(self):
        return self.dash_cooldown <= 0 and self.stamina >= 20

    def start_dash(self):
        self.dash_timer    = 10
        self.dash_cooldown = 30
        self.invuln        = 10
        self.stamina      -= 20

    def heal(self):
        if self.potions > 0 and self.hp < self.total_max_hp():
            self.potions -= 1
            self.hp = min(self.total_max_hp(), self.hp + 60)
            return True
        return False

    def gain_xp(self, amount):
        self.xp += amount
        levels_gained = 0
        while self.xp >= self.xp_to_next:
            self.xp -= self.xp_to_next
            self.level += 1
            self.xp_to_next = int(self.xp_to_next * 1.4) + 10
            levels_gained += 1
        return levels_gained

    def apply_level_up(self, stat):
        """stat: 'hp','stamina','dmg','def'"""
        if stat == "hp":
            self.max_hp += 15
            self.hp = self.total_max_hp()
        elif stat == "stamina":
            self.max_stamina += 10
            self.stamina = self.total_max_stamina()
        elif stat == "dmg":
            self.base_dmg += 3
        elif stat == "def":
            self.base_def += 2

    # ---- hitbox des angriffs ----
    def attack_hitbox(self):
        if self.attack_timer <= 0:
            return None
        reach = 18
        if self.facing == FACE_RIGHT:
            return (self.x,        self.y - 8, reach, 16)
        if self.facing == FACE_LEFT:
            return (self.x - reach, self.y - 8, reach, 16)
        if self.facing == FACE_UP:
            return (self.x - 8, self.y - reach, 16, reach)
        return (self.x - 8, self.y, 16, reach)


class Enemy(Entity):
    """
    Basis-Gegner-Klasse. Typen:
       soldier, skeleton, beast, mage, knight, wolf, guardian
       boss_cursed, boss_fogking, boss_king
    """
    def __init__(self, x, y, kind, spawn_id):
        super().__init__(x, y)
        self.kind = kind
        self.spawn_id = spawn_id
        self._configure(kind)
        self.ai_timer     = 0
        self.attack_timer = 0
        self.attack_cd    = 0
        self.stun         = 0
        self.state        = "idle"   # idle / chase / attack / hurt / dead
        self.drop_xp      = 10

    def _configure(self, kind):
        cfg = {
            "soldier":  dict(hp=25,  dmg=6,  speed=0.8, xp=15,  reach=18, sprite="enemy_soldier",  size=(12,14)),
            "skeleton": dict(hp=18,  dmg=5,  speed=1.0, xp=12,  reach=16, sprite="enemy_skeleton", size=(12,14)),
            "beast":    dict(hp=40,  dmg=8,  speed=1.1, xp=25,  reach=18, sprite="enemy_beast",    size=(14,14)),
            "mage":     dict(hp=22,  dmg=9,  speed=0.6, xp=30,  reach=40, sprite="enemy_mage",     size=(12,14)),
            "knight":   dict(hp=80,  dmg=14, speed=0.6, xp=60,  reach=20, sprite="enemy_knight",   size=(14,14)),
            "wolf":     dict(hp=70,  dmg=12, speed=1.3, xp=55,  reach=18, sprite="enemy_wolf",     size=(14,14)),
            "guardian": dict(hp=120, dmg=16, speed=0.8, xp=90,  reach=22, sprite="enemy_guardian", size=(14,14)),
            "boss_cursed":  dict(hp=260, dmg=14, speed=0.85, xp=250, reach=22, sprite="boss_cursed",  size=(16,16)),
            "boss_fogking": dict(hp=380, dmg=16, speed=0.95, xp=400, reach=24, sprite="boss_fogking", size=(16,16)),
            "boss_king":    dict(hp=650, dmg=20, speed=1.0,  xp=999, reach=26, sprite="boss_king",    size=(16,16)),
        }[kind]
        self.max_hp   = cfg["hp"]
        self.hp       = cfg["hp"]
        self.dmg      = cfg["dmg"]
        self.speed    = cfg["speed"]
        self.drop_xp  = cfg["xp"]
        self.reach    = cfg["reach"]
        self.sprite   = cfg["sprite"]
        self.w, self.h = cfg["size"]
        self.is_boss  = kind.startswith("boss_")
        self.is_elite = kind in ("knight","wolf","guardian")

    def attack_hitbox(self):
        # Fairer Angriffs-Rhythmus:
        # attack_timer laeuft runter. Angriffs-Start = timer == (cfg). Aktive Hitbox
        # nur fuer ein ganz kurzes Zeitfenster in der Mitte (gute Dodge-Chance).
        if self.is_boss:
            # Boss: lange Wind-up (28), nur Frames 10-14 aktiv => 4 Frames Hit
            if self.attack_timer <= 10 or self.attack_timer >= 14:
                return None
        else:
            if self.attack_timer <= 6 or self.attack_timer >= 14:
                return None
        reach = self.reach
        if self.facing == FACE_RIGHT:
            return (self.x,        self.y - 8, reach, 16)
        if self.facing == FACE_LEFT:
            return (self.x - reach, self.y - 8, reach, 16)
        if self.facing == FACE_UP:
            return (self.x - 8, self.y - reach, 16, reach)
        return (self.x - 8, self.y, 16, reach)


class Chest(Entity):
    def __init__(self, x, y, loot):
        super().__init__(x, y)
        self.loot = loot           # dict: {"type": "weapon", "id": "soldatenklinge"} oder {"type": "gold", "amount": 50}
        self.opened = False
        self.w = 14
        self.h = 12


class SavePoint(Entity):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.w = 12
        self.h = 16


class Door(Entity):
    def __init__(self, x, y, target_area, spawn_x, spawn_y, forward=True):
        super().__init__(x, y)
        self.target_area = target_area
        self.spawn_x = spawn_x
        self.spawn_y = spawn_y
        self.forward = forward   # True = fuehrt ins naechste Gebiet (braucht clear)
        self.w = 14
        self.h = 16


class HouseDoor(Entity):
    """Eingang zu einer Taverne (Indoor-Szene)."""
    def __init__(self, x, y, tavern_id):
        super().__init__(x, y)
        self.tavern_id = tavern_id   # "big" oder "small"
        self.w = 14
        self.h = 16


class IndoorExit(Entity):
    """Ausgang aus einer Taverne zurueck in die Welt."""
    def __init__(self, x, y):
        super().__init__(x, y)
        self.w = 14
        self.h = 16


class Merchant(Entity):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.w = 12
        self.h = 14


class Projectile:
    """Schattenmagier-Projektil."""
    def __init__(self, x, y, vx, vy, dmg, owner_is_enemy=True):
        self.x, self.y = x, y
        self.vx, self.vy = vx, vy
        self.dmg = dmg
        self.alive = True
        self.life = 120
        self.owner_is_enemy = owner_is_enemy
        self.w = 6
        self.h = 6

    def update(self):
        self.x += self.vx
        self.y += self.vy
        self.life -= 1
        if self.life <= 0:
            self.alive = False

    def rect(self):
        return (self.x - self.w/2, self.y - self.h/2, self.w, self.h)


class Particle:
    """Einfache Effekt-Partikel (Hit, Dust, Sparkle)."""
    __slots__ = ("x","y","vx","vy","life","color","size")
    def __init__(self, x, y, vx, vy, life, color, size=1):
        self.x = x; self.y = y
        self.vx = vx; self.vy = vy
        self.life = life
        self.color = color
        self.size = size

    def update(self):
        self.x += self.vx
        self.y += self.vy
        self.vx *= 0.9
        self.vy *= 0.9
        self.life -= 1


class FloatingText:
    __slots__ = ("x","y","text","color","life")
    def __init__(self, x, y, text, color):
        self.x = x; self.y = y
        self.text = text
        self.color = color
        self.life = 45


# ============================================================================
#  HILFS-FUNKTIONEN
# ============================================================================

def rect_overlap(r1, r2):
    ax, ay, aw, ah = r1
    bx, by, bw, bh = r2
    return not (ax + aw < bx or bx + bw < ax or ay + ah < by or by + bh < ay)


def clamp(v, a, b):
    return max(a, min(b, v))


# ============================================================================
#  SPIEL-HAUPTKLASSE
# ============================================================================

class Game:
    def __init__(self):
        pyxel.init(SCREEN_W, SCREEN_H, title="Schatten der zerbrochenen Krone",
                   fps=FPS, display_scale=3, quit_key=pyxel.KEY_ESCAPE)

        # Sprites in Image-Bank brennen
        self.sprites = SpriteRegistry()

        # Welt & Spieler
        self.world   = World()
        self.player  = Player(x=4*TILE, y=9*TILE)

        # Entities der aktuellen Karte
        self.enemies     = []
        self.chests      = []
        self.savepoints  = []
        self.doors       = []
        self.house_doors = []
        self.indoor_exits= []
        self.merchants   = []
        self.npcs        = []   # Deko-NPCs in Tavernen
        self.projectiles = []
        self.particles   = []
        self.floats      = []

        # Kamera
        self.cam_x = 0
        self.cam_y = 0

        # State
        self.state         = STATE_MENU
        self.dialog_lines  = []
        self.dialog_index  = 0
        self.msg           = ""
        self.msg_timer     = 0
        self.menu_cursor   = 0
        self.levelup_cursor= 0
        self.merchant_cursor = 0
        self.inv_cursor    = 0
        self.near_object   = None   # SavePoint/Door/Merchant/Chest
        self.game_tick     = 0
        self.cutscene_timer = 0
        self.total_clearable = 0

        # Haendler-Angebote
        self.merchant_stock = [
            ("potion", None, 20),
            ("weapon", "soldatenklinge", 80),
            ("armor",  "lederrustung",   70),
            ("ring",   "kraftring",      120),
        ]

        # Startkarte laden
        self.load_area(AREA_VILLAGE, 4*TILE, 9*TILE)

        # Audio einrichten
        self._setup_audio()

        pyxel.run(self.update, self.draw)

    # ------------------------------------------------------------------------
    #  AUDIO
    # ------------------------------------------------------------------------
    def _setup_audio(self):
        # Pyxel Sound API: pyxel.sounds[idx].set(notes, tones, volumes, effects, speed)
        # Sound 0: Spieler-Angriff
        pyxel.sounds[0].set("c3e3g3", "p", "6", "n", 20)
        # Sound 1: Treffer (getroffen)
        pyxel.sounds[1].set("a1c1", "n", "7", "f", 15)
        # Sound 2: Gegner stirbt
        pyxel.sounds[2].set("g2e2c2a1", "n", "6", "f", 18)
        # Sound 3: Heiltrank
        pyxel.sounds[3].set("c2e2g2c3", "s", "5", "n", 15)
        # Sound 4: Level-Up
        pyxel.sounds[4].set("c2e2g2c3e3g3c4", "s", "6", "n", 15)
        # Sound 5: Dash
        pyxel.sounds[5].set("c2g1", "n", "4", "f", 10)
        # Sound 6: Item aufsammeln
        pyxel.sounds[6].set("e3g3c4", "s", "5", "n", 15)
        # Sound 7: Tuer / Speichern
        pyxel.sounds[7].set("c2g2c3", "s", "4", "n", 25)

    def play(self, idx):
        pyxel.play(0, idx)

    # ------------------------------------------------------------------------
    #  KARTE LADEN
    # ------------------------------------------------------------------------
    def load_area(self, area_id, spawn_px, spawn_py):
        self.world.in_indoor = False
        self.world.current_area = area_id
        self.enemies.clear()
        self.chests.clear()
        self.savepoints.clear()
        self.doors.clear()
        self.house_doors.clear()
        self.indoor_exits.clear()
        self.merchants.clear()
        self.npcs.clear()
        self.projectiles.clear()
        self.particles.clear()
        self.floats.clear()

        mp = self.world.current_map()

        enemy_pool = {
            AREA_VILLAGE:  ["soldier", "skeleton", "soldier"],
            AREA_FOREST:   ["beast", "mage", "beast", "skeleton"],
            AREA_CATACOMB: ["skeleton", "mage", "skeleton", "soldier"],
            AREA_FORTRESS: ["soldier", "mage", "knight"],
        }[area_id]

        elite_pool = {
            AREA_VILLAGE:  "knight",
            AREA_FOREST:   "wolf",
            AREA_CATACOMB: "guardian",
            AREA_FORTRESS: "knight",
        }[area_id]

        boss_map = {
            AREA_VILLAGE:  None,           # Dorf hat keinen eigenen Boss
            AREA_FOREST:   "boss_fogking",
            AREA_CATACOMB: "boss_cursed",
            AREA_FORTRESS: "boss_king",
        }

        chest_pool = {
            AREA_VILLAGE:  [("potion", None), ("weapon", "soldatenklinge"), ("gold", 30)],
            AREA_FOREST:   [("weapon", "waldklinge"), ("armor", "lederrustung"), ("ring", "kraftring"), ("potion", None)],
            AREA_CATACOMB: [("armor", "stahlpanzer"), ("ring", "lebensring"), ("weapon", "waechterklinge"), ("potion", None)],
            AREA_FORTRESS: [("ring", "wallring"), ("armor", "geisterplatte"), ("weapon", "waechterklinge")],
        }[area_id]

        spawn_idx = 0
        chest_idx = 0
        door_counter = 0
        # Alle normalen Gegner-Spawns in diesem Gebiet zaehlen (fuer Clear-Check)
        self.total_clearable = 0
        for ty, row in enumerate(mp):
            for tx, ch in enumerate(row):
                px = tx * TILE + TILE // 2
                py = ty * TILE + TILE // 2

                if ch == 'E':
                    self.total_clearable += 1
                    key = (area_id, spawn_idx)
                    if key not in self.world.defeated_enemies:
                        kind = enemy_pool[spawn_idx % len(enemy_pool)]
                        e = Enemy(px, py, kind, spawn_idx)
                        self.enemies.append(e)
                    spawn_idx += 1
                elif ch == 'X':
                    self.total_clearable += 1
                    key = (area_id, spawn_idx)
                    if key not in self.world.defeated_enemies:
                        e = Enemy(px, py, elite_pool, spawn_idx)
                        self.enemies.append(e)
                    spawn_idx += 1
                elif ch == 'B':
                    boss = boss_map[area_id]
                    if boss and boss not in self.world.defeated_bosses:
                        e = Enemy(px, py, boss, 9000 + area_id)
                        self.enemies.append(e)
                elif ch == 'C':
                    key = (area_id, tx, ty)
                    if key not in self.world.looted_chests:
                        loot_type, loot_id = chest_pool[chest_idx % len(chest_pool)]
                        loot = {"type": loot_type, "id": loot_id}
                        if loot_type == "gold":
                            loot = {"type": "gold", "amount": loot_id}
                        self.chests.append(Chest(px, py, loot))
                        self.chests[-1].tx = tx
                        self.chests[-1].ty = ty
                    chest_idx += 1
                elif ch == 'S':
                    self.savepoints.append(SavePoint(px, py))
                elif ch == 'M':
                    self.merchants.append(Merchant(px, py))
                elif ch == 'D':
                    # forward-Flag anhand Zielgebiet: hoehere Area = forward
                    target, spx, spy = self._door_target(area_id, door_counter)
                    is_forward = target > area_id
                    self.doors.append(Door(px, py, target, spx, spy, is_forward))
                    door_counter += 1
                elif ch == 'h':
                    # Haus-Eingang. Erste Tuer im Gebiet = grosse Taverne, zweite = klein
                    tavern_id = "big" if len(self.house_doors) == 0 else "small"
                    self.house_doors.append(HouseDoor(px, py, tavern_id))

        # Clear-Pruefung: wenn alle normalen Gegner in diesem Gebiet tot sind -> freischalten
        remaining = sum(1 for (a, _) in self.world.defeated_enemies if a == area_id)
        if remaining >= self.total_clearable and self.total_clearable > 0:
            self.world.cleared_areas.add(area_id)

        # Spieler-Position
        self.player.x = spawn_px
        self.player.y = spawn_py

    def load_indoor(self, tavern_id, from_x, from_y):
        """Laedt einen Tavernen-Innenraum."""
        # Merken wo der Spieler draussen war (2 Tiles darunter, nicht in der Tuer)
        self.world.outdoor_return_area = self.world.current_area
        self.world.outdoor_return_pos = (from_x, from_y + TILE + 4)
        self.world.in_indoor = True
        self.world.indoor_map_id = tavern_id

        self.enemies.clear()
        self.chests.clear()
        self.savepoints.clear()
        self.doors.clear()
        self.house_doors.clear()
        self.indoor_exits.clear()
        self.merchants.clear()
        self.npcs.clear()
        self.projectiles.clear()
        self.particles.clear()
        self.floats.clear()

        mp = self.world.current_map()
        for ty, row in enumerate(mp):
            for tx, ch in enumerate(row):
                px = tx * TILE + TILE // 2
                py = ty * TILE + TILE // 2
                if ch == 'M':
                    self.merchants.append(Merchant(px, py))
                elif ch == 'X':
                    self.indoor_exits.append(IndoorExit(px, py))
                elif ch == 'e':
                    npc = Entity(px, py)
                    npc.sprite_name = "npc_elder"
                    self.npcs.append(npc)
                elif ch == 'K':
                    npc = Entity(px, py)
                    npc.sprite_name = "npc_cat"
                    self.npcs.append(npc)

        # Spieler direkt oberhalb des Ausgangs platzieren (blickt nach oben)
        if self.indoor_exits:
            ex = self.indoor_exits[0]
            self.player.x = ex.x
            self.player.y = ex.y - TILE
            self.player.facing = FACE_UP

    def _door_target(self, area_id, door_index):
        """
        Gibt das Ziel der Tuer zurueck (area, spawn_px, spawn_py).
        Doors werden in der Reihenfolge nummeriert, in der sie in der Map
        von links-oben nach rechts-unten gelesen werden.
        """
        # Maps (D-Positionen):
        #   Village:  [(30,9)]                          -> Hoehle
        #   Forest:   [(0,4), (29,17)]                  -> Dorf, Katakomben
        #   Catacomb: [(0,4), (30,16)]                  -> Hoehle, Festung
        #   Fortress: [(0,9)]                           -> Katakomben zurueck
        # Wir spawnen den Spieler je 1 Tile vom Ziel-Portal weg (in Richtung Inneres).
        routes = {
            (AREA_VILLAGE, 0):  (AREA_FOREST,    2*TILE+8, 4*TILE+8),       # Dorf-Ausgang -> Hoehle (am linken Portal)
            (AREA_FOREST, 0):   (AREA_VILLAGE,  29*TILE+8, 9*TILE+8),       # Hoehle-Links -> Dorf (am rechten Portal)
            (AREA_FOREST, 1):   (AREA_CATACOMB,  2*TILE+8, 4*TILE+8),       # Hoehle-Rechts -> Katakomben
            (AREA_CATACOMB, 0): (AREA_FOREST,   28*TILE+8, 17*TILE+8),      # Katakomben-Links -> Hoehle zurueck
            (AREA_CATACOMB, 1): (AREA_FORTRESS,  2*TILE+8, 9*TILE+8),       # Katakomben-Rechts -> Festung
            (AREA_FORTRESS, 0): (AREA_CATACOMB, 29*TILE+8, 16*TILE+8),      # Festung-Links -> Katakomben zurueck
        }
        return routes.get((area_id, door_index), (AREA_VILLAGE, 6*TILE, 9*TILE))

    # ------------------------------------------------------------------------
    #  HAUPT-UPDATE
    # ------------------------------------------------------------------------
    def update(self):
        self.game_tick += 1

        if self.state == STATE_MENU:
            self.update_menu()
        elif self.state == STATE_PLAYING:
            self.update_playing()
        elif self.state == STATE_DIALOG:
            self.update_dialog()
        elif self.state == STATE_LEVEL_UP:
            self.update_levelup()
        elif self.state == STATE_MERCHANT:
            self.update_merchant()
        elif self.state == STATE_INVENTORY:
            self.update_inventory()
        elif self.state == STATE_GAME_OVER:
            if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
                self.restart_game()
        elif self.state == STATE_VICTORY:
            # Cutscene: laeuft automatisch durch, kann nach 3s geskippt werden
            self.cutscene_timer = getattr(self, 'cutscene_timer', 0) + 1
            if self.cutscene_timer > 900:
                pyxel.quit()
            elif self.cutscene_timer > 180 and (pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE)):
                # Skip moeglich nach Phase 1
                self.cutscene_timer = 900

    # ------------------------------------------------------------------------
    #  MENUE
    # ------------------------------------------------------------------------
    def update_menu(self):
        if pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_W):
            self.menu_cursor = (self.menu_cursor - 1) % 2
        if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
            self.menu_cursor = (self.menu_cursor + 1) % 2
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            if self.menu_cursor == 0:
                self.state = STATE_PLAYING
                self.show_msg(AREA_NAMES[self.world.current_area], 120)
            else:
                pyxel.quit()

    # ------------------------------------------------------------------------
    #  GAMEPLAY
    # ------------------------------------------------------------------------
    def update_playing(self):
        # Inventar oeffnen
        if pyxel.btnp(pyxel.KEY_I):
            self.state = STATE_INVENTORY
            return

        p = self.player

        # Passive Regeneration: Ausdauer regeneriert schnell wenn nicht geblockt
        if not p.blocking and p.attack_timer <= 0:
            p.stamina = min(p.total_max_stamina(), p.stamina + 0.6)

        # Cooldowns runterzaehlen
        if p.attack_timer    > 0: p.attack_timer    -= 1
        if p.attack_cooldown > 0: p.attack_cooldown -= 1
        if p.dash_timer      > 0: p.dash_timer      -= 1
        if p.dash_cooldown   > 0: p.dash_cooldown   -= 1
        if p.invuln          > 0: p.invuln          -= 1
        if p.hit_flash       > 0: p.hit_flash       -= 1

        # Eingaben
        dx, dy = 0, 0
        if pyxel.btn(pyxel.KEY_LEFT)  or pyxel.btn(pyxel.KEY_A): dx -= 1; p.facing = FACE_LEFT
        if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D): dx += 1; p.facing = FACE_RIGHT
        if pyxel.btn(pyxel.KEY_UP)    or pyxel.btn(pyxel.KEY_W): dy -= 1; p.facing = FACE_UP
        if pyxel.btn(pyxel.KEY_DOWN)  or pyxel.btn(pyxel.KEY_S): dy += 1; p.facing = FACE_DOWN

        # Block (hält Bewegung an, reduziert Schaden)
        p.blocking = pyxel.btn(pyxel.KEY_L) or pyxel.btn(pyxel.KEY_CTRL) or pyxel.btn(pyxel.KEY_RCTRL)
        if p.blocking and p.stamina <= 0:
            p.blocking = False
        if p.blocking:
            p.stamina = max(0, p.stamina - 0.2)
            dx, dy = 0, 0   # Block -> kein Laufen

        # Angriff
        if (pyxel.btnp(pyxel.KEY_J) or pyxel.btnp(pyxel.KEY_SPACE)) and p.can_attack():
            p.attack()
            self.play(0)

        # Dash
        if (pyxel.btnp(pyxel.KEY_K) or pyxel.btnp(pyxel.KEY_SHIFT)) and p.can_dash() and (dx or dy):
            p.start_dash()
            self.play(5)

        # Heiltrank
        if pyxel.btnp(pyxel.KEY_H):
            if p.heal():
                self.play(3)
                for _ in range(12):
                    ang = random.random() * math.tau
                    self.particles.append(Particle(p.x, p.y,
                        math.cos(ang)*1.5, math.sin(ang)*1.5, 30, 11, 1))
                self.floats.append(FloatingText(p.x, p.y - 10, "+60", 11))

        # Interact
        if pyxel.btnp(pyxel.KEY_E):
            self.try_interact()

        # Bewegung (diagonal normalisieren)
        if dx or dy:
            length = math.hypot(dx, dy)
            dx /= length
            dy /= length
            speed = p.speed
            if p.dash_timer > 0:
                speed = p.speed * 3.0
            if p.attack_timer > 0:
                speed *= 0.3   # beim Angriff deutlich langsamer
            p.vx = dx * speed
            p.vy = dy * speed
        else:
            p.vx = 0
            p.vy = 0

        # Kollision mit Map (Achsen-getrennt)
        self.move_with_collision(p, p.vx, p.vy)

        p.anim_timer = (p.anim_timer + 1) % 30

        # "Near Object" bestimmen (fuer E-Taste Interaktion)
        self.near_object = None
        for s in self.savepoints:
            if p.collides_rect(s):
                self.near_object = ("save", s); break
        if not self.near_object:
            for d in self.doors:
                if p.collides_rect(d):
                    self.near_object = ("door", d); break
        if not self.near_object:
            for d in self.house_doors:
                if p.collides_rect(d):
                    self.near_object = ("house", d); break
        if not self.near_object:
            for x in self.indoor_exits:
                if p.collides_rect(x):
                    self.near_object = ("exit", x); break
        if not self.near_object:
            for m in self.merchants:
                if p.collides_rect(m):
                    self.near_object = ("merchant", m); break
        if not self.near_object:
            for c in self.chests:
                if not c.opened and p.collides_rect(c):
                    self.near_object = ("chest", c); break

        # Gegner updaten (nur draussen - Indoor ist sicher)
        if not self.world.in_indoor:
            for e in self.enemies:
                if e.alive:
                    self.update_enemy(e)

        # Projektile
        for pr in self.projectiles:
            pr.update()
            # Kollision mit Map
            if self.world.is_blocked(int(pr.x // TILE), int(pr.y // TILE)):
                pr.alive = False
            # Treffer pruefen
            if pr.owner_is_enemy:
                if pr.alive and rect_overlap(pr.rect(), p.rect()) and p.invuln <= 0:
                    self.damage_player(pr.dmg)
                    pr.alive = False
            else:
                for e in self.enemies:
                    if e.alive and rect_overlap(pr.rect(), e.rect()):
                        self.damage_enemy(e, pr.dmg)
                        pr.alive = False
                        break

        self.projectiles = [pr for pr in self.projectiles if pr.alive]

        # Treffer-Erkennung Spieler -> Gegner
        if p.attack_timer > 0:
            hb = p.attack_hitbox()
            if hb:
                for e in self.enemies:
                    if e.alive and rect_overlap(hb, e.rect()) and e.stun <= 0:
                        self.damage_enemy(e, p.damage())
                        # knockback
                        kx = e.x - p.x
                        ky = e.y - p.y
                        ln = math.hypot(kx, ky) + 0.01
                        e.x += (kx/ln) * 4
                        e.y += (ky/ln) * 4
                        e.stun = 12

        # Treffer-Erkennung Gegner -> Spieler
        if p.invuln <= 0:
            for e in self.enemies:
                if not e.alive: continue
                hb = e.attack_hitbox()
                if hb and rect_overlap(hb, p.rect()):
                    self.damage_player(e.dmg)

        # Truhen: nix pro Frame; Interaktion via E.
        # Boss-Tode & aufraeumen
        died = [e for e in self.enemies if not e.alive]
        for e in died:
            # XP gewaehren nur einmalig
            pass
        # Entities raus
        self.enemies = [e for e in self.enemies if e.alive or e.state == "death_anim"]

        # Partikel updaten
        for pa in self.particles: pa.update()
        self.particles = [pa for pa in self.particles if pa.life > 0]
        for ft in self.floats:
            ft.y -= 0.5
            ft.life -= 1
        self.floats = [f for f in self.floats if f.life > 0]

        # Kamera auf Spieler zentrieren
        mw, mh = self.world.map_size()
        self.cam_x = clamp(p.x - SCREEN_W // 2, 0, mw * TILE - SCREEN_W)
        self.cam_y = clamp(p.y - SCREEN_H // 2, 0, mh * TILE - SCREEN_H)

        # Ableben?
        if p.hp <= 0:
            self.state = STATE_GAME_OVER

        # Sieg?
        if p.has_crown:
            self.state = STATE_VICTORY
            self.cutscene_timer = 0

        # Message-Timer
        if self.msg_timer > 0:
            self.msg_timer -= 1

    def move_with_collision(self, ent, vx, vy):
        """Bewegt Entity getrennt in X und Y und testet Tile-Kollision."""
        # X
        new_x = ent.x + vx
        if not self._solid_at(new_x, ent.y, ent.w, ent.h):
            ent.x = new_x
        # Y
        new_y = ent.y + vy
        if not self._solid_at(ent.x, new_y, ent.w, ent.h):
            ent.y = new_y

    def _solid_at(self, cx, cy, w, h):
        """Prueft ob die AABB (cx,cy zentriert, wxh) mit einer Wand kollidiert."""
        x0 = cx - w/2; y0 = cy - h/2
        x1 = cx + w/2; y1 = cy + h/2
        for tx in range(int(x0 // TILE), int(x1 // TILE) + 1):
            for ty in range(int(y0 // TILE), int(y1 // TILE) + 1):
                if self.world.is_blocked(tx, ty):
                    return True
        return False

    def update_enemy(self, e):
        if e.stun > 0:
            e.stun -= 1
            return

        p = self.player
        dist = e.distance_to(p)

        if e.attack_cd > 0: e.attack_cd -= 1
        if e.attack_timer > 0:
            e.attack_timer -= 1

        # "Sicht"-Radius je nach Gegnertyp
        aggro_range = 90 if not e.is_boss else 9999
        if e.kind == "wolf":   aggro_range = 140
        if e.kind == "mage":   aggro_range = 120
        if e.is_elite:         aggro_range = 130

        # AI
        if dist < aggro_range:
            e.state = "chase"
            # Blickrichtung
            if abs(p.x - e.x) > abs(p.y - e.y):
                e.facing = FACE_RIGHT if p.x > e.x else FACE_LEFT
            else:
                e.facing = FACE_DOWN if p.y > e.y else FACE_UP

            # Mage: schiesst Projektile aus der Distanz
            if e.kind == "mage" and dist < 110 and dist > 40 and e.attack_cd <= 0:
                ang = math.atan2(p.y - e.y, p.x - e.x)
                speed = 1.8
                self.projectiles.append(Projectile(e.x, e.y,
                    math.cos(ang)*speed, math.sin(ang)*speed, e.dmg, True))
                e.attack_cd = 70
                return

            # Nahkampfangriff starten
            if dist < e.reach and e.attack_cd <= 0 and e.attack_timer <= 0:
                if e.is_boss:
                    # Boss: deutlich laengeres Wind-up (28 Frames = fast 0.5s)
                    # + laengerer Cooldown (80 Frames) fuer faire Kaempfe
                    e.attack_timer = 28
                    e.attack_cd    = 80
                else:
                    e.attack_timer = 18
                    e.attack_cd    = 50
                return

            # Sonst naeher laufen (wenn nicht im Angriff)
            if e.attack_timer <= 0:
                dx = p.x - e.x
                dy = p.y - e.y
                ln = math.hypot(dx, dy) + 0.01
                mx = dx/ln * e.speed
                my = dy/ln * e.speed
                self.move_with_collision(e, mx, my)
        else:
            e.state = "idle"
            # leichtes wandern
            e.ai_timer -= 1
            if e.ai_timer <= 0:
                e.vx = random.choice([-0.4, 0, 0, 0.4])
                e.vy = random.choice([-0.4, 0, 0, 0.4])
                e.ai_timer = random.randint(30, 80)
            self.move_with_collision(e, e.vx, e.vy)

        e.anim_timer = (e.anim_timer + 1) % 30

        if e.hit_flash > 0:
            e.hit_flash -= 1

    # ------------------------------------------------------------------------
    #  DAMAGE-HANDLING
    # ------------------------------------------------------------------------
    def damage_player(self, dmg):
        p = self.player
        if p.invuln > 0: return
        actual = max(1, dmg - p.defense())
        if p.blocking:
            # Blocken halbiert Schaden und kostet Ausdauer
            actual = max(1, actual // 2)
            p.stamina = max(0, p.stamina - 10)
        p.hp -= actual
        p.invuln = 30
        p.hit_flash = 8
        self.floats.append(FloatingText(p.x, p.y - 10, f"-{actual}", 8))
        self.play(1)
        # Blut-Partikel
        for _ in range(8):
            ang = random.random() * math.tau
            sp = random.uniform(0.5, 2.0)
            self.particles.append(Particle(p.x, p.y,
                math.cos(ang)*sp, math.sin(ang)*sp, 20, 8))

    def damage_enemy(self, e, dmg):
        e.hp -= dmg
        e.hit_flash = 6
        self.floats.append(FloatingText(e.x, e.y - 10, f"-{dmg}", 10))
        # Partikel
        for _ in range(6):
            ang = random.random() * math.tau
            sp = random.uniform(0.5, 2.0)
            self.particles.append(Particle(e.x, e.y,
                math.cos(ang)*sp, math.sin(ang)*sp, 18, 8))
        self.play(1)
        if e.hp <= 0:
            e.alive = False
            self.play(2)
            self.world.defeated_enemies.add((self.world.current_area, e.spawn_id))
            # Clear-Check
            area = self.world.current_area
            killed_in_area = sum(1 for (a, _) in self.world.defeated_enemies if a == area)
            if killed_in_area >= self.total_clearable and self.total_clearable > 0:
                if area not in self.world.cleared_areas:
                    self.world.cleared_areas.add(area)
                    if area in self.world.progression:
                        self.show_msg("Alle Feinde besiegt! Das Portal oeffnet sich!", 180)
                    # Gold-Bonus und Aufleuchten
                    for _ in range(30):
                        ang = random.random() * math.tau
                        sp = random.uniform(1.0, 3.0)
                        self.particles.append(Particle(self.player.x, self.player.y,
                            math.cos(ang)*sp, math.sin(ang)*sp, 40, random.choice([10, 12, 7])))
            gained = self.player.gain_xp(e.drop_xp)
            self.floats.append(FloatingText(e.x, e.y - 16, f"+{e.drop_xp} XP", 10))
            for _ in range(16):
                ang = random.random() * math.tau
                sp = random.uniform(0.8, 2.5)
                self.particles.append(Particle(e.x, e.y,
                    math.cos(ang)*sp, math.sin(ang)*sp, 28, random.choice([8, 2, 5])))
            if e.is_boss:
                boss_name = {"boss_cursed":"Verfluchter Waechter",
                             "boss_fogking":"Nebelkoenig",
                             "boss_king":"Koenig ohne Krone"}[e.kind]
                self.world.defeated_bosses.add(e.kind)
                self.show_msg(f"{boss_name} besiegt!", 150)
                # Endboss-Drop = Krone
                if e.kind == "boss_king":
                    self.player.has_crown = True
                else:
                    # Gold / Item Drop
                    self.player.gold += 100
                    self.floats.append(FloatingText(e.x, e.y - 24, "+100 Gold", 10))
            if gained > 0:
                self.state = STATE_LEVEL_UP
                self.levelup_cursor = 0
                self.play(4)

    # ------------------------------------------------------------------------
    #  INTERAKTION
    # ------------------------------------------------------------------------
    def try_interact(self):
        if not self.near_object: return
        kind, obj = self.near_object
        if kind == "save":
            self.save_point(obj)
        elif kind == "door":
            # Vorwaerts-Portal nur wenn Gebiet geraeumt
            if obj.forward and not self.world.is_portal_open(self.world.current_area):
                remaining = self.total_clearable - sum(
                    1 for (a, _) in self.world.defeated_enemies
                    if a == self.world.current_area)
                self.show_msg(f"Das Portal ist versiegelt. Noch {remaining} Feinde.", 120)
                # Schild-Partikel
                for _ in range(10):
                    ang = random.random() * math.tau
                    self.particles.append(Particle(obj.x, obj.y,
                        math.cos(ang)*1.5, math.sin(ang)*1.5, 20, 8))
                return
            self.play(7)
            self.load_area(obj.target_area, obj.spawn_x, obj.spawn_y)
            self.show_msg(AREA_NAMES[obj.target_area], 120)
        elif kind == "house":
            self.play(7)
            self.load_indoor(obj.tavern_id, obj.x, obj.y)
            self.show_msg("Taverne", 80)
        elif kind == "exit":
            self.play(7)
            self.load_area(self.world.outdoor_return_area,
                           self.world.outdoor_return_pos[0],
                           self.world.outdoor_return_pos[1])
        elif kind == "merchant":
            self.state = STATE_MERCHANT
            self.merchant_cursor = 0
        elif kind == "chest":
            self.open_chest(obj)

    def save_point(self, sp):
        self.play(7)
        p = self.player
        p.hp = p.total_max_hp()
        p.stamina = p.total_max_stamina()
        p.potions = p.max_potions
        # Alle (nicht-Boss-)Gegner respawnen
        self.world.defeated_enemies.clear()
        self.show_msg("Gnadenpunkt aktiviert. Gegner kehren zurueck.", 150)
        # Gegner wieder spawnen
        self.load_area(self.world.current_area, p.x, p.y)

    def open_chest(self, chest):
        chest.opened = True
        self.world.looted_chests.add((self.world.current_area, chest.tx, chest.ty))
        loot = chest.loot
        self.play(6)
        if loot["type"] == "potion":
            self.player.potions = min(self.player.max_potions, self.player.potions + 2)
            self.show_msg("Heiltrank +2", 100)
        elif loot["type"] == "gold":
            self.player.gold += loot["amount"]
            self.show_msg(f"+{loot['amount']} Gold", 100)
        elif loot["type"] == "weapon":
            self.player.inventory.append(("weapon", loot["id"]))
            self.show_msg(f"Gefunden: {WEAPONS[loot['id']]['name']}", 150)
        elif loot["type"] == "armor":
            self.player.inventory.append(("armor", loot["id"]))
            self.show_msg(f"Gefunden: {ARMORS[loot['id']]['name']}", 150)
        elif loot["type"] == "ring":
            self.player.inventory.append(("ring", loot["id"]))
            self.show_msg(f"Gefunden: {RINGS[loot['id']]['name']}", 150)
        # Sparkle
        for _ in range(20):
            ang = random.random() * math.tau
            sp = random.uniform(0.5, 2.0)
            self.particles.append(Particle(chest.x, chest.y,
                math.cos(ang)*sp, math.sin(ang)*sp, 30, 10))

    # ------------------------------------------------------------------------
    #  DIALOG / LEVELUP / MERCHANT / INVENTORY
    # ------------------------------------------------------------------------
    def update_dialog(self):
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_E):
            self.dialog_index += 1
            if self.dialog_index >= len(self.dialog_lines):
                self.state = STATE_PLAYING

    def update_levelup(self):
        if pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_W):
            self.levelup_cursor = (self.levelup_cursor - 1) % 4
        if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
            self.levelup_cursor = (self.levelup_cursor + 1) % 4
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_E):
            stats = ["hp", "stamina", "dmg", "def"]
            self.player.apply_level_up(stats[self.levelup_cursor])
            self.state = STATE_PLAYING
            self.show_msg("Stufe gestiegen!", 80)

    def update_merchant(self):
        if pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_W):
            self.merchant_cursor = (self.merchant_cursor - 1) % (len(self.merchant_stock) + 1)
        if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
            self.merchant_cursor = (self.merchant_cursor + 1) % (len(self.merchant_stock) + 1)
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            if self.merchant_cursor == len(self.merchant_stock):
                self.state = STATE_PLAYING
                return
            typ, iid, price = self.merchant_stock[self.merchant_cursor]
            if self.player.gold >= price:
                self.player.gold -= price
                if typ == "potion":
                    self.player.potions = min(self.player.max_potions, self.player.potions + 1)
                    self.show_msg("Heiltrank gekauft!", 80)
                else:
                    self.player.inventory.append((typ, iid))
                    self.show_msg("Gekauft!", 80)
                self.play(6)
            else:
                self.show_msg("Nicht genug Gold.", 80)
        if pyxel.btnp(pyxel.KEY_E):
            self.state = STATE_PLAYING

    def update_inventory(self):
        total = len(self.player.inventory)
        if pyxel.btnp(pyxel.KEY_I) or pyxel.btnp(pyxel.KEY_ESCAPE):
            self.state = STATE_PLAYING
            return
        if total > 0:
            if pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_W):
                self.inv_cursor = (self.inv_cursor - 1) % total
            if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
                self.inv_cursor = (self.inv_cursor + 1) % total
            if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_E):
                # Ausruesten
                typ, iid = self.player.inventory[self.inv_cursor]
                if typ == "weapon":
                    old = self.player.weapon
                    self.player.weapon = iid
                    self.player.inventory[self.inv_cursor] = ("weapon", old)
                elif typ == "armor":
                    old = self.player.armor
                    self.player.armor = iid
                    self.player.inventory[self.inv_cursor] = ("armor", old)
                elif typ == "ring":
                    old = self.player.ring
                    self.player.ring = iid
                    if old:
                        self.player.inventory[self.inv_cursor] = ("ring", old)
                    else:
                        del self.player.inventory[self.inv_cursor]
                        if self.inv_cursor >= len(self.player.inventory):
                            self.inv_cursor = max(0, len(self.player.inventory) - 1)
                self.show_msg("Ausgeruestet!", 60)

    # ------------------------------------------------------------------------
    #  RESTART
    # ------------------------------------------------------------------------
    def restart_game(self):
        self.world = World()
        self.player = Player(x=4*TILE, y=9*TILE)
        self.load_area(AREA_VILLAGE, 4*TILE, 9*TILE)
        self.state = STATE_PLAYING
        self.show_msg("Neuer Versuch", 100)

    def show_msg(self, text, frames=90):
        self.msg = text
        self.msg_timer = frames

    # ========================================================================
    #  DRAW
    # ========================================================================
    def draw(self):
        pyxel.cls(0)
        if self.state == STATE_MENU:
            self.draw_menu(); return
        if self.state == STATE_GAME_OVER:
            self.draw_game_over(); return
        if self.state == STATE_VICTORY:
            self.draw_victory(); return

        # Welt zeichnen
        self.draw_world()
        self.draw_entities()
        self.draw_particles_and_floats()
        self.draw_hud()

        if self.state == STATE_LEVEL_UP:
            self.draw_levelup()
        elif self.state == STATE_MERCHANT:
            self.draw_merchant()
        elif self.state == STATE_INVENTORY:
            self.draw_inventory()
        elif self.state == STATE_DIALOG:
            self.draw_dialog()

        if self.msg_timer > 0:
            self.draw_msg()

    def draw_menu(self):
        # Hintergrund: dunkel mit Sternen
        pyxel.cls(0)
        # Titel-Partikel
        for i in range(40):
            x = (i * 73 + self.game_tick // 2) % SCREEN_W
            y = (i * 31) % SCREEN_H
            pyxel.pset(x, y, 1 if (i + self.game_tick // 20) % 3 else 5)

        title = "SCHATTEN DER"
        sub   = "ZERBROCHENEN KRONE"
        pyxel.text(SCREEN_W//2 - len(title)*2, 40, title, 8)
        pyxel.text(SCREEN_W//2 - len(sub)*2,   52, sub,   2)

        # Krone-Icon in der Mitte
        self.sprites.blt_alpha("item_crown", SCREEN_W//2 - 8, 64)

        options = ["Spiel beginnen", "Beenden"]
        for i, o in enumerate(options):
            col = 10 if i == self.menu_cursor else 6
            prefix = "> " if i == self.menu_cursor else "  "
            pyxel.text(SCREEN_W//2 - 32, 110 + i * 12, prefix + o, col)

        pyxel.text(10, SCREEN_H - 18, "WASD/Pfeile bewegen  J Angriff", 5)
        pyxel.text(10, SCREEN_H - 10, "K Dash  L Block  H Trank  I Inv", 5)

    def draw_world(self):
        # Sichtbaren Kachel-Bereich berechnen
        mw, mh = self.world.map_size()
        start_tx = int(self.cam_x // TILE)
        start_ty = int(self.cam_y // TILE)
        end_tx   = start_tx + VIEW_TILES_X + 2
        end_ty   = start_ty + VIEW_TILES_Y + 2
        floor    = self.world.theme_floor_sprite()
        is_portal_open = self.world.is_portal_open(self.world.current_area) if not self.world.in_indoor else False
        portal_frame = (self.game_tick // 15) % 2
        fire_frame   = (self.game_tick // 10) % 2
        in_indoor    = self.world.in_indoor

        # Mapping der Haus-Kacheln zu ihren Sprites
        house_sprites = {
            '1': 'tile_roof_l',   '2': 'tile_roof_m',   '3': 'tile_roof_r',
            '4': 'tile_wall_wl',  '5': 'tile_wall_m',   '6': 'tile_wall_wr',
            '7': 'tile_base_l',   '8': 'tile_base_m',   '9': 'tile_base_r',
            'a': 'tile_base_m',
        }

        for ty in range(max(0, start_ty), min(mh, end_ty)):
            for tx in range(max(0, start_tx), min(mw, end_tx)):
                ch = self.world.current_map()[ty][tx]
                sx = tx * TILE - int(self.cam_x)
                sy = ty * TILE - int(self.cam_y)

                # Boden zuerst
                self.sprites.blt_solid(floor, sx, sy)

                if in_indoor:
                    # Indoor-Rendering
                    if ch == 'W':
                        self.sprites.blt_solid("tile_wood_wall", sx, sy)
                    elif ch == 't':
                        self.sprites.blt_alpha("tile_table", sx, sy)
                    elif ch == 'F':
                        self.sprites.blt_alpha("tile_fireplace", sx, sy)
                        # Feuerschein
                        pyxel.circb(sx + 8, sy + 6, 10 + (self.game_tick // 10) % 3, 9)
                    elif ch == 'c':
                        self.sprites.blt_alpha("tile_carpet", sx, sy)
                    elif ch == 'X':
                        # Ausgangstür
                        self.sprites.blt_alpha("tile_house_door", sx, sy)
                        pyxel.rectb(sx - 1, sy - 1, 18, 18, 10)
                    continue

                # Aussenwelt
                if ch == '#':
                    if self.world.current_area == AREA_FOREST:
                        self.sprites.blt_solid("tile_cave_wall", sx, sy)
                    else:
                        self.sprites.blt_solid("tile_wall", sx, sy)
                elif ch == 'T':
                    self.sprites.blt_alpha("tile_tree", sx, sy)
                elif ch == 't':
                    self.sprites.blt_alpha("tile_dead_tree", sx, sy)
                elif ch == '~':
                    self.sprites.blt_solid("tile_water", sx, sy)
                elif ch == 'S':
                    self.sprites.blt_alpha("tile_save", sx, sy)
                    if (self.game_tick // 8) % 3 == 0:
                        pyxel.circb(sx + 8, sy + 4, 10, 10)
                elif ch == 'D':
                    if is_portal_open:
                        spr = "tile_portal_1" if portal_frame == 0 else "tile_portal_2"
                        self.sprites.blt_alpha(spr, sx, sy)
                        pyxel.rectb(sx - 1, sy - 1, 18, 18, 10)
                    else:
                        self.sprites.blt_alpha("tile_portal_lck", sx, sy)
                        pyxel.rectb(sx - 1, sy - 1, 18, 18, 8)
                elif ch in house_sprites:
                    # Haus-Kachel (Dach/Wand/Sockel)
                    self.sprites.blt_alpha(house_sprites[ch], sx, sy)
                elif ch == 'h':
                    # Hauseingang - leuchtender Rahmen und "Betreten"-Pfeil
                    self.sprites.blt_alpha("tile_house_door", sx, sy)
                    pyxel.rectb(sx - 1, sy - 1, 18, 18, 10)
                    if (self.game_tick // 20) % 2:
                        pyxel.text(sx + 6, sy - 7, "v", 10)
                elif ch == 'F':
                    self.sprites.blt_alpha("tile_fence", sx, sy)
                elif ch == 'L':
                    self.sprites.blt_alpha("tile_lantern", sx, sy)
                    pyxel.circb(sx + 8, sy + 4, 10 + (self.game_tick // 15) % 2, 10)
                elif ch == 'g':
                    self.sprites.blt_alpha("tile_grave", sx, sy)
                elif ch == 'r':
                    self.sprites.blt_alpha("tile_rock", sx, sy)
                elif ch == 'f':
                    spr = "tile_campfire_1" if fire_frame == 0 else "tile_campfire_2"
                    self.sprites.blt_alpha(spr, sx, sy)
                    pyxel.circb(sx + 8, sy + 8, 14 + (self.game_tick // 8) % 3, 9)
                elif ch == 'k':
                    self.sprites.blt_alpha("tile_crystal", sx, sy)
                    pyxel.circb(sx + 8, sy + 8, 8 + (self.game_tick // 10) % 3, 12)
                elif ch == 'p':
                    self.sprites.blt_alpha("tile_pumpkin", sx, sy)
                    if (self.game_tick // 20) % 2:
                        pyxel.pset(sx + 5, sy + 8, 10)
                        pyxel.pset(sx + 10, sy + 8, 10)

        # Globaler Atmosphaeren-Overlay (nur draussen)
        if not in_indoor:
            self._draw_atmosphere_overlay()

    def _draw_atmosphere_overlay(self):
        """Gebiets-spezifische Atmosphaere: Nebel, Dunkelheit, Partikel."""
        area = self.world.current_area
        t = self.game_tick

        if area == AREA_VILLAGE:
            # Leichte Pollen/Staub-Teilchen driften durch
            for i in range(8):
                x = (i * 37 + t // 2) % SCREEN_W
                y = ((i * 53 + t // 3) % SCREEN_H)
                pyxel.pset(x, y, 6)

        elif area == AREA_FOREST:
            # Hoehle: dunkle Atmosphaere mit wenigen blauen Funken
            # "Vignette" durch dunkle Pixel an den Raendern
            for i in range(SCREEN_W):
                if (i + t) % 7 == 0:
                    pyxel.pset(i, 0, 1)
                    pyxel.pset(i, SCREEN_H - 1, 1)
            for j in range(SCREEN_H):
                if (j + t) % 7 == 0:
                    pyxel.pset(0, j, 1)
                    pyxel.pset(SCREEN_W - 1, j, 1)
            # Tropf-Partikel simulieren
            for i in range(5):
                x = (i * 53 + t * 2) % SCREEN_W
                y = (i * 23 + t) % SCREEN_H
                pyxel.pset(x, y, 12)

        elif area == AREA_CATACOMB:
            # Halloween: sehr dunkle Vignette + orangene Glut in der Luft
            # Dunkle Ecken-Rahmen
            for i in range(24):
                for j in range(24):
                    if i + j < 22 and ((i + j + t//5) % 3 == 0):
                        pyxel.pset(i, j, 0)
                        pyxel.pset(SCREEN_W - 1 - i, j, 0)
                        pyxel.pset(i, SCREEN_H - 1 - j, 0)
                        pyxel.pset(SCREEN_W - 1 - i, SCREEN_H - 1 - j, 0)
            # Orange "Geisterfunken"
            for i in range(10):
                x = (i * 41 + t) % SCREEN_W
                y = (i * 29 - t // 2) % SCREEN_H
                pyxel.pset(x, y, random.choice([9, 2, 4]))
            # Gelegentliches "Blitz" der Stimmung (kuerzer Helligkeitswechsel)
            if (t // 300) % 4 == 0 and t % 300 < 5:
                # kurzer Aufblitzen-Effekt (nur erste paar Frames jedes Zyklus)
                for _ in range(30):
                    x = random.randint(0, SCREEN_W - 1)
                    y = random.randint(0, SCREEN_H - 1)
                    pyxel.pset(x, y, 10)

        elif area == AREA_FORTRESS:
            # Schwarze Festung: rote Glut / Asche in der Luft
            for i in range(12):
                x = (i * 31 + t) % SCREEN_W
                y = ((i * 17 - t) % SCREEN_H)
                pyxel.pset(x, y, random.choice([2, 8, 4]))
            # Starke Vignette
            for i in range(30):
                if (i + t//3) % 4 == 0:
                    pyxel.pset(i, i // 2, 0)
                    pyxel.pset(SCREEN_W - 1 - i, i // 2, 0)
                    pyxel.pset(i, SCREEN_H - 1 - i // 2, 0)
                    pyxel.pset(SCREEN_W - 1 - i, SCREEN_H - 1 - i // 2, 0)

    def draw_entities(self):
        p = self.player

        drawables = []
        for c in self.chests:
            if not c.opened:
                drawables.append((c.y, "chest", c))
        for m in self.merchants:
            drawables.append((m.y, "merchant", m))
        for n in self.npcs:
            drawables.append((n.y, "npc", n))
        for e in self.enemies:
            drawables.append((e.y, "enemy", e))
        drawables.append((p.y, "player", p))
        drawables.sort(key=lambda t: t[0])

        for _, kind, obj in drawables:
            if kind == "chest":
                self._draw_chest(obj)
            elif kind == "merchant":
                self._draw_merchant(obj)
            elif kind == "npc":
                self._draw_npc(obj)
            elif kind == "enemy":
                self._draw_enemy(obj)
            elif kind == "player":
                self._draw_player(obj)

        for pr in self.projectiles:
            sx = int(pr.x - self.cam_x)
            sy = int(pr.y - self.cam_y)
            col = 14 if pr.owner_is_enemy else 10
            pyxel.circb(sx, sy, 3, col)
            pyxel.pset(sx, sy, 7)

    def _draw_npc(self, n):
        sx = int(n.x - 8 - self.cam_x)
        sy = int(n.y - 10 - self.cam_y)
        pyxel.ellib(int(n.x - 5 - self.cam_x), int(n.y + 5 - self.cam_y), 10, 3, 1)
        self.sprites.blt_alpha(n.sprite_name, sx, sy)

    def _draw_chest(self, c):
        sx = int(c.x - 7 - self.cam_x)
        sy = int(c.y - 7 - self.cam_y)
        # Simple Truhe: Holzkiste mit Gold-Rand
        pyxel.rect(sx, sy, 14, 12, 4)
        pyxel.rectb(sx, sy, 14, 12, 9)
        pyxel.line(sx, sy+4, sx+13, sy+4, 9)
        pyxel.pset(sx + 7, sy + 7, 10)
        # Glitzer-Animation
        if (self.game_tick // 10) % 4 == 0:
            pyxel.pset(sx + random.randint(0, 13), sy - 2, 10)

    def _draw_merchant(self, m):
        sx = int(m.x - 8 - self.cam_x)
        sy = int(m.y - 10 - self.cam_y)
        self.sprites.blt_alpha("npc_merchant", sx, sy)
        # "!" ueber Kopf
        if (self.game_tick // 20) % 2:
            pyxel.text(sx + 6, sy - 8, "!", 10)

    def _draw_enemy(self, e):
        sx = int(e.x - 8 - self.cam_x)
        sy = int(e.y - 10 - self.cam_y)

        # Schatten
        pyxel.ellib(int(e.x - 6 - self.cam_x), int(e.y + 5 - self.cam_y), 12, 4, 1)

        # hit-flash
        if e.hit_flash > 0 and e.hit_flash % 2 == 0:
            # Weiss-Flash: zeichne weisses Rechteck
            pyxel.rect(sx + 2, sy + 2, 12, 14, 7)
            return

        self.sprites.blt_alpha(e.sprite, sx, sy)

        # Angriff-Indikator: kleiner Wisch
        if e.attack_timer > 6 and e.attack_timer < 14:
            rx = int(e.x - self.cam_x)
            ry = int(e.y - self.cam_y)
            if e.facing == FACE_RIGHT:
                pyxel.line(rx, ry, rx + e.reach, ry, 8)
            elif e.facing == FACE_LEFT:
                pyxel.line(rx, ry, rx - e.reach, ry, 8)
            elif e.facing == FACE_UP:
                pyxel.line(rx, ry, rx, ry - e.reach, 8)
            else:
                pyxel.line(rx, ry, rx, ry + e.reach, 8)

        # HP-Balken
        if e.hp < e.max_hp:
            w = 16
            ratio = e.hp / e.max_hp
            bar_x = int(e.x - w/2 - self.cam_x)
            bar_y = int(e.y - 14 - self.cam_y)
            pyxel.rect(bar_x, bar_y, w, 2, 0)
            pyxel.rect(bar_x, bar_y, int(w * ratio), 2, 8)

        # Boss-HP oben am Bildschirm
        if e.is_boss:
            self._draw_boss_bar(e)

        # Elite-Indikator
        if e.is_elite:
            pyxel.text(int(e.x - 6 - self.cam_x), int(e.y - 20 - self.cam_y), "ELITE", 9)

    def _draw_boss_bar(self, e):
        bar_w = SCREEN_W - 40
        ratio = max(0.0, e.hp / e.max_hp)
        pyxel.rect(20, SCREEN_H - 22, bar_w, 6, 0)
        pyxel.rectb(20, SCREEN_H - 22, bar_w, 6, 5)
        pyxel.rect(20, SCREEN_H - 22, int(bar_w * ratio), 6, 2)
        name = {"boss_cursed":"Verfluchter Waechter",
                "boss_fogking":"Nebelkoenig",
                "boss_king":"Koenig ohne Krone"}[e.kind]
        pyxel.text(20, SCREEN_H - 30, name, 7)

    def _draw_player(self, p):
        sx = int(p.x - 8 - self.cam_x)
        sy = int(p.y - 10 - self.cam_y)

        # Kraeftiger Schatten unter dem Ritter
        pyxel.elli(int(p.x - 7 - self.cam_x), int(p.y + 4 - self.cam_y), 14, 5, 1)
        pyxel.ellib(int(p.x - 7 - self.cam_x), int(p.y + 4 - self.cam_y), 14, 5, 0)

        # Beim Dash: "Ghost"-Trail
        if p.dash_timer > 0:
            for k in range(1, 4):
                tx = sx - int(p.vx * k * 3)
                ty = sy - int(p.vy * k * 3)
                # Nur wenn die Position halbwegs nahe ist
                self.sprites.blt_alpha(self._hero_sprite(p), tx, ty)

        # Hit-Flash
        if p.hit_flash > 0 and p.hit_flash % 2 == 0:
            pyxel.rect(sx + 2, sy + 2, 12, 14, 7)
        else:
            self.sprites.blt_alpha(self._hero_sprite(p), sx, sy)

        # Attack-Swing-Effekt
        if p.attack_timer > 0:
            self._draw_attack_swing(p)

        # Block-Schild
        if p.blocking:
            self._draw_block_shield(p)

    def _hero_sprite(self, p):
        frame = (p.anim_timer // 12) % 2 if (p.vx != 0 or p.vy != 0) else 0
        if p.facing == FACE_DOWN:
            return "hero_down_1" if frame == 0 else "hero_down_2"
        if p.facing == FACE_UP:
            return "hero_up_1" if frame == 0 else "hero_up_2"
        if p.facing == FACE_RIGHT:
            return "hero_right_1" if frame == 0 else "hero_right_2"
        return "hero_left_1" if frame == 0 else "hero_left_2"

    def _draw_attack_swing(self, p):
        """Zeichnet einen Schwert-Wisch als Partikel-Arc."""
        progress = 1.0 - (p.attack_timer / 12.0)
        px = int(p.x - self.cam_x)
        py = int(p.y - self.cam_y)
        reach = 16
        if p.facing == FACE_RIGHT:
            cx, cy, start = px + 8, py, -math.pi/2
        elif p.facing == FACE_LEFT:
            cx, cy, start = px - 8, py, math.pi/2
        elif p.facing == FACE_UP:
            cx, cy, start = px, py - 8, math.pi
        else:
            cx, cy, start = px, py + 8, 0

        ang = start + progress * math.pi * 0.8
        for k in range(5):
            a = ang + k * 0.1
            ex = cx + math.cos(a) * reach
            ey = cy + math.sin(a) * reach
            col = 7 if k < 2 else 6
            pyxel.pset(int(ex), int(ey), col)

    def _draw_block_shield(self, p):
        px = int(p.x - self.cam_x)
        py = int(p.y - self.cam_y)
        dx, dy = 0, 10
        if p.facing == FACE_RIGHT: dx, dy = 10, 0
        if p.facing == FACE_LEFT:  dx, dy = -10, 0
        if p.facing == FACE_UP:    dx, dy = 0, -10
        pyxel.circb(px + dx, py + dy, 6, 6)
        pyxel.circb(px + dx, py + dy, 5, 12)

    def draw_particles_and_floats(self):
        for pa in self.particles:
            sx = int(pa.x - self.cam_x)
            sy = int(pa.y - self.cam_y)
            if pa.size <= 1:
                pyxel.pset(sx, sy, pa.color)
            else:
                pyxel.rect(sx, sy, pa.size, pa.size, pa.color)
        for ft in self.floats:
            sx = int(ft.x - len(ft.text)*2 - self.cam_x)
            sy = int(ft.y - self.cam_y)
            pyxel.text(sx + 1, sy + 1, ft.text, 0)
            pyxel.text(sx, sy, ft.text, ft.color)

    # ---- HUD ----------------------------------------------------------------
    def draw_hud(self):
        p = self.player
        # HP-Balken
        pyxel.rect(4, 4, 80, 6, 0)
        pyxel.rectb(4, 4, 80, 6, 5)
        hp_r = max(0.0, p.hp / p.total_max_hp())
        pyxel.rect(4, 4, int(80 * hp_r), 6, 8)
        pyxel.text(5, 5, f"HP {int(p.hp)}/{p.total_max_hp()}", 7)

        # Stamina
        pyxel.rect(4, 12, 60, 4, 0)
        pyxel.rectb(4, 12, 60, 4, 5)
        st_r = p.stamina / p.total_max_stamina()
        pyxel.rect(4, 12, int(60 * st_r), 4, 11)

        # XP
        pyxel.rect(4, 18, 60, 3, 0)
        xr = p.xp / p.xp_to_next
        pyxel.rect(4, 18, int(60 * xr), 3, 10)

        # Infos rechts oben
        pyxel.text(SCREEN_W - 64, 4,  f"Stufe  {p.level}", 7)
        pyxel.text(SCREEN_W - 64, 12, f"Gold   {p.gold}", 10)
        pyxel.text(SCREEN_W - 64, 20, f"Trank  {p.potions}/{p.max_potions}", 14)

        # Gebiet + Clear-Fortschritt
        area_name = "Taverne" if self.world.in_indoor else AREA_NAMES[self.world.current_area]
        pyxel.text(SCREEN_W//2 - len(area_name)*2, 4, area_name, 6)
        if self.total_clearable > 0 and not self.world.in_indoor:
            killed = sum(1 for (a, _) in self.world.defeated_enemies
                         if a == self.world.current_area)
            status = f"{killed}/{self.total_clearable} Feinde"
            col = 10 if killed >= self.total_clearable else 6
            pyxel.text(SCREEN_W//2 - len(status)*2, 12, status, col)

        # Prompt fuer Interact
        if self.near_object:
            kind, obj = self.near_object
            if kind == "door":
                if obj.forward and not self.world.is_portal_open(self.world.current_area):
                    prompt = "Portal versiegelt"
                    col = 8
                else:
                    prompt = "E: Portal betreten"
                    col = 10
            elif kind == "house":
                prompt = "E: Taverne betreten"
                col = 10
            elif kind == "exit":
                prompt = "E: Hinaus"
                col = 10
            else:
                prompt = {"save":"E: Rasten",
                          "merchant":"E: Handeln",
                          "chest":"E: Oeffnen"}[kind]
                col = 10
            pyxel.text(SCREEN_W//2 - len(prompt)*2, SCREEN_H - 38, prompt, col)

    def draw_msg(self):
        if not self.msg: return
        w = len(self.msg) * 4 + 8
        x = SCREEN_W//2 - w//2
        y = 30
        pyxel.rect(x, y, w, 10, 0)
        pyxel.rectb(x, y, w, 10, 10)
        pyxel.text(x + 4, y + 3, self.msg, 7)

    # ---- Overlays ----------------------------------------------------------
    def draw_levelup(self):
        p = self.player
        pyxel.rect(40, 40, SCREEN_W - 80, SCREEN_H - 80, 0)
        pyxel.rectb(40, 40, SCREEN_W - 80, SCREEN_H - 80, 10)
        pyxel.text(60, 48, "STUFENAUFSTIEG", 10)
        pyxel.text(60, 60, f"Du bist Stufe {p.level}.", 7)
        pyxel.text(60, 68, "Waehle eine Verbesserung:", 6)
        options = [
            f"Leben      +15  (aktuell: {p.max_hp})",
            f"Ausdauer   +10  (aktuell: {p.max_stamina})",
            f"Schaden    +3   (aktuell: {p.base_dmg})",
            f"Vert.      +2   (aktuell: {p.base_def})",
        ]
        for i, o in enumerate(options):
            col = 10 if i == self.levelup_cursor else 7
            prefix = "> " if i == self.levelup_cursor else "  "
            pyxel.text(60, 82 + i * 10, prefix + o, col)
        pyxel.text(60, SCREEN_H - 50, "ENTER bestaetigen", 6)

    def draw_merchant(self):
        pyxel.rect(20, 20, SCREEN_W - 40, SCREEN_H - 40, 0)
        pyxel.rectb(20, 20, SCREEN_W - 40, SCREEN_H - 40, 9)
        pyxel.text(30, 26, "HAENDLER", 9)
        pyxel.text(30, 36, f"Dein Gold: {self.player.gold}", 10)
        y = 50
        for i, (typ, iid, price) in enumerate(self.merchant_stock):
            col = 10 if i == self.merchant_cursor else 7
            prefix = "> " if i == self.merchant_cursor else "  "
            if typ == "potion":
                name = "Heiltrank"
            elif typ == "weapon":
                name = WEAPONS[iid]["name"]
            elif typ == "armor":
                name = ARMORS[iid]["name"]
            elif typ == "ring":
                name = RINGS[iid]["name"]
            pyxel.text(30, y + i * 10, f"{prefix}{name:<20} {price} G", col)
        # Exit
        i = len(self.merchant_stock)
        col = 10 if i == self.merchant_cursor else 7
        prefix = "> " if i == self.merchant_cursor else "  "
        pyxel.text(30, y + i * 10, prefix + "Verlassen", col)
        pyxel.text(30, SCREEN_H - 30, "ENTER kaufen  E zurueck", 6)

    def draw_inventory(self):
        p = self.player
        pyxel.rect(20, 20, SCREEN_W - 40, SCREEN_H - 40, 0)
        pyxel.rectb(20, 20, SCREEN_W - 40, SCREEN_H - 40, 12)
        pyxel.text(30, 26, "INVENTAR & AUSRUESTUNG", 12)

        # Equipped
        pyxel.text(30, 40, f"Waffe:    {WEAPONS[p.weapon]['name']}  (+{WEAPONS[p.weapon]['dmg']} Sch)", 10)
        pyxel.text(30, 48, f"Ruestung: {ARMORS[p.armor]['name']}  (+{ARMORS[p.armor]['def']} Vert)", 10)
        ring = RINGS[p.ring]['name'] if p.ring else "-keiner-"
        effect = RINGS[p.ring]['effect'] if p.ring else ""
        pyxel.text(30, 56, f"Ring:     {ring}  {effect}", 10)

        pyxel.text(30, 68, "Stats:", 6)
        pyxel.text(30, 76, f"Schaden {p.damage()}   Vert. {p.defense()}   HP {p.total_max_hp()}", 7)

        pyxel.text(30, 92, "Inventar:", 6)
        if not p.inventory:
            pyxel.text(30, 102, "  (leer)", 5)
        else:
            for i, (typ, iid) in enumerate(p.inventory[:7]):
                if typ == "weapon":   name = WEAPONS[iid]["name"]
                elif typ == "armor":  name = ARMORS[iid]["name"]
                elif typ == "ring":   name = RINGS[iid]["name"]
                else:                 name = iid
                col = 10 if i == self.inv_cursor else 7
                prefix = "> " if i == self.inv_cursor else "  "
                pyxel.text(30, 102 + i * 8, f"{prefix}{typ:<6} {name}", col)

        pyxel.text(30, SCREEN_H - 30, "ENTER ausruesten  I schliessen", 6)

    def draw_dialog(self):
        # Dialogbox unten
        pyxel.rect(10, SCREEN_H - 56, SCREEN_W - 20, 46, 0)
        pyxel.rectb(10, SCREEN_H - 56, SCREEN_W - 20, 46, 7)
        if self.dialog_index < len(self.dialog_lines):
            pyxel.text(16, SCREEN_H - 48, self.dialog_lines[self.dialog_index], 7)
        pyxel.text(16, SCREEN_H - 18, "ENTER weiter", 5)

    def draw_game_over(self):
        pyxel.cls(0)
        # Blut-Effekt
        for i in range(50):
            x = (i * 53 + self.game_tick) % SCREEN_W
            y = (i * 17) % SCREEN_H
            pyxel.pset(x, y, 2)
        t = "DU BIST GEFALLEN"
        pyxel.text(SCREEN_W//2 - len(t)*2, SCREEN_H//2 - 20, t, 8)
        pyxel.text(SCREEN_W//2 - 40, SCREEN_H//2, "Die Dunkelheit nimmt dich...", 5)
        pyxel.text(SCREEN_W//2 - 40, SCREEN_H//2 + 20, "ENTER = Neuer Versuch", 7)

    def draw_victory(self):
        t = self.cutscene_timer
        pyxel.cls(0)

        # --- THRONSAAL-HINTERGRUND ---
        # Himmel/Decke mit Gradient (dunkelblau -> violett -> schwarz)
        for y in range(50):
            if y < 15:
                col = 1
            elif y < 30:
                col = 13
            else:
                col = 0
            pyxel.line(0, y, SCREEN_W, y, col)
            # Sterne-Flimmern
            if (y * 7 + t) % 23 == 0:
                pyxel.pset((y * 13 + t // 2) % SCREEN_W, y, 7)

        # Säulen links und rechts (3D-Tiefe angedeutet)
        for col_x in [22, 58, SCREEN_W - 62, SCREEN_W - 26]:
            # Kapitell oben
            pyxel.rect(col_x - 2, 44, 14, 4, 6)
            pyxel.rect(col_x - 1, 48, 12, 2, 5)
            # Säulenschaft
            pyxel.rect(col_x, 50, 10, SCREEN_W, 6)
            pyxel.rect(col_x + 1, 50, 2, SCREEN_H, 7)   # Highlight
            pyxel.rect(col_x + 7, 50, 2, SCREEN_H, 5)   # Schatten
            # Basis unten
            pyxel.rect(col_x - 2, SCREEN_H - 30, 14, 4, 6)

        # Boden mit Schachbrettmuster
        for row in range(SCREEN_H - 30, SCREEN_H, 4):
            for col in range(0, SCREEN_W, 16):
                offset = (row // 4) % 2 * 8
                pyxel.rect(col + offset, row, 8, 4, 4 if (col + row) % 16 else 5)

        # Roter Teppich zur Mitte
        carpet_top = 90
        for y in range(carpet_top, SCREEN_H - 20):
            width = int((y - carpet_top) * 1.2) + 20
            cx = SCREEN_W // 2
            pyxel.rect(cx - width // 2, y, width, 1, 2)
            pyxel.pset(cx - width // 2, y, 8)
            pyxel.pset(cx + width // 2 - 1, y, 8)

        # --- THRON ---
        throne_cx = SCREEN_W // 2
        throne_top = SCREEN_H - 90
        # Rückenlehne
        pyxel.rect(throne_cx - 18, throne_top, 36, 50, 5)
        pyxel.rect(throne_cx - 18, throne_top, 36, 3, 9)   # Gold-Rand oben
        pyxel.rect(throne_cx - 18, throne_top + 47, 36, 3, 9)
        # Zinnen oben am Thron
        for i in range(5):
            xp = throne_cx - 16 + i * 8
            pyxel.rect(xp, throne_top - 5, 4, 5, 9)
            pyxel.pset(xp + 1, throne_top - 7, 10)
        # Sitzfläche
        pyxel.rect(throne_cx - 20, throne_top + 35, 40, 20, 4)
        pyxel.rect(throne_cx - 20, throne_top + 35, 40, 2, 9)
        # Seitliche Lehnen
        pyxel.rect(throne_cx - 22, throne_top + 30, 4, 15, 5)
        pyxel.rect(throne_cx + 18, throne_top + 30, 4, 15, 5)

        # Lichtstrahlen in Phase 3 (ab t=400)
        if t > 400:
            rays = 16
            for i in range(rays):
                ang = (i / rays) * math.tau + t * 0.015
                length = 70 + int(math.sin(t * 0.06 + i) * 8)
                ex = throne_cx + int(math.cos(ang) * length)
                ey = (throne_top + 20) + int(math.sin(ang) * length)
                col = 10 if i % 2 == 0 else 9
                pyxel.line(throne_cx, throne_top + 20, ex, ey, col)

        # --- RITTER VOR DEM THRON ---
        hero_x = throne_cx - 8
        hero_y = throne_top + 35
        if t < 400:
            # Stehend
            self.sprites.blt_alpha("hero_down_1", hero_x, hero_y)
        else:
            # Kniend - tieferes Sprite + Knie-Andeutung
            self.sprites.blt_alpha("hero_down_2", hero_x, hero_y + 4)
            pyxel.rect(hero_x + 3, hero_y + 16, 4, 3, 0)
            pyxel.rect(hero_x + 9, hero_y + 16, 4, 3, 0)

        # --- KRONE ANIMATION ---
        crown_target_y = hero_y - 6
        if t < 120:
            # Phase 1: Krone noch nicht sichtbar
            pass
        elif t < 400:
            # Phase 2: Krone schwebt von oben herab
            progress = (t - 120) / 280.0
            crown_y = int(10 + (crown_target_y - 10) * progress)
            # Leuchteffekt-Ringe um die Krone
            for r in range(3):
                glow_r = 10 + r + int(math.sin(t * 0.2) * 2)
                pyxel.circb(throne_cx, crown_y + 8, glow_r, 10)
            self.sprites.blt_alpha("item_crown", throne_cx - 8, crown_y)
            # Partikel-Schweif
            if t % 3 == 0:
                self.particles.append(Particle(
                    throne_cx + random.randint(-6, 6), crown_y + 10,
                    random.uniform(-0.3, 0.3), random.uniform(0.2, 0.8),
                    30, random.choice([10, 9, 7])))
        else:
            # Phase 3: Krone sitzt auf dem Kopf
            for r in range(5):
                if (t + r * 3) % 4 == 0:
                    pyxel.circb(throne_cx, crown_target_y + 6, 6 + r * 3, 10)
            self.sprites.blt_alpha("item_crown", throne_cx - 8, crown_target_y)
            # Goldene Funken regnen
            if t % 2 == 0:
                for _ in range(2):
                    ang = random.random() * math.tau
                    sp = random.uniform(0.5, 2.0)
                    self.particles.append(Particle(
                        throne_cx + random.randint(-30, 30),
                        crown_target_y + 8,
                        math.cos(ang)*sp, math.sin(ang)*sp + 0.5,
                        50, random.choice([10, 9, 7])))

        # Partikel updaten und rendern
        for pa in self.particles:
            pa.update()
        self.particles = [pa for pa in self.particles if pa.life > 0]
        for pa in self.particles:
            pyxel.pset(int(pa.x), int(pa.y), pa.color)

        # --- TEXT-OVERLAYS (Phasen-weise) ---
        if 60 < t < 160:
            self._cutscene_text("Der Koenig ohne Krone ist gefallen.", 58, 7)
        elif 180 < t < 280:
            self._cutscene_text("Du trittst vor den leeren Thron...", 58, 7)
        elif 300 < t < 400:
            self._cutscene_text("...und die Krone erkennt ihren Traeger.", 58, 10)
        elif 420 < t < 600:
            self._cutscene_text("Das Koenigreich erwacht.", 50, 10)
            self._cutscene_text("Schatten werden Licht.", 62, 9)
        elif t >= 620:
            # Finaler Titel
            pyxel.rect(0, 30, SCREEN_W, 40, 0)
            pyxel.rectb(0, 30, SCREEN_W, 40, 10)
            t1 = "DU BIST NUN"
            t2 = "DER HERRSCHER"
            t3 = "DER WELT"
            self._cutscene_text(t1, 36, 10)
            self._cutscene_text(t2, 48, 8)
            self._cutscene_text(t3, 60, 8)

        # Skip-Hinweis
        if 180 < t < 880 and (t // 30) % 2:
            pyxel.text(SCREEN_W - 76, SCREEN_H - 10, "ENTER = weiter", 5)

        # Fade-Out am Ende
        if t > 850:
            fade = min(1.0, (t - 850) / 50.0)
            count = int(fade * 400)
            for _ in range(count):
                pyxel.pset(random.randint(0, SCREEN_W - 1),
                           random.randint(0, SCREEN_H - 1), 0)

    def _cutscene_text(self, text, y, color):
        x = SCREEN_W // 2 - len(text) * 2
        # Schatten für Lesbarkeit
        pyxel.text(x + 1, y + 1, text, 0)
        pyxel.text(x, y, text, color)


# ============================================================================
#  START
# ============================================================================

if __name__ == "__main__":
    Game()