import pyxel
import random
from dataclasses import dataclass, field

SCREEN_W = 512
SCREEN_H = 512
ROAD_LEFT   = 106
ROAD_RIGHT  = 406
ROAD_W      = ROAD_RIGHT - ROAD_LEFT
ROAD_CENTER = (ROAD_LEFT + ROAD_RIGHT) // 2
LANES = [
    int(ROAD_LEFT + ROAD_W * 0.20),
    int(ROAD_LEFT + ROAD_W * 0.50),
    int(ROAD_LEFT + ROAD_W * 0.80),
]

STATE_TITLE       = 0
STATE_SELECT_TYPE = 1
STATE_GARAGE      = 2
STATE_SELECT_MAP  = 3
STATE_PLAY        = 4
STATE_GAME_OVER   = 5
STATE_2P_PLAY     = 6
STATE_SELECT_MODE = 7

PU_SHIELD = "shield"
PU_NITRO  = "nitro"
PU_OIL    = "oil_trap"
PU_REPAIR = "repair"
PU_TIME   = "slowmo"

PU_DURATION = {PU_SHIELD:300, PU_NITRO:180, PU_TIME:180}
PU_COLORS   = {PU_SHIELD:12, PU_NITRO:9, PU_OIL:2, PU_REPAIR:11, PU_TIME:10}
PU_LABELS   = {PU_SHIELD:"SCH", PU_NITRO:"NIT", PU_OIL:"OIL", PU_REPAIR:"REP", PU_TIME:"SLO"}

FONT = {
    "A":["011","101","111","101","101"],"B":["110","101","110","101","110"],
    "C":["011","100","100","100","011"],"D":["110","101","101","101","110"],
    "E":["111","100","110","100","111"],"F":["111","100","110","100","100"],
    "G":["011","100","111","101","011"],"H":["101","101","111","101","101"],
    "I":["111","010","010","010","111"],"J":["001","001","001","101","010"],
    "K":["101","110","100","110","101"],"L":["100","100","100","100","111"],
    "M":["101","111","111","101","101"],"N":["101","111","111","101","101"],
    "O":["010","101","101","101","010"],"P":["110","101","110","100","100"],
    "Q":["010","101","101","111","001"],"R":["110","101","110","110","101"],
    "S":["011","100","010","001","110"],"T":["111","010","010","010","010"],
    "U":["101","101","101","101","010"],"V":["101","101","101","010","010"],
    "W":["101","101","111","111","101"],"X":["101","010","010","010","101"],
    "Y":["101","010","010","010","010"],"Z":["111","001","010","100","111"],
    "0":["010","101","101","101","010"],"1":["010","110","010","010","111"],
    "2":["110","001","010","100","111"],"3":["110","001","011","001","110"],
    "4":["101","101","111","001","001"],"5":["111","100","110","001","110"],
    "6":["010","100","110","101","010"],"7":["111","001","010","010","010"],
    "8":["010","101","010","101","010"],"9":["010","101","011","001","010"],
    "!":["010","010","010","000","010"],":":["000","010","000","010","000"],
    "/":["001","001","010","100","100"],"-":["000","000","111","000","000"],
    ".":["000","000","000","000","010"]," ":["000","000","000","000","000"],
    "<":["001","010","100","010","001"],">":["100","010","001","010","100"],
    "|":["010","010","010","010","010"],"x":["101","010","010","010","101"],
}

# FIX 1: Single definition of _r helper — the duplicate at the bottom of the
# sprite section has been removed. One definition here covers the whole file.
def _r(x, y, w, h, c):
    pyxel.rect(x, y, w, h, c)

def big_text(txt, x, y, col, scale=2):
    cx = x
    for ch in txt.upper():
        if ch not in FONT:
            cx += (3+1)*scale; continue
        rows = FONT[ch]
        for ry, row in enumerate(rows):
            for rx, bit in enumerate(row):
                if bit=="1": pyxel.rect(cx+rx*scale, y+ry*scale, scale, scale, col)
        cx += (len(rows[0])+1)*scale

def big_text_w(txt, scale=2):
    w=0
    for ch in txt.upper():
        rows = FONT.get(ch,["000"])
        w += (len(rows[0])+1)*scale
    return w

def clamp(v,lo,hi): return max(lo,min(hi,v))
def rects_overlap(ax,ay,aw,ah,bx,by,bw,bh):
    return ax<bx+bw and ax+aw>bx and ay<by+bh and ay+ah>by

# ===================================================
#  DATACLASSES
# ===================================================

@dataclass
class Vehicle:
    name: str
    vtype: str
    max_speed: float
    accel: float
    brake: float
    handling: float
    hp: int
    width: int
    height: int
    col_body: int
    col_window: int
    price: int
    description: str

@dataclass
class ObstacleType:
    kind: str; w: int; h: int; damage: int
    speed_penalty: float; police_penalty: float; weight: int

@dataclass
class Obstacle:
    kind: str; x: float; y: float; w: int; h: int
    damage: int; speed_penalty: float; police_penalty: float; lane: int

@dataclass
class PowerUp:
    kind: str; x: float; y: float

@dataclass
class Coin:
    x: float; y: float; bob: int = 0

@dataclass
class OncomingCar:
    x: float; y: float; lane: int; speed: float; col: int; w: int = 12; h: int = 20

@dataclass
class Tumbleweed:
    x: float; y: float; vx: float; spin: int = 0; size: int = 10

@dataclass
class Particle:
    x: float; y: float; vx: float; vy: float; col: int; life: int; max_life: int

@dataclass
class MapSpec:
    name: str; bg_color: int; side_color: int; shoulder_color: int
    lane_color: int; deco_color: int; road_color: int
    police_pressure: float; spawn_modifier: int; description: str
    deco_style: str; obstacle_pool: list; difficulty: str; diff_color: int

# ===================================================
#  GARAGE  (persistent coin + unlock state)
# ===================================================

class Garage:
    def __init__(self):
        self.coins       = 0
        self.unlocked    = {"car": {0}, "moto": {0}}
        self.selected    = {"car": 0, "moto": 0}
        self.vtype       = "car"

    def can_afford(self, v_idx, vtype):
        pool = CAR_FLEET if vtype=="car" else MOTO_FLEET
        if v_idx >= len(pool): return False
        return self.coins >= pool[v_idx].price

    def buy(self, v_idx, vtype):
        pool = CAR_FLEET if vtype=="car" else MOTO_FLEET
        if v_idx >= len(pool): return False
        v = pool[v_idx]
        if self.coins >= v.price and v_idx not in self.unlocked[vtype]:
            self.coins -= v.price
            self.unlocked[vtype].add(v_idx)
            return True
        return False

    def current_vehicle(self):
        pool = CAR_FLEET if self.vtype=="car" else MOTO_FLEET
        idx  = self.selected[self.vtype]
        return pool[idx]

# ===================================================
#  SOUND MANAGER
# ===================================================

class SoundManager:
    def __init__(self):
        self._setup_sounds()
        self.engine_pitch  = 0
        self.siren_timer   = 0
        self.engine_timer  = 0

    def _setup_sounds(self):
        pyxel.sounds[0].set("c2e2g2c3","t","4","f",20)
        pyxel.sounds[1].set("f1c1a0","n","7","f",8)
        pyxel.sounds[2].set("c3e3g3c4","t","4","n",6)
        pyxel.sounds[3].set("g2c3e3g3","t","5","n",8)
        pyxel.sounds[4].set("a3f3a3f3","s","6","n",10)
        pyxel.sounds[5].set("c3e3g3c4e4g4","t","6","n",12)
        pyxel.sounds[6].set("c4g3e3c3","t","5","f",8)
        # --- Motorensounds (kurze "Hochdreh"-/Schaltsounds beim Gasgeben) ---
        # Auto: kurzes tiefes Hochdrehen
        pyxel.sounds[7].set("c1e1g1","t","3","f",6)
        # Auto bei Nitro/hoher Drehzahl: hoeheres, kraeftigeres Hochdrehen
        pyxel.sounds[8].set("e1g1c2","t","4","f",5)
        # Motorrad: helleres, schnelleres Hochdrehen
        pyxel.sounds[9].set("e1a1e2","t","3","f",5)
        # Motorrad bei Nitro/hoher Drehzahl: scharfes Hochdrehen
        pyxel.sounds[10].set("a1e2a2","t","4","f",4)
        # --- UI Sounds ---
        # Navigations-Klick (Pfeiltasten, Wechsel)
        pyxel.sounds[11].set("c3","s","2","f",4)
        # Bestaetigung / Auswahl (Enter, weiter)
        pyxel.sounds[12].set("c3e3g3","t","4","n",5)
        # Kauf-Erfolg (Fahrzeug freigeschaltet)
        pyxel.sounds[13].set("e3g3c4e4","t","5","n",7)
        # --- Distinkte Powerup-Sounds ---
        # Nitro: schneller Aufwaerts-Sweep, energisch
        pyxel.sounds[14].set("c3e3g3c4e4","s","6","f",6)
        # Oel-Falle: tiefes, "schmieriges" Absacken
        pyxel.sounds[15].set("g2e2c2a1","t","5","f",9)
        # Reparatur: warmer, heilender Zweiklang aufwaerts
        pyxel.sounds[16].set("c3g3c4","t","4","n",8)
        # Schild aktiviert: glasiger, schwebender Klang
        pyxel.sounds[17].set("e3b3e4","t","3","s",10)
        # --- Weitere Spiel-Events ---
        # Schild blockt Treffer ab: kurzer metallischer "Abprall"
        pyxel.sounds[18].set("c4g3","p","6","f",5)
        # Combo-Stufe erreicht: heller, kurzer Belohnungston
        pyxel.sounds[19].set("g3c4","s","4","n",4)
        # Warnung bei kritischem HP / wenig Distanz zur Polizei
        pyxel.sounds[20].set("a2f2","p","5","f",7)

    def play_crash(self):
        pyxel.play(1, 1)

    def play_coin(self):
        pyxel.play(2, 2)

    def play_powerup(self):
        pyxel.play(3, 3)

    def play_siren(self, police_dist):
        """Sirene ertoent, sobald die Polizei naeher kommt. Je naeher,
        desto schneller die Wiederholung (steigende Bedrohung)."""
        self.siren_timer -= 1
        if self.siren_timer <= 0 and police_dist < 80:
            # Naehe 80 -> langes Intervall, Naehe 0 -> sehr kurzes Intervall
            interval = max(12, int(police_dist * 0.7))
            self.siren_timer = interval
            pyxel.play(0, 4)

    def play_win(self):
        pyxel.play(3, 5)

    def play_engine(self, vtype, speed_ratio, nitro, accelerating):
        """Spielt einen kurzen Hochdreh-Sound NUR beim aktiven Gasgeben.
        Kein Dauer-Loop mehr - der Sound kommt in Intervallen, solange
        beschleunigt wird, und schweigt beim Ausrollen/Bremsen."""
        self.engine_timer -= 1
        if not accelerating:
            return
        if self.engine_timer > 0:
            return
        high = nitro or speed_ratio > 0.55
        if vtype == "moto":
            snd = 10 if high else 9
        else:
            snd = 8 if high else 7
        # Intervall: bei hoher Drehzahl schnellere Schaltfolge, sonst langsamer.
        # Deutlich groesser als die Sound-Laenge -> klingt wie Gasstoesse,
        # nicht wie ein durchgehendes Brummen.
        self.engine_timer = 14 if high else 22
        pyxel.play(1, snd)

    def play_click(self):
        pyxel.play(0, 11)

    def play_confirm(self):
        pyxel.play(0, 12)

    def play_buy(self):
        pyxel.play(3, 13)

    def play_nitro(self):
        pyxel.play(3, 14)

    def play_oil(self):
        pyxel.play(3, 15)

    def play_repair(self):
        pyxel.play(3, 16)

    def play_shield_on(self):
        pyxel.play(3, 17)

    def play_shield_block(self):
        pyxel.play(2, 18)

    def play_combo(self):
        pyxel.play(2, 19)

    def play_warning(self):
        pyxel.play(0, 20)

    def update_engine(self, speed, max_speed):
        pass

# ===================================================
#  VEHICLE FLEETS
# ===================================================

CAR_FLEET = [
    Vehicle("VW Golf",       "car", 1.40, 0.030, 0.07, 1.60, 5, 14, 24,  6, 1,    0, "Solides Einsteiger-Auto"),
    Vehicle("BMW M3",        "car", 1.65, 0.035, 0.075,1.75, 5, 14, 24, 12, 1,  100, "Ausgewogen, empfohlen"),
    Vehicle("Ferrari",       "car", 1.85, 0.038, 0.08, 2.00, 4, 14, 24,  8, 1,  300, "Schnellstes Auto, wenig HP"),
    Vehicle("Lamborghini",   "car", 2.00, 0.042, 0.09, 2.10, 3, 14, 24,  9, 1,  600, "Extremgeschwindigkeit"),
]
MOTO_FLEET = [
    Vehicle("Vespa",         "moto",1.30, 0.028, 0.065,1.80, 2,  7, 18,  7, 6,    0, "Langsam aber wendig"),
    Vehicle("Yamaha R125",   "moto",1.60, 0.036, 0.08, 2.10, 2,  7, 18, 10, 6,  100, "Schnell, schmal"),
    Vehicle("Tracer 900",    "moto",1.90, 0.042, 0.09, 2.30, 2,  7, 18,  8, 1,  300, "Schnellstes Motorrad"),
]

# ===================================================
#  OBSTACLE POOLS
# ===================================================

AUTOBAHN_OBS = [
    ObstacleType("cone",8,8,1,0.20,2.0,34),
    ObstacleType("crate",12,12,1,0.28,2.5,22),
    ObstacleType("oil",14,8,1,0.15,1.8,20),
    ObstacleType("broken_car",14,22,2,0.42,3.5,12),
]
CITY_OBS = [
    ObstacleType("cone",8,8,1,0.20,2.0,24),
    ObstacleType("crate",12,12,1,0.30,2.5,24),
    ObstacleType("bollard",10,10,1,0.24,2.2,18),
    ObstacleType("taxi",14,22,2,0.48,3.8,12),
    ObstacleType("oil",14,8,1,0.15,1.8,12),
]
DESERT_OBS = [
    ObstacleType("cactus",10,14,1,0.21,2.1,20),
    ObstacleType("rock",12,10,1,0.25,2.2,20),
    ObstacleType("tumbleweed",12,12,1,0.10,1.5,30),
    ObstacleType("pickup",14,22,2,0.45,3.5,10),
]
ADV_OBS = [
    ObstacleType("roadblock",30,10,2,0.60,5.0,20),
    ObstacleType("barrier",22,8,1,0.35,3.5,25),
    ObstacleType("spike",16,6,2,0.50,4.5,20),
]

MAPS = [
    MapSpec("Autobahn",1,3,5,7,11,0,0.085,10,"Breit, 3 Spuren","highway",AUTOBAHN_OBS,"EINFACH",11),
    MapSpec("Stadt",1,13,5,6,7,0,0.105,0,"Enge Strassen","city",CITY_OBS,"MITTEL",10),
    MapSpec("Wueste",4,10,9,7,3,5,0.092,10,"Sand, Hitze, Weite","desert",DESERT_OBS,"EINFACH",11),
]

# ===================================================
#  SPRITE DRAW FUNCTIONS
# ===================================================

def draw_vehicle_sprite(v, cx, cy, flip=False):
    if v.vtype == "moto":
        draw_moto_sprite(cx, cy, v.col_body, flip)
    else:
        draw_car_small(cx, cy, v.col_body, flip)

def draw_car_small(cx, cy, col, flip=False):
    x = cx - 7; y = cy - 12
    _r(x+1, y,    12, 24, col)
    _r(x+2, y+5,  10,  5, 6)
    _r(x+3, y+6,   8,  3, 1)
    _r(x+2, y+11, 10,  4, 6)
    _r(x+3, y+12,  8,  2, 1)
    _r(x+2, y+2,   4,  2, 10)
    _r(x+8, y+2,   4,  2, 10)
    _r(x-1, y+3,   3,  4, 0)
    _r(x+12,y+3,   3,  4, 0)
    _r(x-1, y+15,  3,  4, 0)
    _r(x+12,y+15,  3,  4, 0)
    _r(x,   y+4,   2,  2, 5)
    _r(x+12,y+4,   2,  2, 5)
    _r(x,   y+16,  2,  2, 5)
    _r(x+12,y+16,  2,  2, 5)

def draw_car_golf(cx, cy, flip=False):
    draw_car_small(cx, cy, 6, flip)

def draw_car_bmw_s(cx, cy, flip=False):
    draw_car_small(cx, cy, 12, flip)

def draw_car_ferrari_s(cx, cy, flip=False):
    x = cx - 7; y = cy - 12
    _r(x+1, y,    12, 24, 8)
    _r(x+2, y+5,  10,  5, 6)
    _r(x+3, y+6,   8,  3, 1)
    _r(x+2, y+11, 10,  4, 6)
    _r(x+2, y+2,   4,  2, 10)
    _r(x+8, y+2,   4,  2, 10)
    _r(x-1, y+3,   3,  4, 0)
    _r(x+12,y+3,   3,  4, 0)
    _r(x-1, y+15,  3,  4, 0)
    _r(x+12,y+15,  3,  4, 0)
    _r(x,   y+4,   2,  2, 5)
    _r(x+12,y+4,   2,  2, 5)
    _r(x+4, y,     6,  2, 2)

def draw_car_lambo(cx, cy, flip=False):
    x = cx - 7; y = cy - 12
    _r(x+1, y,    12, 24, 9)
    _r(x+2, y+4,  10,  6, 6)
    _r(x+3, y+5,   8,  4, 1)
    _r(x+2, y+11, 10,  5, 6)
    _r(x+3, y+12,  8,  3, 1)
    _r(x+2, y+1,   4,  3, 10)
    _r(x+8, y+1,   4,  3, 10)
    _r(x-1, y+3,   3,  4, 0)
    _r(x+12,y+3,   3,  4, 0)
    _r(x-1, y+15,  3,  4, 0)
    _r(x+12,y+15,  3,  4, 0)
    _r(x,   y+4,   2,  2, 5)
    _r(x+12,y+4,   2,  2, 5)
    _r(x+3, y,     8,  2, 10)
    _r(x+2, y+22,  5,  3, 8)
    _r(x+7, y+22,  5,  3, 8)

def draw_moto_sprite(cx, cy, col, flip=False):
    x = cx - 3; y = cy - 9
    _r(x+1, y,    4, 18, col)
    _r(x,   y+2,  6,  3, 5)
    _r(x+1, y+6,  4,  4, 6)
    _r(x+1, y+5,  4,  2, 1)
    _r(x,   y,    2,  3, 5)
    _r(x+4, y,    2,  3, 5)
    _r(x-1, y+1,  2,  3, 0)
    _r(x+5, y+1,  2,  3, 0)
    _r(x-1, y+12, 2,  3, 0)
    _r(x+5, y+12, 2,  3, 0)
    _r(x,   y+13, 1,  2, 5)
    _r(x+5, y+13, 1,  2, 5)
    _r(x+1, y+2,  4,  1, 10)

def draw_moto_vespa(cx, cy, flip=False):
    draw_moto_sprite(cx, cy, 7, flip)

def draw_moto_r125(cx, cy, flip=False):
    draw_moto_sprite(cx, cy, 10, flip)

def draw_moto_tracer(cx, cy, flip=False):
    draw_moto_sprite(cx, cy, 8, flip)

def draw_player_vehicle(v, cx, cy, flip=False):
    idx_car  = ["VW Golf","BMW M3","Ferrari","Lamborghini"]
    idx_moto = ["Vespa","Yamaha R125","Tracer 900"]
    if v.vtype == "moto":
        fns = [draw_moto_vespa, draw_moto_r125, draw_moto_tracer]
        i = idx_moto.index(v.name) if v.name in idx_moto else 0
        fns[i](cx, cy, flip)
    else:
        fns = [draw_car_golf, draw_car_bmw_s, draw_car_ferrari_s, draw_car_lambo]
        i = idx_car.index(v.name) if v.name in idx_car else 0
        fns[i](cx, cy, flip)

def draw_police_sprite(cx, cy, flip=False):
    x = cx - 7; y = cy - 12
    _r(x+1, y,    12, 24,  1)
    _r(x+2, y+5,  10,  5,  7)
    _r(x+3, y+6,   8,  3,  1)
    _r(x+2, y+11, 10,  4,  7)
    _r(x+3, y+12,  8,  2,  1)
    _r(x+2, y+17,  5,  3,  8)
    _r(x+7, y+17,  5,  3,  8)
    _r(x-1, y+3,   3,  4,  0)
    _r(x+12,y+3,   3,  4,  0)
    _r(x-1, y+15,  3,  4,  0)
    _r(x+12,y+15,  3,  4,  0)
    _r(x,   y+4,   2,  2,  5)
    _r(x+12,y+4,   2,  2,  5)
    flash = (pyxel.frame_count//4)%2
    _r(x+2, y,     5, 3, 8  if flash==0 else 5)
    _r(x+7, y,     5, 3, 12 if flash==1 else 5)

def draw_police_moto(cx, cy, flip=False):
    x = cx - 3; y = cy - 9
    _r(x+1, y,    4, 18,  1)
    _r(x,   y+2,  6,  3,  7)
    _r(x+1, y+6,  4,  4,  1)
    _r(x-1, y+1,  2,  3,  0)
    _r(x+5, y+1,  2,  3,  0)
    _r(x-1, y+12, 2,  3,  0)
    _r(x+5, y+12, 2,  3,  0)
    flash = (pyxel.frame_count//4)%2
    _r(x,   y,    3, 2, 8  if flash==0 else 5)
    _r(x+3, y,    3, 2, 12 if flash==1 else 5)

def draw_obstacle_sprite(kind, cx, cy):
    x,y = int(cx),int(cy)
    if kind=="cone":
        _r(x-1,y-6,4,9,9); _r(x-3,y-2,8,2,7); _r(x-4,y+2,9,3,9)
        _r(x,y-8,2,2,10); _r(x-2,y-3,5,1,7)
    elif kind=="crate":
        _r(x-6,y-6,13,13,9); _r(x-5,y-5,11,11,4)
        pyxel.line(x-5,y,x+5,y,9); pyxel.line(x,y-5,x,y+5,9)
        _r(x-4,y-4,3,3,10); _r(x+2,y-4,3,3,10)
    elif kind=="oil":
        _r(x-7,y-3,14,6,2); _r(x-5,y-4,10,8,2)
        _r(x-4,y-2,8,4,1); _r(x-2,y-1,5,2,13); _r(x-1,y,3,1,12)
    elif kind=="broken_car":
        _r(x-7,y-11,15,22,13); _r(x-6,y-10,13,20,5)
        _r(x-5,y-7,11,8,1); _r(x-4,y+3,9,4,1)
        _r(x-7,y-11,15,2,8); _r(x-7,y+9,15,2,8)
        pyxel.line(x-3,y-6,x+2,y-2,8); pyxel.line(x+2,y-6,x-3,y-2,8)
    elif kind=="bollard":
        _r(x-3,y-7,6,13,7); _r(x-4,y-8,8,3,8); _r(x-4,y+4,8,3,8)
        _r(x-1,y-1,2,2,7)
    elif kind=="taxi":
        _r(x-7,y-11,15,22,10); _r(x-6,y-10,13,20,9)
        _r(x-5,y-7,11,8,7); _r(x-4,y-6,9,6,6)
        _r(x-7,y-11,9,3,10); _r(x-5,y-10,7,2,0)
        pyxel.text(x-2,y-10,"TAXI",10)
    elif kind=="cactus":
        _r(x-1,y-8,4,16,3); _r(x-5,y-4,4,3,3); _r(x-5,y-4,3,6,3)
        _r(x+3,y-3,4,3,3); _r(x+4,y-3,3,6,3)
        _r(x-1,y-10,4,3,11); pyxel.pset(x,y-11,10)
    elif kind=="rock":
        _r(x-6,y-3,13,7,13); _r(x-4,y-5,9,11,13)
        _r(x-5,y-4,11,9,5); _r(x-3,y-5,7,2,13)
        _r(x-2,y-4,3,2,7); _r(x+1,y-4,2,2,7)
    elif kind=="tumbleweed":
        _r(x-5,y-5,11,11,9); _r(x-4,y-6,9,13,9)
        _r(x-6,y-4,13,9,9); _r(x-3,y-3,7,7,4)
        pyxel.line(x-4,y-4,x+4,y+4,9); pyxel.line(x+4,y-4,x-4,y+4,9)
        pyxel.line(x,y-5,x,y+5,9); pyxel.line(x-5,y,x+5,y,9)
    elif kind=="pickup":
        _r(x-7,y-11,15,22,8); _r(x-6,y-10,13,20,2)
        _r(x-5,y-7,11,8,6); _r(x-4,y-6,9,6,1)
        _r(x-5,y+2,11,8,4); pyxel.line(x-5,y+2,x+5,y+2,8)
    elif kind=="roadblock":
        _r(x-15,y-5,30,10,8); pyxel.rectb(x-15,y-5,30,10,7)
        for bx2 in range(-14,15,6):
            _r(x+bx2,y-5,4,10,8 if (bx2//6)%2==0 else 7)
        big_text("STOP",x-10,y-4,8,1)
    elif kind=="barrier":
        _r(x-11,y-4,22,8,9); pyxel.rectb(x-11,y-4,22,8,8)
        for bx3 in range(-10,11,5):
            _r(x+bx3,y-4,4,8,8 if (bx3//5)%2==0 else 7)
    elif kind=="spike":
        for sp in range(-8,9,5):
            _r(x+sp,y-3,3,6,13); _r(x+sp+1,y-5,1,3,7)
        _r(x-8,y+2,16,2,5)

def draw_powerup_sprite(kind, cx, cy):
    x,y = int(cx),int(cy)
    col = PU_COLORS[kind]
    pulse = (pyxel.frame_count//6)%2
    _r(x-8,y-8,17,17,1)
    pyxel.rectb(x-8,y-8,17,17,col)
    if pulse: pyxel.rectb(x-7,y-7,15,15,7)
    if kind==PU_SHIELD:
        _r(x-4,y-6,9,2,col); _r(x-5,y-4,11,6,col)
        _r(x-4,y+2,9,3,col); _r(x-3,y+5,7,2,col)
        _r(x-3,y-3,7,5,1); _r(x-1,y-1,3,2,7)
        pyxel.text(x-5,y+3,"SCH",7)
    elif kind==PU_NITRO:
        _r(x-2,y-6,5,3,col); _r(x-4,y-4,9,3,col)
        _r(x-5,y-1,11,3,col); _r(x-3,y+2,8,3,col)
        _r(x-1,y+5,4,2,col); pyxel.text(x-5,y+3,"NIT",col)
    elif kind==PU_OIL:
        _r(x-5,y-2,11,5,col); _r(x-3,y-5,7,3,col)
        _r(x-2,y-3,5,5,1); _r(x-1,y-1,3,2,12)
        pyxel.text(x-5,y+3,"OIL",col)
    elif kind==PU_REPAIR:
        _r(x-1,y-6,3,13,col); _r(x-6,y-1,13,3,col)
        _r(x-1,y-5,3,11,7); _r(x-5,y-1,11,3,7)
        pyxel.text(x-5,y+3,"REP",col)
    elif kind==PU_TIME:
        _r(x-5,y-5,11,11,5); pyxel.rectb(x-5,y-5,11,11,col)
        pyxel.line(x,y-3,x,y,col); pyxel.line(x,y,x+3,y+2,col)
        pyxel.text(x-5,y+3,"SLO",col)

def draw_coin_sprite(cx, cy, bob=0):
    x,y = int(cx),int(cy-bob//3)
    _r(x-4,y-4,9,9,10); _r(x-3,y-5,7,11,10)
    _r(x-5,y-3,11,7,10); _r(x-3,y-3,7,7,9)
    pyxel.text(x-2,y-3,"$",10)

# FIX 1 applied: the second "def _r(...)" that appeared here in the original
# has been removed. The single definition at the top of the file is used throughout.

def draw_logo_small(cx, cy):
    fc = pyxel.frame_count
    S  = 3; E = 2
    def lH(x,y,s,c): _r(x,y,s,s*9,c);_r(x+s*3,y,s,s*9,c);_r(x,y+s*4,s*4,s,c)
    def lI(x,y,s,c): _r(x,y,s*3,s,c);_r(x,y+s*8,s*3,s,c);_r(x+s,y,s,s*9,c)
    def lG(x,y,s,c): _r(x+s,y,s*3,s,c);_r(x,y+s,s,s*7,c);_r(x+s,y+s*8,s*3,s,c);_r(x+s*4,y+s*4,s,s*5,c);_r(x+s*2,y+s*4,s*2,s,c)
    def lW(x,y,s,c): _r(x,y,s,s*9,c);_r(x+s*4,y,s,s*9,c);_r(x+s*2,y+s*5,s,s*4,c);_r(x+s,y+s*6,s,s*3,c);_r(x+s*3,y+s*6,s,s*3,c)
    def lA(x,y,s,c): _r(x+s,y,s*2,s,c);_r(x,y+s,s,s*8,c);_r(x+s*3,y+s,s,s*8,c);_r(x+s,y+s*4,s*2,s,c)
    def lY(x,y,s,c): _r(x,y,s,s*4,c);_r(x+s*3,y,s,s*4,c);_r(x+s,y+s*3,s*2,s,c);_r(x+s,y+s*4,s*2,s*5,c)
    def lE(x,y,s,c): _r(x,y,s*4,s,c);_r(x,y,s,s*9,c);_r(x,y+s*4,s*3,s,c);_r(x,y+s*8,s*4,s,c)
    def lS(x,y,s,c): _r(x+s,y,s*3,s,c);_r(x,y+s,s,s*3,c);_r(x+s,y+s*4,s*3,s,c);_r(x+s*4,y+s*5,s,s*3,c);_r(x+s,y+s*8,s*3,s,c)
    def lC(x,y,s,c): _r(x+s,y,s*3,s,c);_r(x,y+s,s,s*7,c);_r(x+s,y+s*8,s*3,s,c)
    def lP(x,y,s,c): _r(x,y,s,s*9,c);_r(x+s,y,s*3,s,c);_r(x+s*4,y+s,s,s*3,c);_r(x+s,y+s*4,s*3,s,c)

    shine = (fc//4)%60
    for ox2,oy2,ch,ce in [(2,2,0,0),(1,1,13,5),(0,0,7,8)]:
        lH(cx-92+ox2,cy-15+oy2,S,ch); lI(cx-74+ox2,cy-15+oy2,S,ch)
        lG(cx-64+ox2,cy-15+oy2,S,ch); lH(cx-40+ox2,cy-15+oy2,S,ch)
        lW(cx-20+ox2,cy-15+oy2,S,ch); lA(cx+5+ox2,cy-15+oy2,S,ch)
        lY(cx+28+ox2,cy-15+oy2,S,ch)
        lE(cx-76+ox2,cy+13+oy2,E,ce); lS(cx-55+ox2,cy+13+oy2,E,ce)
        lC(cx-34+ox2,cy+13+oy2,E,ce); lA(cx-14+ox2,cy+13+oy2,E,ce)
        lP(cx+7+ox2,cy+13+oy2,E,ce);  lE(cx+28+ox2,cy+13+oy2,E,ce)
    if shine < 50:
        sx = cx - 95 + shine*4
        for ly in range(cy-15, cy+30):
            if 0<=sx<SCREEN_W: pyxel.rect(sx,ly,3,1,7)

# ===================================================
#  MAIN GAME CLASS
# ===================================================

class HighwayEscape:
    def __init__(self):
        pyxel.init(SCREEN_W, SCREEN_H, title="Highway Escape", fps=60)
        self.garage  = Garage()
        self.sounds  = SoundManager()
        self.state   = STATE_TITLE
        self.game_mode = "1p"
        self.selected_map = 0
        self.highscore    = 0
        self.road_scroll  = 0.0
        self.stars = [[random.randint(0,SCREEN_W-1), random.randint(0,SCREEN_H-1),
                       random.choice([1,5,6,7])] for _ in range(60)]
        self.reset_run()
        pyxel.run(self.update, self.draw)

    def reset_run(self):
        v = self.garage.current_vehicle()
        self.player_spec   = v
        self.map_spec      = MAPS[self.selected_map]
        self.player_x      = float(ROAD_CENTER)
        self.player_y      = float(SCREEN_H // 2)
        self.player_speed  = 0.0
        self.player_hp     = v.hp
        self.player_iframes = 0
        self.road_scroll   = 0.0
        self.distance      = 0.0
        self.score         = 0
        self.run_coins     = 0
        self.time_survived = 0
        self.combo         = 1
        self.combo_dist    = 0.0
        self.obstacles     = []
        self.spawn_timer   = 60
        self.spawn_gap_min = max(20, 75 - self.map_spec.spawn_modifier)
        self.spawn_gap_max = max(36, 115 - self.map_spec.spawn_modifier)
        self.powerups      = []
        self.pu_timer      = random.randint(180,340)
        self.coins_list    = []
        self.coin_timer    = random.randint(60,120)
        self.active_shield = 0
        self.active_nitro  = 0
        self.active_slowmo = 0
        self.police_oil_timer = 0
        self.oncoming      = []
        self.oncoming_timer = random.randint(80,160)
        self.tumbleweeds   = []
        self.tumble_timer  = random.randint(120,240)
        self.particles     = []
        self.police_distance = 160.0
        self.police_x      = float(ROAD_CENTER)
        # Start: unteres Drittel des Bildschirms (siehe update_police fuer Formel)
        self.police_y      = float(SCREEN_H // 2) + 30 + 160.0 * 0.5
        self.target_dist   = 3000.0
        self.game_won      = False
        self.police_warned = False
        self.is_accelerating = False
        self.shake_timer   = 0
        self.flash_timer   = 0
        self.game_over_reason = ""
        self.popup_text    = ""
        self.popup_timer   = 0
        self.popup_col     = 7
        self.slowmo_factor = 1.0
        self.distance_bonus_timer = 0

    def reset_2p(self):
        self.map_spec      = MAPS[self.selected_map]
        self.p1_x = float(ROAD_LEFT + ROAD_W * 0.35)
        self.p1_y = float(SCREEN_H // 2)
        self.p1_speed = 0.0
        self.p1_hp    = 5
        self.p1_iframes = 0
        self.p2_x = float(ROAD_LEFT + ROAD_W * 0.65)
        self.p2_y = float(SCREEN_H * 0.72)
        self.p2_speed = 0.0
        self.road_scroll = 0.0
        self.distance    = 0.0
        self.obstacles   = []
        self.spawn_timer = 60
        self.spawn_gap_min = 30; self.spawn_gap_max = 60
        self.particles   = []
        self.shake_timer = 0
        self.flash_timer = 0
        self.p2_caught   = False
        self.p1_survived_dist = 0.0
        self.p2_catch_count   = 0
        self.game_over_reason = ""
        self.game_won      = False
        self.target_dist   = 3000.0
        self.score         = 0
        self.run_coins     = 0
        self.combo         = 1
        self.time_survived = 0

    # -- UPDATE --------------------------------------
    def update(self):
        if pyxel.btnp(pyxel.KEY_Q): pyxel.quit()
        if   self.state==STATE_TITLE:       self.update_title()
        elif self.state==STATE_SELECT_MODE: self.update_select_mode()
        elif self.state==STATE_SELECT_TYPE: self.update_select_type()
        elif self.state==STATE_GARAGE:      self.update_garage()
        elif self.state==STATE_SELECT_MAP:  self.update_select_map()
        elif self.state==STATE_PLAY:        self.update_play()
        elif self.state==STATE_GAME_OVER:   self.update_game_over()
        elif self.state==STATE_2P_PLAY:     self.update_2p()

    def update_title(self):
        self.road_scroll += 1.2
        self.animate_bg(0.2)
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            self.sounds.play_confirm()
            self.state = STATE_SELECT_MODE

    def update_select_mode(self):
        self.animate_bg(0.3)
        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):
            if self.game_mode != "1p": self.sounds.play_click()
            self.game_mode = "1p"
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            if self.game_mode != "2p": self.sounds.play_click()
            self.game_mode = "2p"
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            self.sounds.play_confirm()
            self.state = STATE_SELECT_TYPE
        if pyxel.btnp(pyxel.KEY_BACKSPACE):
            self.sounds.play_click()
            self.state = STATE_TITLE

    def update_select_type(self):
        self.animate_bg(0.3)
        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):
            if self.garage.vtype != "car": self.sounds.play_click()
            self.garage.vtype = "car"
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            if self.garage.vtype != "moto": self.sounds.play_click()
            self.garage.vtype = "moto"
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            self.sounds.play_confirm()
            self.state = STATE_GARAGE
        if pyxel.btnp(pyxel.KEY_BACKSPACE):
            self.sounds.play_click()
            self.state = STATE_SELECT_MODE

    def update_garage(self):
        self.animate_bg(0.3)
        pool = CAR_FLEET if self.garage.vtype=="car" else MOTO_FLEET
        sel  = self.garage.selected[self.garage.vtype]
        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):
            self.sounds.play_click()
            self.garage.selected[self.garage.vtype] = (sel-1) % len(pool)
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            self.sounds.play_click()
            self.garage.selected[self.garage.vtype] = (sel+1) % len(pool)
        if pyxel.btnp(pyxel.KEY_E):
            bought = self.garage.buy(self.garage.selected[self.garage.vtype], self.garage.vtype)
            if bought: self.sounds.play_buy()
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            sel2 = self.garage.selected[self.garage.vtype]
            if sel2 in self.garage.unlocked[self.garage.vtype]:
                self.sounds.play_confirm()
                self.state = STATE_SELECT_MAP
        if pyxel.btnp(pyxel.KEY_BACKSPACE):
            self.sounds.play_click()
            self.state = STATE_SELECT_TYPE

    def update_select_map(self):
        self.animate_bg(0.3)
        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):
            self.sounds.play_click()
            self.selected_map = (self.selected_map-1) % len(MAPS)
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            self.sounds.play_click()
            self.selected_map = (self.selected_map+1) % len(MAPS)
        if pyxel.btnp(pyxel.KEY_BACKSPACE):
            self.sounds.play_click()
            self.state = STATE_GARAGE
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            self.sounds.play_confirm()
            if self.game_mode == "2p":
                self.reset_2p()
                self.state = STATE_2P_PLAY
            else:
                self.reset_run()
                self.state = STATE_PLAY

    def update_game_over(self):
        self.animate_bg(0.15)
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE):
            self.sounds.play_confirm()
            self.state = STATE_SELECT_TYPE

    def update_play(self):
        self.time_survived += 1
        for attr in ("player_iframes","shake_timer","flash_timer",
                     "active_shield","active_nitro","police_oil_timer","popup_timer","active_slowmo"):
            v = getattr(self,attr)
            if v > 0: setattr(self,attr,v-1)
        self.slowmo_factor = 0.35 if self.active_slowmo > 0 else 1.0

        self.handle_input()
        self.sounds.play_engine(self.player_spec.vtype,
                                self.player_speed / max(self.player_spec.max_speed, 0.01),
                                self.active_nitro > 0,
                                self.is_accelerating)
        self.update_road()
        self.update_obstacles()
        self.update_oncoming()
        self.update_tumbleweeds()
        self.update_powerups()
        self.update_coins()
        self.update_police()
        self.update_particles()
        self.check_collisions()
        self.check_powerup_collisions()
        self.check_coin_collisions()
        self.update_score()
        self.sounds.play_siren(self.police_distance)

        if self.distance >= self.target_dist:
            self.game_won = True
            self.run_coins += 10
            self.garage.coins += 10
            self.sounds.play_win()
            self.end_game("Ziel erreicht - Bonus 10 Coins")
        elif self.player_hp <= 0:    self.end_game("Fahrzeug zerstoert")
        elif self.police_distance<=0: self.end_game("Von der Polizei erwischt")

    def handle_input(self):
        spec  = self.player_spec
        sf    = self.slowmo_factor
        up    = pyxel.btn(pyxel.KEY_UP)   or pyxel.btn(pyxel.KEY_W)
        down  = pyxel.btn(pyxel.KEY_DOWN)  or pyxel.btn(pyxel.KEY_S)
        left  = pyxel.btn(pyxel.KEY_LEFT)  or pyxel.btn(pyxel.KEY_A)
        right = pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D)
        eff_max = spec.max_speed * (1.45 if self.active_nitro>0 else 1.0) * sf
        if up:   self.player_speed += spec.accel * sf
        else:    self.player_speed -= 0.012 * sf
        if down: self.player_speed -= spec.brake * sf
        # Merkt sich, ob aktiv beschleunigt wird (Gas gedrueckt und noch nicht
        # am Limit) - steuert den Motorensound.
        self.is_accelerating = up and self.player_speed < eff_max - 0.02
        self.player_speed = clamp(self.player_speed, 0.0, eff_max)
        lateral = (spec.handling * 0.9 + self.player_speed * 0.04) * sf
        if self.map_spec.deco_style=="desert": lateral *= 0.95
        if left:  self.player_x -= lateral
        if right: self.player_x += lateral
        hw = spec.width // 2
        self.player_x = clamp(self.player_x, ROAD_LEFT+hw+2, ROAD_RIGHT-hw-2)

    def update_road(self):
        sf = self.slowmo_factor
        self.road_scroll += (self.player_speed + 0.30) * sf
        self.distance    += (self.player_speed + 0.06) * sf

    def update_obstacles(self):
        sf = self.slowmo_factor
        self.spawn_timer -= 1
        if self.spawn_timer <= 0:
            self.spawn_obstacle()
            self.spawn_timer = random.randint(self.spawn_gap_min, self.spawn_gap_max)
        spd = (self.player_speed + 0.45) * sf
        for ob in self.obstacles: ob.y += spd
        self.obstacles = [ob for ob in self.obstacles if ob.y < SCREEN_H+30]

    def spawn_obstacle(self):
        pool = list(self.map_spec.obstacle_pool)
        if self.distance > 1500: pool += ADV_OBS
        chosen = random.choices(pool, weights=[o.weight for o in pool], k=1)[0]
        li = random.randint(0,2)
        jitter = random.randint(-8,8)
        x = clamp(LANES[li]+jitter, ROAD_LEFT+chosen.w//2+2, ROAD_RIGHT-chosen.w//2-2)
        y = -chosen.h - random.randint(0,30)
        for ob in self.obstacles:
            if abs(ob.y-y)<40 and abs(ob.x-x)<20: y -= 34
        self.obstacles.append(Obstacle(chosen.kind,x,y,chosen.w,chosen.h,
                                       chosen.damage,chosen.speed_penalty,chosen.police_penalty,li))

    def update_oncoming(self):
        sf = self.slowmo_factor
        self.oncoming_timer -= 1
        if self.oncoming_timer <= 0 and self.distance > 500:
            li   = random.randint(0,2)
            spd  = random.uniform(1.2, 2.0)
            cols = [8,9,12,11,6,13]
            col  = random.choice(cols)
            self.oncoming.append(OncomingCar(LANES[li], SCREEN_H+20, li, spd, col))
            self.oncoming_timer = random.randint(90,180)
        for oc in self.oncoming: oc.y -= (oc.speed + self.player_speed*0.3)*sf
        self.oncoming = [oc for oc in self.oncoming if oc.y > -30]

    def update_tumbleweeds(self):
        if self.map_spec.deco_style != "desert": return
        sf = self.slowmo_factor
        self.tumble_timer -= 1
        if self.tumble_timer <= 0:
            side = random.choice([-1,1])
            sx   = ROAD_LEFT - 5 if side > 0 else ROAD_RIGHT + 5
            vx   = random.uniform(0.8,1.8) * side
            self.tumbleweeds.append(Tumbleweed(sx, random.randint(-20,0), vx))
            self.tumble_timer = random.randint(120,280)
        spd = (self.player_speed + 0.3)*sf
        for tw in self.tumbleweeds:
            tw.y   += spd
            tw.x   += tw.vx * sf
            tw.spin = (tw.spin+2) % 360
        self.tumbleweeds = [tw for tw in self.tumbleweeds
                            if -20 < tw.y < SCREEN_H+20 and ROAD_LEFT-30 < tw.x < ROAD_RIGHT+30]

    def update_powerups(self):
        sf = self.slowmo_factor
        self.pu_timer -= 1
        if self.pu_timer <= 0:
            kind = random.choices(
                [PU_SHIELD,PU_NITRO,PU_OIL,PU_REPAIR,PU_TIME],
                weights=[25,25,20,15,15], k=1)[0]
            li = random.randint(0,2)
            x  = clamp(LANES[li]+random.randint(-8,8), ROAD_LEFT+8, ROAD_RIGHT-8)
            self.powerups.append(PowerUp(kind, x, -12.0))
            self.pu_timer = random.randint(200,380)
        spd = (self.player_speed+0.45)*sf
        for pu in self.powerups: pu.y += spd
        self.powerups = [pu for pu in self.powerups if pu.y < SCREEN_H+20]

    def update_coins(self):
        sf = self.slowmo_factor
        self.coin_timer -= 1
        if self.coin_timer <= 0:
            li = random.randint(0,2)
            x  = clamp(LANES[li]+random.randint(-10,10), ROAD_LEFT+6, ROAD_RIGHT-6)
            for _ in range(random.randint(1,3)):
                self.coins_list.append(Coin(x + random.randint(-8,8), -8.0))
            self.coin_timer = random.randint(60,120)
        spd = (self.player_speed+0.45)*sf
        for c in self.coins_list:
            c.y   += spd
            c.bob  = (c.bob+2) % 24
        self.coins_list = [c for c in self.coins_list if c.y < SCREEN_H+20]

    def update_police(self):
        sf       = self.slowmo_factor
        max_spd  = self.player_spec.max_speed
        sp_ratio = self.player_speed / max_spd if max_spd > 0 else 0
        base_press = self.map_spec.police_pressure * (1.0 + min(0.8, self.distance/5000))
        if self.police_oil_timer > 0: base_press *= 0.25
        nitro_on = self.active_nitro > 0
        escape   = sp_ratio * (0.32 if nitro_on else 0.105)
        if self.active_slowmo > 0: base_press *= 0.2
        self.police_distance += (escape - base_press) * sf
        if self.player_speed < max_spd*0.30: self.police_distance -= 0.05*sf
        self.police_distance  = clamp(self.police_distance, 0.0, 200.0)
        # Einmalige Warnung, wenn die Polizei die kritische Naehe unterschreitet
        # (Flankenerkennung, damit der Sound nicht jeden Frame spielt)
        if self.police_distance < 30 and not self.police_warned:
            self.sounds.play_warning()
            self.police_warned = True
        elif self.police_distance > 50:
            self.police_warned = False
        self.police_x        += (self.player_x - self.police_x) * 0.06 * sf
        # Abbildung: police_distance 200 (weit weg) -> unten am Rand,
        # police_distance 0 (erwischt) -> direkt hinter dem Spieler.
        # Start (160) liegt damit im unteren Drittel des Bildschirms.
        target_y = self.player_y + 30 + self.police_distance * 0.5
        self.police_y = clamp(target_y, self.player_y+14, SCREEN_H-14)

    def update_particles(self):
        for p in self.particles:
            p.x += p.vx; p.y += p.vy; p.vy += 0.15; p.life -= 1
        self.particles = [p for p in self.particles if p.life > 0]

    def spawn_particles(self, x, y, n=8):
        for _ in range(n):
            vx = random.uniform(-2.5, 2.5)
            vy = random.uniform(-3.0, 0.5)
            col = random.choice([8,9,10,7,5])
            life = random.randint(12,22)
            self.particles.append(Particle(x,y,vx,vy,col,life,life))

    def check_collisions(self):
        if self.player_iframes > 0: return
        spec = self.player_spec
        px = self.player_x - spec.width//2
        py = self.player_y - spec.height//2
        for ob in self.obstacles:
            ox = ob.x - ob.w//2; oy = ob.y - ob.h//2
            if rects_overlap(px,py,spec.width,spec.height,ox,oy,ob.w,ob.h):
                if self.active_shield > 0:
                    self.active_shield = 0
                    self.popup("SCHILD!", 12)
                    self.player_iframes = 24; self.shake_timer = 6
                    self.sounds.play_shield_block()
                else:
                    self.player_hp      -= ob.damage
                    self.player_speed    = max(0.0, self.player_speed-ob.speed_penalty)
                    self.police_distance = max(0.0, self.police_distance-ob.police_penalty)
                    self.player_iframes  = 32; self.shake_timer=10; self.flash_timer=8
                    self.combo=1; self.combo_dist=0.0
                    self.spawn_particles(self.player_x, self.player_y)
                    self.sounds.play_crash()
                    if self.player_hp == 1: self.sounds.play_warning()
                ob.y = SCREEN_H+999; break
        for oc in self.oncoming:
            if rects_overlap(px,py,spec.width,spec.height,oc.x-oc.w//2,oc.y-oc.h//2,oc.w,oc.h):
                if self.active_shield > 0:
                    self.active_shield = 0; self.popup("SCHILD!", 12)
                    self.player_iframes=24; self.shake_timer=6
                    self.sounds.play_shield_block()
                else:
                    self.player_hp -= 1; self.player_speed = max(0.0,self.player_speed-0.3)
                    self.player_iframes=28; self.shake_timer=8; self.flash_timer=6
                    self.spawn_particles(self.player_x, self.player_y, 5)
                    self.sounds.play_crash()
                    if self.player_hp == 1: self.sounds.play_warning()
                oc.y = -999; break
        for tw in self.tumbleweeds:
            if rects_overlap(px,py,spec.width,spec.height,tw.x-5,tw.y-5,10,10):
                if self.active_shield == 0:
                    self.player_speed = max(0.0,self.player_speed-0.15)
                    self.player_iframes=15; self.shake_timer=4
                tw.x = -999

    def check_powerup_collisions(self):
        spec=self.player_spec
        px=self.player_x-spec.width//2; py=self.player_y-spec.height//2
        rem=[]
        for pu in self.powerups:
            if rects_overlap(px,py,spec.width,spec.height,pu.x-6,pu.y-6,12,12):
                self.apply_powerup(pu.kind)
            else: rem.append(pu)
        self.powerups=rem

    def apply_powerup(self, kind):
        if kind==PU_SHIELD:
            self.active_shield=PU_DURATION[PU_SHIELD]; self.popup("SCHILD AKTIV!",12)
            self.sounds.play_shield_on()
        elif kind==PU_NITRO:
            self.active_nitro=PU_DURATION[PU_NITRO]
            self.police_distance=min(200.0,self.police_distance+20)
            self.popup("NITRO!",9)
            self.sounds.play_nitro()
        elif kind==PU_OIL:
            self.police_oil_timer=420
            self.police_distance=min(200.0,self.police_distance+35)
            self.popup("POLIZEI GEBREMST!",2)
            self.sounds.play_oil()
        elif kind==PU_REPAIR:
            self.player_hp=min(self.player_hp+1,self.player_spec.hp)
            self.popup("+1 HP!",11)
            self.sounds.play_repair()
        elif kind==PU_TIME:
            self.active_slowmo=PU_DURATION[PU_TIME]
            self.popup("ZEITLUPE!",10)
            pyxel.play(2,6)

    def check_coin_collisions(self):
        spec=self.player_spec
        px=self.player_x-spec.width//2; py=self.player_y-spec.height//2
        rem=[]
        for c in self.coins_list:
            if rects_overlap(px,py,spec.width,spec.height,c.x-4,c.y-4,8,8):
                self.run_coins+=1; self.garage.coins+=1
                self.spawn_particles(c.x,c.y,3)
                self.sounds.play_coin()
            else: rem.append(c)
        self.coins_list=rem

    def update_score(self):
        delta = self.player_speed + 0.06
        self.combo_dist += delta
        if self.combo_dist >= 400:
            old_combo = self.combo
            self.combo=min(self.combo+1,8); self.combo_dist=0.0
            if self.combo>1 and self.combo>old_combo:
                self.popup("x"+str(self.combo)+" COMBO!",10)
                self.sounds.play_combo()
        self.score = int((self.distance + self.time_survived*0.12)*self.combo)

    def end_game(self, reason):
        self.game_over_reason = reason
        self.highscore = max(self.highscore, self.score)
        self.state = STATE_GAME_OVER

    def popup(self, text, col):
        self.popup_text=text; self.popup_timer=80; self.popup_col=col

    def animate_bg(self, speed):
        for s in self.stars:
            s[1] += speed
            if s[1] >= SCREEN_H:
                s[0]=random.randint(0,SCREEN_W-1); s[1]=0
                s[2]=random.choice([1,5,6,7])

    # -- 2-PLAYER ------------------------------------
    def update_2p(self):
        self.time_survived = getattr(self,"time_survived",0) + 1
        for attr in ("p1_iframes","shake_timer","flash_timer"):
            v=getattr(self,attr,0)
            if v>0: setattr(self,attr,v-1)
        p1_up   = pyxel.btn(pyxel.KEY_W)
        p1_down = pyxel.btn(pyxel.KEY_S)
        p1_left = pyxel.btn(pyxel.KEY_A)
        p1_right= pyxel.btn(pyxel.KEY_D)
        if p1_up:   self.p1_speed += 0.035
        else:       self.p1_speed -= 0.015
        if p1_down: self.p1_speed -= 0.07
        self.p1_speed = clamp(self.p1_speed, 0.0, 1.8)
        if p1_left:  self.p1_x -= 1.8
        if p1_right: self.p1_x += 1.8
        self.p1_x = clamp(self.p1_x, ROAD_LEFT+8, ROAD_RIGHT-8)
        p2_up   = pyxel.btn(pyxel.KEY_UP)
        p2_down = pyxel.btn(pyxel.KEY_DOWN)
        p2_left = pyxel.btn(pyxel.KEY_LEFT)
        p2_right= pyxel.btn(pyxel.KEY_RIGHT)
        if p2_up:   self.p2_speed += 0.04
        else:       self.p2_speed -= 0.012
        if p2_down: self.p2_speed -= 0.06
        self.p2_speed = clamp(self.p2_speed, 0.0, 1.6)
        if p2_left:  self.p2_x -= 2.0
        if p2_right: self.p2_x += 2.0
        self.p2_x = clamp(self.p2_x, ROAD_LEFT+8, ROAD_RIGHT-8)
        self.road_scroll += self.p1_speed + 0.3
        self.distance    += self.p1_speed + 0.06
        self.p1_survived_dist = self.distance
        self.update_obstacles()
        if rects_overlap(self.p1_x-7,self.p1_y-12,14,24,
                         self.p2_x-7,self.p2_y-12,14,24):
            self.p2_catch_count = getattr(self,"p2_catch_count",0)+1
            self.p1_hp = getattr(self,"p1_hp",5)-1
            self.shake_timer=10; self.flash_timer=8
            self.sounds.play_crash()
            self.p2_x = self.p1_x + random.choice([-30,30])
            if getattr(self,"p1_hp",5) <= 0:
                self.game_won = False
                self.game_over_reason = "P2 hat P1 erwischt!"
                self.state=STATE_GAME_OVER
        # Ziel im 2-Spieler-Modus: P1 gewinnt bei 3000 m
        if self.distance >= getattr(self,"target_dist",3000.0):
            self.game_won = True
            self.game_over_reason = "P1 ist entkommen!"
            self.sounds.play_win()
            self.state = STATE_GAME_OVER
        scroll_diff = self.p1_speed - self.p2_speed
        self.p2_y += scroll_diff * 0.5
        self.p2_y = clamp(self.p2_y, self.p1_y+20, SCREEN_H-14)
        if pyxel.btnp(pyxel.KEY_ESCAPE):
            self.sounds.play_click()
            self.state=STATE_SELECT_MODE

    # -- DRAW ----------------------------------------
    def draw(self):
        ox = random.randint(-2,2) if self.shake_timer>0 else 0
        oy = random.randint(-2,2) if self.shake_timer>0 else 0
        if   self.state==STATE_TITLE:       self.draw_title()
        elif self.state==STATE_SELECT_MODE: self.draw_select_mode()
        elif self.state==STATE_SELECT_TYPE: self.draw_select_type()
        elif self.state==STATE_GARAGE:      self.draw_garage()
        elif self.state==STATE_SELECT_MAP:  self.draw_select_map()
        elif self.state==STATE_PLAY:        self.draw_play(ox,oy)
        elif self.state==STATE_GAME_OVER:   self.draw_game_over()
        elif self.state==STATE_2P_PLAY:     self.draw_2p(ox,oy)

    def draw_title(self):
        sc = int(self.road_scroll)
        SEG_W = SCREEN_W//3
        SEG = [(3,0,11,11,"highway"),(1,13,6,7,"city"),(9,10,7,3,"desert")]
        for i,(bg,road,lc,dc,style) in enumerate(SEG):
            bx=i*SEG_W
            pyxel.rect(bx,0,SEG_W,SCREEN_H,bg)
            rw=SEG_W*2//5; ro=SEG_W//2-rw//2
            pyxel.rect(bx+ro,0,rw,SCREEN_H,road if road!=0 else 0)
            pyxel.rect(bx+ro-3,0,4,SCREEN_H,bg)
            pyxel.rect(bx+ro+rw,0,4,SCREEN_H,bg)
            for dy in range(-50,SCREEN_H+50,50):
                pyxel.rect(bx+SEG_W//2-1,dy+sc%50,3,18,lc)
            if style=="highway":
                for dy in range(-40,SCREEN_H+40,40):
                    pyxel.rect(bx+4,dy+(sc//2)%40,6,10,dc)
                    pyxel.rect(bx+SEG_W-10,dy+(sc//2)%40,6,10,dc)
            elif style=="city":
                for dy in range(-50,SCREEN_H+50,50):
                    pyxel.rect(bx+2,dy+(sc//2)%50,14,20,5)
                    pyxel.rectb(bx+2,dy+(sc//2)%50,14,20,1)
                    pyxel.rect(bx+SEG_W-16,dy+(sc//2)%50,14,20,5)
                    pyxel.rectb(bx+SEG_W-16,dy+(sc//2)%50,14,20,1)
            elif style=="desert":
                for dy in range(-50,SCREEN_H+50,55):
                    self.draw_cactus_small(bx+4,dy+(sc//2)%55)
                    self.draw_rock_small(bx+SEG_W-12,dy+(sc//2)%55+12)
        for i in range(2):
            pyxel.rect(SEG_W*(i+1)-1,0,3,SCREEN_H,0)
        seg_labels=["AUTOBAHN","STADT","WUESTE"]
        seg_cols  =[11,7,3]
        for i,(lbl,c2) in enumerate(zip(seg_labels,seg_cols)):
            lw=big_text_w(lbl,1); bx2=i*SEG_W+SEG_W//2-lw//2
            pyxel.rect(bx2-3,SCREEN_H-18,lw+6,12,0)
            pyxel.rectb(bx2-3,SCREEN_H-18,lw+6,12,c2)
            big_text(lbl,bx2,SCREEN_H-15,c2,1)
        cx0=SCREEN_W//2; cy0=SCREEN_H//2-10
        pw=340; ph=260; pxx=cx0-pw//2; pyy=cy0-ph//2
        pyxel.rect(pxx-3,pyy-3,pw+6,ph+6,0)
        pyxel.rect(pxx,pyy,pw,ph,1)
        pyxel.rectb(pxx,pyy,pw,ph,8)
        pyxel.rectb(pxx+1,pyy+1,pw-2,ph-2,9)
        draw_logo_small(cx0,pyy+55)
        pyxel.rect(pxx+12,pyy+88,pw-24,1,8)
        sub="TOP-DOWN POLICE CHASE"
        sw=big_text_w(sub,1); big_text(sub,cx0-sw//2,pyy+93,6,1)
        cy_cars=pyy+140
        draw_car_golf(cx0-80,cy_cars)
        draw_car_ferrari_s(cx0,cy_cars)
        draw_car_lambo(cx0+80,cy_cars)
        draw_moto_vespa(cx0-110,cy_cars+5)
        draw_moto_tracer(cx0+108,cy_cars+5)
        draw_police_sprite(cx0,cy_cars+50)
        siren_c=8 if (pyxel.frame_count//5)%2==0 else 12
        pyxel.rect(cx0-15,cy_cars+47,30,2,siren_c)
        blink=(pyxel.frame_count//18)%2
        bc=11 if blink else 10
        bw2=240; bxx=cx0-bw2//2; byy=pyy+ph-46
        pyxel.rect(bxx,byy,bw2,28,0)
        pyxel.rectb(bxx,byy,bw2,28,bc)
        st="ENTER = STARTEN"
        stw=big_text_w(st,2); big_text(st,cx0-stw//2,byy+9,bc,2)
        if self.highscore>0:
            hs="COINS:"+str(self.garage.coins)+"  HS:"+str(self.highscore)
            hsw=big_text_w(hs,1); big_text(hs,cx0-hsw//2,byy-14,10,1)
        pyxel.text(pxx+4,pyy+ph-8,"Q=Quit",5)

    def draw_select_mode(self):
        pyxel.cls(1)
        for sx,sy,col in self.stars: pyxel.pset(sx,int(sy),col)
        cx=SCREEN_W//2
        t="SPIELMODUS WAEHLEN"
        tw=big_text_w(t,2); big_text(t,cx-tw//2+1,30,13,2); big_text(t,cx-tw//2,29,7,2)
        pyxel.text(cx-90,50,"< / >  wechseln   |   ENTER = Weiter",6)
        modes=[
            ("1 SPIELER","Flucht vor der Polizei-KI","Erreiche 3000 m",11,"1p"),
            ("2 SPIELER","P1 fluechtet (WASD)","P2 ist die Polizei (Pfeile)",9,"2p"),
        ]
        for i,(label,desc1,desc2,vcol,mode_id) in enumerate(modes):
            x=40+i*250; sel=(self.game_mode==mode_id)
            c=vcol if sel else 5
            pyxel.rect(x,80,200,300,0 if sel else 1)
            pyxel.rectb(x,80,200,300,c)
            if sel: pyxel.rectb(x+1,81,198,298,c)
            lw=big_text_w(label,2); big_text(label,x+100-lw//2,92,c,2)
            pyxel.text(x+100-len(desc1)*2,116,desc1,6)
            pyxel.text(x+100-len(desc2)*2,126,desc2,6)
            pyxel.rect(x+20,145,160,120,5)
            if i==0:
                # 1P: Spielerauto vorne, Polizei-KI dahinter
                draw_car_ferrari_s(x+100,180)
                draw_police_sprite(x+100,235)
            else:
                # 2P: zwei Fahrzeuge nebeneinander
                draw_car_golf(x+65,195)
                draw_police_sprite(x+135,215)
            if sel:
                blink=(pyxel.frame_count//18)%2; bc=c if blink else 7
                pyxel.rect(x+20,340,160,28,0); pyxel.rectb(x+20,340,160,28,bc)
                st="AUSGEWAEHLT"
                stw=big_text_w(st,2); big_text(st,x+100-stw//2,348,bc,2)
            else:
                pyxel.rect(x+20,340,160,28,1); pyxel.rectb(x+20,340,160,28,5)
                st="< / > WAEHLEN"; stw=big_text_w(st,1)
                big_text(st,x+100-stw//2,352,5,1)
        tw2=big_text_w("ENTER = WEITER",2)
        pyxel.rect(cx-tw2//2-6,430,tw2+12,24,0)
        pyxel.rectb(cx-tw2//2-6,430,tw2+12,24,11)
        big_text("ENTER = WEITER",cx-tw2//2,436,11,2)

    def draw_select_type(self):
        pyxel.cls(1)
        for sx,sy,col in self.stars: pyxel.pset(sx,int(sy),col)
        cx=SCREEN_W//2
        t="FAHRZEUGTYP WAEHLEN"
        tw=big_text_w(t,2); big_text(t,cx-tw//2+1,30,13,2); big_text(t,cx-tw//2,29,7,2)
        pyxel.text(cx-110,50,"< / >  wechseln   |   ENTER = Weiter   |   BACK = Zurueck",6)
        for i,(label,desc,vcol) in enumerate([("AUTO","Golf, BMW, Ferrari, Lambo",11),("MOTORRAD","Vespa, R125, Tracer",9)]):
            x=40+i*250; sel=(self.garage.vtype=="car" and i==0) or (self.garage.vtype=="moto" and i==1)
            c=vcol if sel else 5
            pyxel.rect(x,80,200,300,0 if sel else 1)
            pyxel.rectb(x,80,200,300,c)
            if sel: pyxel.rectb(x+1,81,198,298,c)
            lw=big_text_w(label,2); big_text(label,x+100-lw//2,92,c,2)
            pyxel.text(x+100-len(desc)*2,116,desc,6)
            pyxel.rect(x+20,135,160,130,5)
            if i==0:
                draw_car_golf(x+55,180)
                draw_car_ferrari_s(x+105,200)
                draw_car_lambo(x+155,180)
            else:
                draw_moto_vespa(x+60,185)
                draw_moto_r125(x+100,195)
                draw_moto_tracer(x+145,185)
            if sel:
                blink=(pyxel.frame_count//18)%2; bc=c if blink else 7
                bw=160; bx2=x+20
                pyxel.rect(bx2,340,bw,28,0); pyxel.rectb(bx2,340,bw,28,bc)
                st="AUSGEWAEHLT"
                stw=big_text_w(st,2); big_text(st,x+100-stw//2,348,bc,2)
            else:
                pyxel.rect(x+20,340,160,28,1); pyxel.rectb(x+20,340,160,28,5)
                st="< / > WAEHLEN"; stw=big_text_w(st,1)
                big_text(st,x+100-stw//2,352,5,1)
        tw2=big_text_w("ENTER = WEITER",2)
        pyxel.rect(cx-tw2//2-6,430,tw2+12,24,0)
        pyxel.rectb(cx-tw2//2-6,430,tw2+12,24,11)
        big_text("ENTER = WEITER",cx-tw2//2,436,11,2)

    def draw_garage(self):
        pyxel.cls(1)
        for sx,sy,col in self.stars: pyxel.pset(sx,int(sy),col)
        cx=SCREEN_W//2
        vt=self.garage.vtype
        pool=CAR_FLEET if vt=="car" else MOTO_FLEET
        sel=self.garage.selected[vt]
        v=pool[sel]
        unlocked=sel in self.garage.unlocked[vt]
        t="GARAGE"
        tw=big_text_w(t,3); big_text(t,cx-tw//2+1,18,13,3); big_text(t,cx-tw//2,17,7,3)
        cw=big_text_w("COINS: "+str(self.garage.coins),2)
        big_text("COINS: "+str(self.garage.coins),cx-cw//2,38,10,2)
        pyxel.rect(30,60,200,340,5)
        pyxel.rectb(30,60,200,340,v.col_body)
        pyxel.rectb(31,61,198,338,13)
        nw=big_text_w(v.name,2); big_text(v.name,130-nw//2+1,72,v.col_body,2)
        big_text(v.name,130-nw//2,71,7,2)
        pyxel.rect(50,90,160,100,0)
        pyxel.rectb(50,90,160,100,13)
        draw_player_vehicle(v,130,145)
        if unlocked:
            pyxel.rect(60,200,120,16,3); pyxel.rectb(60,200,120,16,11)
            ow=big_text_w("FREIGESCHALTET",1); big_text("FREIGESCHALTET",130-ow//2,204,11,1)
        else:
            pyxel.rect(55,200,150,16,8); pyxel.rectb(55,200,150,16,10)
            pw_=big_text_w(str(v.price)+" COINS",2)
            big_text(str(v.price)+" COINS",130-pw_//2,204,10,2)
        stats=[("SPD",v.max_speed,2.0,9),("HP",float(v.hp),5.0,11),("HAND",v.handling,2.3,12)]
        for si,(lbl,val,mx,sc) in enumerate(stats):
            sy2=228+si*30
            pyxel.text(38,sy2,lbl,6)
            bw=120; fill=int(bw*clamp(val/mx,0,1))
            pyxel.rect(70,sy2,bw,8,0); pyxel.rect(70,sy2,fill,8,sc)
            pyxel.rect(70,sy2,fill,3,7); pyxel.rectb(70,sy2,bw,8,5)
        dw=big_text_w(v.description,1)
        pyxel.text(130-dw//2,322,v.description,6)
        if not unlocked and self.garage.coins>=v.price:
            blink=(pyxel.frame_count//15)%2; bc=10 if blink else 9
            pyxel.rect(50,348,160,24,0); pyxel.rectb(50,348,160,24,bc)
            ew=big_text_w("E = KAUFEN",2); big_text("E = KAUFEN",130-ew//2,354,bc,2)
        elif unlocked:
            pyxel.rect(50,348,160,24,3); pyxel.rectb(50,348,160,24,11)
            sw2=big_text_w("ENTER = WAEHLEN",1); big_text("ENTER = WAEHLEN",130-sw2//2,354,11,1)
        ip=282
        pyxel.rect(ip,60,200,340,1); pyxel.rectb(ip,60,200,340,13)
        tt="FAHRZEUGE"
        ttw=big_text_w(tt,2); big_text(tt,ip+100-ttw//2,70,7,2)
        for vi,vv in enumerate(pool):
            vy=92+vi*52; vu=vi in self.garage.unlocked[vt]
            vc=vv.col_body if vi==sel else (5 if vu else 1)
            pyxel.rect(ip+8,vy,184,46,0 if vi==sel else 1)
            pyxel.rectb(ip+8,vy,184,46,vc)
            draw_player_vehicle(vv,ip+36,vy+23)
            nw2=big_text_w(vv.name,1); big_text(vv.name,ip+58,vy+8,vc,1)
            pyxel.text(ip+60,vy+20,vv.description,6 if vu else 13)
            if vu: pyxel.text(ip+60,vy+30,"FREI",11)
            else:  pyxel.text(ip+60,vy+30,str(vv.price)+" C",10)
        if sel>0:
            pyxel.rect(0,230,24,28,8); pyxel.rectb(0,230,24,28,7)
            pyxel.text(8,241,"<",7)
        if sel<len(pool)-1:
            pyxel.rect(488,230,24,28,8); pyxel.rectb(488,230,24,28,7)
            pyxel.text(496,241,">",7)

    def draw_select_map(self):
        pyxel.cls(1)
        for sx,sy,col in self.stars: pyxel.pset(sx,int(sy),col)
        m=MAPS[self.selected_map]; cx=SCREEN_W//2
        t="STRECKE WAEHLEN"
        tw=big_text_w(t,2); big_text(t,cx-tw//2+1,20,13,2); big_text(t,cx-tw//2,19,7,2)
        pyxel.text(cx-110,36,"< / > wechseln | ENTER = Start | BACK = Zurueck",6)
        rp=cx-50; ry=52
        pyxel.rect(cx-80,ry,160,400,m.side_color if m.side_color else 1)
        road_col=m.road_color if m.road_color else 0
        pyxel.rect(rp,ry,100,400,road_col)
        pyxel.rect(rp-3,ry,4,400,m.shoulder_color)
        pyxel.rect(rp+99,ry,4,400,m.shoulder_color)
        scroll_off=int(self.road_scroll)%20
        for yy in range(ry,ry+400,20):
            pyxel.rect(rp+48,yy+scroll_off%20,3,12,m.lane_color)
        self.draw_map_deco_preview(m,rp,ry)
        pyxel.rectb(cx-80,ry,160,400,m.diff_color)
        mnw=big_text_w(m.name,2); big_text(m.name,cx-mnw//2,ry+8,m.diff_color,2)
        ip=8; iw=cx-88
        pyxel.rect(ip,52,iw,340,1); pyxel.rectb(ip,52,iw,340,13)
        pyxel.rect(ip+4,60,iw-8,20,5); pyxel.rectb(ip+4,60,iw-8,20,m.diff_color)
        dlbl="SCHWIERIGKEIT: "+m.difficulty
        dlw=big_text_w(dlbl,1); big_text(dlbl,ip+iw//2-dlw//2,65,m.diff_color,1)
        pyxel.text(ip+6,88,m.description,7)
        pyxel.rect(ip+4,98,iw-8,12,5); big_text("POLIZEI",ip+8,100,6,1)
        bw=iw-12; fill=int(bw*clamp(m.police_pressure/0.07,0,1))
        pyxel.rect(ip+4,114,bw,7,0); pyxel.rect(ip+4,114,fill,7,8)
        pyxel.rectb(ip+4,114,bw,7,5)
        pyxel.rect(ip+4,126,iw-8,12,5); big_text("HINDERNISSE",ip+8,128,6,1)
        bw2=iw-12; fill2=int(bw2*clamp((20-m.spawn_modifier)/20,0,1))
        pyxel.rect(ip+4,142,bw2,7,0); pyxel.rect(ip+4,142,fill2,7,9)
        pyxel.rectb(ip+4,142,bw2,7,5)
        total=len(MAPS); dot_x=cx-total*12
        for i in range(total):
            dc=m.diff_color if i==self.selected_map else 5
            pyxel.rect(dot_x+i*24,460,10,10,dc if i==self.selected_map else 1)
            pyxel.rectb(dot_x+i*24,460,10,10,dc)
        blink=(pyxel.frame_count//20)%2; bc=11 if blink else 10
        bw3=180; bx3=cx-bw3//2
        pyxel.rect(bx3,472,bw3,24,0); pyxel.rectb(bx3,472,bw3,24,bc)
        et="ENTER = STARTEN"; etw=big_text_w(et,2)
        big_text(et,cx-etw//2,479,bc,2)
        if self.selected_map>0:
            pyxel.rect(0,230,20,24,8); pyxel.rectb(0,230,20,24,7); pyxel.text(7,238,"<",7)
        if self.selected_map<len(MAPS)-1:
            pyxel.rect(492,230,20,24,8); pyxel.rectb(492,230,20,24,7); pyxel.text(499,238,">",7)

    def draw_map_deco_preview(self,m,rp,ry):
        style=m.deco_style
        sc=int(self.road_scroll)%40
        if style=="highway":
            for yy in range(ry,ry+400,40):
                pyxel.rect(rp-18,yy+sc%40,6,9,m.deco_color)
                pyxel.rect(rp+112,yy+sc%40,6,9,m.deco_color)
        elif style=="city":
            for yy in range(ry,ry+400,44):
                self.draw_building_small(rp-20,yy+(sc//2)%44)
                self.draw_building_small(rp+108,yy+(sc//2)%44)
        elif style=="desert":
            for yy in range(ry,ry+400,55):
                self.draw_cactus_small(rp-18,yy+(sc//2)%55)
                self.draw_rock_small(rp+108,yy+(sc//2)%55+8)

    def draw_play(self, ox, oy):
        pyxel.cls(self.map_spec.bg_color)
        if self.flash_timer>0 and self.flash_timer%2==0: pyxel.cls(8)
        self.draw_environment(ox,oy)
        self.draw_coins_obj(ox,oy)
        self.draw_powerup_objects(ox,oy)
        self.draw_obstacles_obj(ox,oy)
        self.draw_oncoming(ox,oy)
        self.draw_tumbleweeds(ox,oy)
        self.draw_particles()
        # Polizeiauto ist im Einzelspieler-Modus die ganze Zeit sichtbar,
        # direkt hinter dem Spieler. Der Abstand (police_distance) bestimmt
        # nur die kleine Differenz der Y-Position (siehe update_police).
        v=self.garage.current_vehicle()
        if v.vtype=="moto": draw_police_moto(int(self.police_x+ox),int(self.police_y+oy))
        else:               draw_police_sprite(int(self.police_x+ox),int(self.police_y+oy))
        if self.active_shield>0:
            r=8+(pyxel.frame_count//4)%3
            pyxel.rectb(int(self.player_x+ox)-r,int(self.player_y+oy)-r,r*2,r*2,12)
        if self.active_slowmo>0:
            if (pyxel.frame_count//4)%2==0:
                pyxel.rectb(0,0,SCREEN_W,SCREEN_H,10)
                pyxel.rectb(1,1,SCREEN_W-2,SCREEN_H-2,10)
        blink=self.player_iframes>0 and (pyxel.frame_count//3)%2==0
        if not blink:
            spec=self.player_spec
            if self.active_nitro>0:
                for i in range(1,4):
                    pyxel.pset(int(self.player_x+ox)+random.randint(-2,2),
                               int(self.player_y+oy)+spec.height//2+i*2,9)
            draw_player_vehicle(spec,int(self.player_x+ox),int(self.player_y+oy))
        self.draw_hud()
        if self.popup_timer>0:
            x=ROAD_CENTER-len(self.popup_text)*2
            y=int(self.player_y-22-(1.0-self.popup_timer/80.0)*10)
            pyxel.text(x,y,self.popup_text,self.popup_col)

    def draw_2p(self, ox, oy):
        m=self.map_spec
        pyxel.cls(m.bg_color)
        if self.flash_timer>0 and self.flash_timer%2==0: pyxel.cls(8)
        self.draw_environment(ox,oy)
        self.draw_obstacles_obj(ox,oy)
        self.draw_particles()
        v=self.garage.current_vehicle()
        draw_player_vehicle(v,int(self.p1_x+ox),int(self.p1_y+oy))
        if v.vtype=="moto": draw_police_moto(int(self.p2_x+ox),int(self.p2_y+oy))
        else:               draw_police_sprite(int(self.p2_x+ox),int(self.p2_y+oy))
        pyxel.text(4,4,"P1: WASD  HP:"+str(getattr(self,"p1_hp",5)),11)
        pyxel.text(4,14,"P2: PFEILE  Fange P1!",8)
        pyxel.text(4,24,"ZIEL "+str(int(self.distance))+"/"+str(int(getattr(self,"target_dist",3000))),7)
        pyxel.text(SCREEN_W-80,4,"CATCHES:"+str(getattr(self,"p2_catch_count",0)),8)
        pyxel.text(SCREEN_W//2-30,SCREEN_H-12,"ESC = Beenden",5)

    def draw_game_over(self):
        self.draw_environment(0,0)
        pyxel.rect(0,0,SCREEN_W,SCREEN_H,0)
        for i in range(0,SCREEN_H,4):
            pyxel.rect(0,i,SCREEN_W,2,self.map_spec.bg_color)
        cx=SCREEN_W//2
        pw=360; ph=300; px2=cx-pw//2; py2=SCREEN_H//2-ph//2
        pyxel.rect(px2,py2,pw,ph,1)
        pyxel.rectb(px2,py2,pw,ph,8)
        pyxel.rectb(px2+1,py2+1,pw-2,ph-2,9)
        header_col = 11 if self.game_won else 8
        header_txt = "GEWONNEN!" if self.game_won else "GAME OVER"
        reason_txt = self.game_over_reason
        pyxel.rect(px2+4,py2+4,pw-8,36,header_col)
        gow=big_text_w(header_txt,3)
        big_text(header_txt,cx-gow//2+1,py2+10,header_col,3)
        big_text(header_txt,cx-gow//2,py2+9,7,3)
        rw2=big_text_w(reason_txt,2)
        big_text(reason_txt,cx-rw2//2,py2+48,header_col,2)
        rows=[
            ("MAP:    "+self.map_spec.name,6),
            ("DISTANZ:"+str(int(self.distance))+" m",7),
            ("SCORE:  "+str(self.score),10),
            ("COINS:  +"+str(self.run_coins)+"  GESAMT:"+str(self.garage.coins),9),
            ("HS:     "+str(self.highscore),13),
        ]
        for i,(txt,col) in enumerate(rows):
            ry2=py2+68+i*24
            pyxel.rect(px2+10,ry2,pw-20,18,5)
            pyxel.rectb(px2+10,ry2,pw-20,18,13)
            tw2=big_text_w(txt,2); big_text(txt,cx-tw2//2,ry2+5,col,2)
        if self.score>0 and self.score>=self.highscore:
            pyxel.rect(px2+10,py2+194,pw-20,16,9)
            nr=big_text_w("NEW RECORD!",2); big_text("NEW RECORD!",cx-nr//2,py2+198,7,2)
        blink=(pyxel.frame_count//20)%2; bc=11 if blink else 10
        bw2=220; bx2=cx-bw2//2; by2=py2+ph-44
        pyxel.rect(bx2,by2,bw2,28,8); pyxel.rectb(bx2,by2,bw2,28,bc)
        et2=big_text_w("ENTER = WEITER",2); big_text("ENTER = WEITER",cx-et2//2,by2+9,bc,2)

    def draw_environment(self, ox=0, oy=0):
        m=self.map_spec
        pyxel.rect(0,0,SCREEN_W,SCREEN_H,m.side_color)
        pyxel.rect(ROAD_LEFT-5+ox,0,5,SCREEN_H,m.shoulder_color)
        pyxel.rect(ROAD_RIGHT+ox,0,5,SCREEN_H,m.shoulder_color)
        road_col=m.road_color if m.road_color else 0
        pyxel.rect(ROAD_LEFT+ox,0,ROAD_W,SCREEN_H,road_col)
        pyxel.line(ROAD_LEFT+ox+1,0,ROAD_LEFT+ox+1,SCREEN_H,13)
        pyxel.line(ROAD_RIGHT+ox-1,0,ROAD_RIGHT+ox-1,SCREEN_H,13)
        scroll=int(self.road_scroll)%28
        for y in range(-28,SCREEN_H+28,28):
            yy=y+scroll
            pyxel.rect(int(LANES[1]+ox-1),yy,2,14,m.lane_color)
        style=m.deco_style; fc=pyxel.frame_count
        sc=int(self.road_scroll)
        if style=="highway":
            for y in range(-20,SCREEN_H+20,20):
                yy=y+(sc//2)%20
                pyxel.rect(8,yy,5,7,m.deco_color)
                pyxel.rect(SCREEN_W-13,yy,5,7,m.deco_color)
            pyxel.line(14,0,14,SCREEN_H,13)
            pyxel.line(SCREEN_W-15,0,SCREEN_W-15,SCREEN_H,13)
            for y in range(-40,SCREEN_H+40,34):
                yy=y+(sc//3)%34
                pyxel.rect(18,yy,8,12,5); pyxel.rectb(18,yy,8,12,1)
                pyxel.rect(SCREEN_W-26,yy,8,12,5); pyxel.rectb(SCREEN_W-26,yy,8,12,1)
        elif style=="city":
            for y in range(-28,SCREEN_H+28,26):
                yy=y+(sc//2)%26
                self.draw_building_small(4,yy)
                self.draw_building_small(SCREEN_W-18,yy)
            pyxel.rect(16,0,3,SCREEN_H,5); pyxel.rect(SCREEN_W-19,0,3,SCREEN_H,5)
            cross_col=8 if (fc//30)%2==0 else 10
            for y in range(20,SCREEN_H,80):
                pyxel.rect(18,y,4,8,cross_col); pyxel.rect(19,y+8,2,2,7)
        elif style=="desert":
            for y in range(-22,SCREEN_H+22,22):
                yy=y+(sc//2)%22
                self.draw_cactus_small(6,yy)
                self.draw_rock_small(SCREEN_W-18,yy+5)
            rng=random.Random(int(sc/3))
            for _ in range(4):
                pyxel.pset(rng.randint(ROAD_RIGHT+6,SCREEN_W-5),rng.randint(0,SCREEN_H),9)
                pyxel.pset(rng.randint(2,ROAD_LEFT-6),rng.randint(0,SCREEN_H),9)
            ha=(fc//4)%3
            for y in range(0,SCREEN_H,24):
                if ha!=2:
                    pyxel.rect(ROAD_RIGHT+12+ha,y,2,5,9)
                    pyxel.rect(ROAD_LEFT-16+ha,y,2,5,9)
        finish_y=int(self.player_y-(getattr(self,"target_dist",3000)-self.distance)*0.8)
        if 0<finish_y<SCREEN_H: self.draw_finish_line(finish_y)

    def draw_building_small(self,x,y):
        pyxel.rect(x,y,12,18,5); pyxel.rectb(x,y,12,18,1)
        pyxel.rect(x+2,y,8,3,13)
        for row in range(2):
            for col2 in range(2):
                wx=x+2+col2*4; wy=y+5+row*5
                lit=(pyxel.frame_count//30+row+col2)%3!=0
                pyxel.rect(wx,wy,2,2,7 if lit else 10)

    def draw_cactus_small(self,x,y):
        pyxel.rect(x+2,y,3,10,3)
        pyxel.rect(x,y+3,3,2,3); pyxel.rect(x+5,y+4,3,2,3)
        pyxel.pset(x+3,y-1,11)

    def draw_rock_small(self,x,y):
        pyxel.rect(x-4,y-2,9,5,13); pyxel.rect(x-3,y-3,7,7,13)
        pyxel.rect(x-3,y-3,6,6,5); pyxel.rect(x-1,y-2,3,2,7)

    def draw_coins_obj(self,ox,oy):
        for c in self.coins_list:
            draw_coin_sprite(int(c.x+ox),int(c.y+oy),c.bob)

    def draw_obstacles_obj(self,ox,oy):
        for ob in self.obstacles:
            draw_obstacle_sprite(ob.kind,ob.x+ox,ob.y+oy)

    def draw_oncoming(self,ox,oy):
        for oc in self.oncoming:
            draw_car_small(int(oc.x+ox),int(oc.y+oy),oc.col,flip=True)

    def draw_tumbleweeds(self,ox,oy):
        for tw in self.tumbleweeds:
            x,y=int(tw.x+ox),int(tw.y+oy)
            spin=(tw.spin//45)%4
            cols=[9,4,9,4]
            pyxel.rect(x-5,y-5,11,11,cols[spin])
            pyxel.rect(x-4,y-6,9,13,cols[spin])
            pyxel.rect(x-6,y-4,13,9,cols[spin])
            pyxel.rect(x-3,y-3,7,7,cols[(spin+1)%4])
            pyxel.line(x-3,y-3,x+3,y+3,9); pyxel.line(x+3,y-3,x-3,y+3,9)
            pyxel.line(x,y-4,x,y+4,9); pyxel.line(x-4,y,x+4,y,9)

    def draw_powerup_objects(self,ox,oy):
        for pu in self.powerups:
            draw_powerup_sprite(pu.kind,int(pu.x+ox),int(pu.y+oy))

    def draw_particles(self):
        for p in self.particles:
            if p.life > 0:
                alpha=p.life/p.max_life
                if alpha>0.5 or (pyxel.frame_count//2)%2==0:
                    pyxel.pset(int(p.x),int(p.y),p.col)

    def draw_hud(self):
        pyxel.rect(2,2,98,64,1); pyxel.rectb(2,2,98,64,7)
        pyxel.text(8,8,"HP",6); self.draw_hp_bar(20,6,self.player_hp,self.player_spec.hp)
        # FIX 2: replaced f-strings with explicit str() for Pyxel Studio compatibility
        pyxel.text(8,18,"SPD "+str(round(self.player_speed,2)),7)
        combo_col=10 if self.combo>=4 else (9 if self.combo>=2 else 7)
        pyxel.text(8,28,"x"+str(self.combo)+" COMBO",combo_col)
        pyxel.text(8,38,"PTS "+str(self.score),10)
        pyxel.text(8,48,"COINS "+str(self.garage.coins),9)
        if self.active_slowmo>0:
            lbl="ZEITLUPE!"; col=10
        else:
            lbl="ZIEL "+str(int(self.distance))+"/"+str(int(self.target_dist))
            col=11 if self.distance/self.target_dist>0.8 else 6
        pyxel.text(8,58,lbl,col)
        rp=SCREEN_W-100
        pyxel.rect(rp,2,98,64,1); pyxel.rectb(rp,2,98,64,7)
        dist_frac=self.police_distance/200.0
        bar_col=11 if dist_frac>0.5 else (10 if dist_frac>0.25 else 8)
        plbl="POLIZEI!!!" if self.police_distance<40 else "POLIZEI"
        pcol=8 if self.police_distance<40 else 7
        pyxel.text(rp+4,8,plbl,pcol)
        pyxel.rect(rp+4,18,88,6,0)
        pyxel.rect(rp+4,18,int(88*dist_frac),6,bar_col)
        pyxel.rectb(rp+4,18,88,6,7)
        pyxel.text(rp+4,28,"DIST "+str(int(self.police_distance)),bar_col)
        if self.police_oil_timer>0: pyxel.text(rp+4,38,"GEBREMST!",2)
        pyxel.text(rp+4,58,self.player_spec.name[:12],6)
        icon_x=ROAD_LEFT+2
        if self.active_shield>0:
            frac=self.active_shield/PU_DURATION[PU_SHIELD]
            self.draw_pu_icon(icon_x,SCREEN_H-18,PU_SHIELD,frac); icon_x+=24
        if self.active_nitro>0:
            frac=self.active_nitro/PU_DURATION[PU_NITRO]
            self.draw_pu_icon(icon_x,SCREEN_H-18,PU_NITRO,frac); icon_x+=24
        if self.active_slowmo>0:
            frac=self.active_slowmo/PU_DURATION[PU_TIME]
            self.draw_pu_icon(icon_x,SCREEN_H-18,PU_TIME,frac)
        bw=60; bx=ROAD_CENTER-bw//2; by=SCREEN_H-8
        pyxel.rect(bx,by,bw,5,1)
        fill=int(bw*clamp(self.player_speed/max(self.player_spec.max_speed,0.01),0,1))
        pyxel.rect(bx,by,fill,5,11 if fill<bw*0.6 else 9)
        pyxel.rectb(bx,by,bw,5,7)

    def draw_pu_icon(self,x,y,kind,frac):
        col=PU_COLORS[kind]
        pyxel.rect(x,y,20,12,1); pyxel.rectb(x,y,20,12,col)
        pyxel.text(x+2,y+3,PU_LABELS[kind],col)
        pyxel.rect(x+1,y+9,18,2,0); pyxel.rect(x+1,y+9,int(18*frac),2,col)

    def draw_hp_bar(self,x,y,hp,max_hp):
        bw=76; pyxel.rect(x,y,bw,6,0)
        frac=hp/max_hp if max_hp else 0
        col=11 if frac>0.6 else (10 if frac>0.3 else 8)
        pyxel.rect(x,y,int(bw*frac),6,col)
        pyxel.rectb(x,y,bw,6,7)

    def draw_finish_line(self,fy):
        fw=ROAD_W; fx=ROAD_LEFT; fc2=pyxel.frame_count
        tile=10
        for tx in range(fx,fx+fw,tile):
            col=7 if ((tx-fx)//tile+fy//tile)%2==0 else 0
            pyxel.rect(tx,fy-4,tile,8,col)
        pole_l=fx-6; pole_r=fx+fw+2
        pyxel.rect(pole_l,fy-40,4,44,7); pyxel.rect(pole_r,fy-40,4,44,7)
        fw2=(fc2//5)%4
        for fy3 in range(0,18,3):
            fc3=8 if (fy3//3)%2==0 else 7
            pyxel.rect(pole_l+4,fy-40+fy3,24+fw2*2,3,fc3)
            pyxel.rect(pole_r+4,fy-40+fy3,24+fw2*2,3,fc3)
        flbl="ZIEL"; flw=big_text_w(flbl,2)
        big_text(flbl,ROAD_CENTER-flw//2+1,fy-52,3,2)
        big_text(flbl,ROAD_CENTER-flw//2,fy-53,11,2)


HighwayEscape()