import pyxel
import random
import math

WIDTH, HEIGHT = 256, 192
FPS = 30

C_BLACK=0;C_DARKBL=1;C_PURPLE=2;C_DKGRN=3;C_BROWN=4
C_DKGRAY=5;C_LGRAY=6;C_WHITE=7;C_RED=8;C_ORANGE=9
C_YELLOW=10;C_GREEN=11;C_BLUE=12;C_INDIGO=13;C_PINK=14;C_PEACH=15

INGREDIENTS=["Mehl","Ei","Zucker","Schoko","Wasser","Salz","Butter","Milch"]
INGR_COLS=[C_WHITE,C_ORANGE,C_YELLOW,C_BROWN,C_BLUE,C_LGRAY,C_YELLOW,C_WHITE]
BAD_ITEMS=["Muell","Bombe","Stein","Pilz"]

RECIPES={
    "Kuchen":["Mehl","Ei","Zucker","Butter"],
    "Keks":["Mehl","Zucker","Schoko"],
    "Brot":["Mehl","Wasser","Salz"],
    "Muffin":["Mehl","Ei","Milch","Zucker"],
    "Pancake":["Mehl","Ei","Milch"],
    "Brownie":["Schoko","Butter","Zucker","Ei"],
    "Salzbrezel":["Mehl","Salz","Wasser"],
}
RECIPE_BONUS={"Kuchen":200,"Keks":120,"Brot":100,"Muffin":170,"Pancake":140,"Brownie":220,"Salzbrezel":110}
RECIPE_COLOR={
    "Kuchen":C_PEACH,"Keks":C_BROWN,"Brot":C_ORANGE,
    "Muffin":C_PINK,"Pancake":C_YELLOW,
    "Brownie":C_BROWN,"Salzbrezel":C_LGRAY}
RECIPE_COLOR2={
    "Kuchen":C_YELLOW,"Keks":C_ORANGE,"Brot":C_BROWN,
    "Muffin":C_RED,"Pancake":C_ORANGE,
    "Brownie":C_DKGRAY,"Salzbrezel":C_WHITE}

ORDER_TIME=FPS*40; BAKE_TIME=FPS*6
MIX_KEYS=[pyxel.KEY_Q,pyxel.KEY_W,pyxel.KEY_E,pyxel.KEY_R,pyxel.KEY_T,pyxel.KEY_Z,pyxel.KEY_U,pyxel.KEY_I]
MIX_KEY_NAMES=["Q","W","E","R","T","Z","U","I"]
DROP_KEYS=[pyxel.KEY_A,pyxel.KEY_S,pyxel.KEY_D,pyxel.KEY_F,pyxel.KEY_G,pyxel.KEY_H]
DROP_KEY_NAMES=["A","S","D","F","G","H"]
MIX_CLICKS=6; BASKET_W=36; BASKET_H=14; PLAYER_SPEED=3
INGR_SPEED=1.8; MAX_ORDERS=3; MAX_INV=8; CLOUD_PLAT_Y=HEIGHT-42
MAZE_ROWS=7; MAZE_COLS=9; CELL_W=24; CELL_H=18
MAZE_OX=(WIDTH-MAZE_COLS*CELL_W)//2; MAZE_OY=24

CHARS=[
    {"name":"Koch","desc":"Der klassische Backer",
     "hat_col":C_WHITE,"hat_rim":C_LGRAY,"body_col":C_WHITE,"body_rim":C_LGRAY,
     "skin_col":C_PEACH,"skin_rim":C_BROWN,"apron_col":C_LGRAY,"shoe_col":C_BROWN,
     "eye_col":C_BLACK,"mouth_col":C_RED,"basket_col":C_BROWN,"basket_pat":C_PEACH,"name_col":C_ORANGE},
    {"name":"Magma","desc":"Meister der Hitze",
     "hat_col":C_RED,"hat_rim":C_ORANGE,"body_col":C_ORANGE,"body_rim":C_RED,
     "skin_col":C_ORANGE,"skin_rim":C_RED,"apron_col":C_YELLOW,"shoe_col":C_RED,
     "eye_col":C_YELLOW,"mouth_col":C_YELLOW,"basket_col":C_RED,"basket_pat":C_ORANGE,"name_col":C_RED},
    {"name":"Ozean","desc":"Kuhler Ozean Koch",
     "hat_col":C_BLUE,"hat_rim":C_INDIGO,"body_col":C_BLUE,"body_rim":C_INDIGO,
     "skin_col":C_BLUE,"skin_rim":C_DARKBL,"apron_col":C_LGRAY,"shoe_col":C_INDIGO,
     "eye_col":C_WHITE,"mouth_col":C_WHITE,"basket_col":C_INDIGO,"basket_pat":C_BLUE,"name_col":C_BLUE},
]

def draw_mehl(cx,cy):
    pyxel.rect(cx-5,cy-4,11,10,C_WHITE);pyxel.rect(cx+3,cy-4,2,10,C_LGRAY)
    pyxel.rect(cx-3,cy-8,7,5,C_WHITE);pyxel.line(cx-4,cy+1,cx+1,cy+1,C_LGRAY)
    pyxel.rectb(cx-5,cy-4,11,11,C_LGRAY)
def draw_ei(cx,cy):
    pyxel.circ(cx,cy+2,5,C_ORANGE);pyxel.circ(cx,cy-1,4,C_ORANGE)
    pyxel.circ(cx,cy-3,3,C_PEACH);pyxel.pset(cx-2,cy-4,C_WHITE);pyxel.circb(cx,cy+1,6,C_BROWN)
def draw_zucker(cx,cy):
    pyxel.rect(cx-7,cy,15,6,C_WHITE);pyxel.rect(cx-8,cy-2,17,3,C_WHITE)
    pyxel.rect(cx-6,cy+6,13,2,C_LGRAY);pyxel.rectb(cx-8,cy-2,17,9,C_LGRAY)
    for i in range(-5,6,2):pyxel.pset(cx+i,cy-1,C_YELLOW)
    pyxel.rect(cx-4,cy+1,5,2,C_YELLOW)
def draw_schoko(cx,cy):
    pyxel.rect(cx-6,cy-4,13,10,C_BROWN);pyxel.rectb(cx-6,cy-4,13,10,C_BLACK)
    pyxel.line(cx-5,cy+1,cx+5,cy+1,C_BLACK);pyxel.line(cx-1,cy-4,cx-1,cy+5,C_BLACK)
    pyxel.line(cx+3,cy-4,cx+3,cy+5,C_BLACK);pyxel.pset(cx-4,cy-3,C_ORANGE)
def draw_wasser(cx,cy):
    pyxel.rect(cx-3,cy-3,8,11,C_BLUE);pyxel.rect(cx-2,cy-7,5,5,C_BLUE)
    pyxel.rect(cx-1,cy-10,3,4,C_INDIGO);pyxel.rectb(cx-3,cy-3,8,11,C_DARKBL)
    pyxel.rectb(cx-2,cy-7,5,5,C_DARKBL);pyxel.line(cx-2,cy-2,cx-2,cy+7,C_WHITE)
def draw_salz(cx,cy):
    pyxel.rect(cx-3,cy-3,8,10,C_WHITE);pyxel.rect(cx-2,cy-6,6,4,C_WHITE)
    pyxel.rectb(cx-3,cy-3,8,10,C_LGRAY);pyxel.rectb(cx-2,cy-6,6,4,C_LGRAY)
def draw_butter(cx,cy):
    pyxel.rect(cx-6,cy-3,13,8,C_YELLOW);pyxel.rectb(cx-6,cy-3,13,8,C_BROWN)
    pyxel.rect(cx-4,cy-1,5,3,C_ORANGE);pyxel.text(cx-3,cy,"B",C_BROWN)
def draw_milch(cx,cy):
    pyxel.rect(cx-3,cy-4,8,11,C_WHITE);pyxel.rect(cx-2,cy-7,6,4,C_WHITE)
    pyxel.rectb(cx-3,cy-4,8,11,C_LGRAY);pyxel.rectb(cx-2,cy-7,6,4,C_LGRAY)
    pyxel.pset(cx+1,cy+1,C_BLUE)
def draw_muell(cx,cy):
    pyxel.rect(cx-5,cy-1,11,8,C_DKGRAY);pyxel.rect(cx-4,cy-3,9,3,C_DKGRAY)
    pyxel.rectb(cx-5,cy-1,11,8,C_BLACK);pyxel.rectb(cx-4,cy-3,9,3,C_BLACK)
    pyxel.line(cx-1,cy-3,cx-1,cy-5,C_LGRAY);pyxel.line(cx+1,cy-3,cx+1,cy-5,C_LGRAY)
    pyxel.line(cx-2,cy-5,cx+2,cy-5,C_LGRAY)
    pyxel.line(cx-2,cy+1,cx+2,cy+5,C_RED);pyxel.line(cx+2,cy+1,cx-2,cy+5,C_RED)
def draw_bombe(cx,cy):
    pyxel.circ(cx,cy+1,6,C_BLACK);pyxel.circb(cx,cy+1,6,C_DKGRAY)
    pyxel.line(cx+3,cy-3,cx+5,cy-6,C_BROWN);pyxel.pset(cx+5,cy-6,C_YELLOW)
def draw_stein(cx,cy):
    pyxel.circ(cx,cy,6,C_DKGRAY);pyxel.circb(cx,cy,6,C_BLACK)
    pyxel.pset(cx-2,cy-2,C_LGRAY);pyxel.line(cx-3,cy+1,cx-1,cy+3,C_BLACK)
def draw_pilz(cx,cy):
    pyxel.circ(cx,cy-1,6,C_PURPLE);pyxel.rect(cx-2,cy+4,6,4,C_PEACH)
    pyxel.circb(cx,cy-1,6,C_INDIGO);pyxel.pset(cx-2,cy-1,C_WHITE)

INGR_DRAW_FNS=[draw_mehl,draw_ei,draw_zucker,draw_schoko,draw_wasser,draw_salz,draw_butter,draw_milch]
BAD_DRAW_FNS=[draw_muell,draw_bombe,draw_stein,draw_pilz]

def draw_ingredient_sprite(name,cx,cy):
    if name in INGREDIENTS: INGR_DRAW_FNS[INGREDIENTS.index(name)](cx,cy)
    elif name in BAD_ITEMS: BAD_DRAW_FNS[BAD_ITEMS.index(name)](cx,cy)

def draw_panel(x,y,w,h,col_bg=C_DARKBL,col_border=C_WHITE):
    pyxel.rect(x,y,w,h,col_bg);pyxel.rectb(x,y,w,h,col_border)
def centered_text(y,text,col=C_WHITE):
    pyxel.text((WIDTH-len(text)*4)//2,y,text,col)
def centered_text_in(rx,ry,rw,text,col):
    pyxel.text(rx+(rw-len(text)*4)//2,ry,text,col)

def draw_chef(cx,cy,tick=0,holding=False,char=None):
    if char is None:char=CHARS[0]
    hc=char["hat_col"];hr=char["hat_rim"];bc=char["body_col"];br=char["body_rim"]
    sc=char["skin_col"];sr=char["skin_rim"];shc=char["shoe_col"]
    ec=char["eye_col"];mc=char["mouth_col"]
    lo=int(math.sin(tick*0.35)*2)
    pyxel.rect(cx-3,cy+8,3,5+lo,bc);pyxel.rectb(cx-3,cy+8,3,5+lo,br)
    pyxel.rect(cx+1,cy+8,3,5-lo,bc);pyxel.rectb(cx+1,cy+8,3,5-lo,br)
    pyxel.rect(cx-4,cy+12+lo,4,3,shc);pyxel.rect(cx+1,cy+12-lo,4,3,shc)
    pyxel.rect(cx-4,cy,9,9,bc);pyxel.rectb(cx-4,cy,9,9,br)
    pyxel.line(cx,cy+1,cx,cy+7,char["apron_col"])
    pyxel.pset(cx-2,cy+3,ec);pyxel.pset(cx+2,cy+3,ec)
    aw=int(math.sin(tick*0.3)*2)
    pyxel.rect(cx-7,cy+1+aw,4,3,sc);pyxel.rectb(cx-7,cy+1+aw,4,3,sr)
    if holding:
        pyxel.rect(cx+4,cy-1,4,3,sc);pyxel.rectb(cx+4,cy-1,4,3,sr)
        pyxel.rect(cx+7,cy-5,10,7,char["basket_col"]);pyxel.rectb(cx+7,cy-5,10,7,C_BLACK)
    else:
        pyxel.rect(cx+4,cy+1,4,3,sc);pyxel.rectb(cx+4,cy+1,4,3,sr)
    pyxel.circ(cx,cy-5,5,sc);pyxel.circb(cx,cy-5,5,sr)
    pyxel.pset(cx-2,cy-6,ec);pyxel.pset(cx+2,cy-6,ec)
    pyxel.pset(cx-1,cy-3,mc);pyxel.pset(cx,cy-3,mc);pyxel.pset(cx+1,cy-3,mc)
    pyxel.rect(cx-4,cy-14,9,8,hc);pyxel.rect(cx-6,cy-9,13,3,hc)
    pyxel.rectb(cx-6,cy-9,13,3,hr);pyxel.rectb(cx-4,cy-14,9,8,hr)
    if hc==C_RED:
        for fi in range(3):
            fx=cx-3+fi*3;fy=cy-15-int(abs(math.sin(tick*0.3+fi))*3)
            pyxel.pset(fx,fy,C_ORANGE);pyxel.pset(fx,fy-1,C_YELLOW)
    if hc==C_BLUE:
        for fi in range(2):pyxel.pset(cx-2+fi*4,cy-16,C_WHITE)

def draw_chef_big(cx,cy,tick,char):
    hc=char["hat_col"];hr=char["hat_rim"];bc=char["body_col"];br=char["body_rim"]
    sc=char["skin_col"];sr=char["skin_rim"];shc=char["shoe_col"]
    ec=char["eye_col"];mc=char["mouth_col"]
    lo=int(math.sin(tick*0.3)*3)
    pyxel.rect(cx-5,cy+10,4,7+lo,bc);pyxel.rectb(cx-5,cy+10,4,7+lo,br)
    pyxel.rect(cx+2,cy+10,4,7-lo,bc);pyxel.rectb(cx+2,cy+10,4,7-lo,br)
    pyxel.rect(cx-6,cy+16+lo,5,4,shc);pyxel.rect(cx+2,cy+16-lo,5,4,shc)
    pyxel.rect(cx-6,cy,13,11,bc);pyxel.rectb(cx-6,cy,13,11,br)
    pyxel.line(cx,cy+1,cx,cy+9,char["apron_col"])
    pyxel.pset(cx-3,cy+4,ec);pyxel.pset(cx+3,cy+4,ec)
    aw=int(math.sin(tick*0.3)*3)
    pyxel.rect(cx-10,cy+1+aw,5,4,sc);pyxel.rectb(cx-10,cy+1+aw,5,4,sr)
    pyxel.rect(cx+6,cy+1,5,4,sc);pyxel.rectb(cx+6,cy+1,5,4,sr)
    pyxel.circ(cx,cy-7,7,sc);pyxel.circb(cx,cy-7,7,sr)
    pyxel.pset(cx-3,cy-8,ec);pyxel.pset(cx+3,cy-8,ec)
    pyxel.pset(cx-2,cy-8,ec);pyxel.pset(cx+2,cy-8,ec)
    pyxel.line(cx-2,cy-4,cx+2,cy-4,mc)
    pyxel.rect(cx-5,cy-20,11,12,hc);pyxel.rect(cx-8,cy-12,17,4,hc)
    pyxel.rectb(cx-8,cy-12,17,4,hr);pyxel.rectb(cx-5,cy-20,11,12,hr)
    if hc==C_RED:
        for fi in range(3):
            fx=cx-3+fi*3;fy=cy-22-int(abs(math.sin(tick*0.3+fi))*4)
            pyxel.pset(fx,fy,C_ORANGE);pyxel.pset(fx,fy-1,C_YELLOW)
    if hc==C_BLUE:
        for fi in range(3):
            fx=cx-3+fi*3;fy=cy-22-int(abs(math.sin(tick*0.2+fi))*2)
            pyxel.pset(fx,fy,C_WHITE)

class BgCloud:
    def __init__(self,x=None):
        self.x=x if x is not None else random.randint(0,WIDTH)
        self.y=random.randint(8,HEIGHT//2-20);self.speed=random.uniform(0.08,0.25);self.r=random.randint(10,26)
    def update(self):
        self.x+=self.speed
        if self.x>WIDTH+self.r+12:self.x=-self.r-12;self.y=random.randint(8,HEIGHT//2-20)
    def draw(self):
        x,y,r=int(self.x),self.y,self.r
        pyxel.circ(x,y,r,C_WHITE);pyxel.circ(x+r,y-r//2,r*3//4,C_WHITE)
        pyxel.circ(x+r*2,y,r*3//4,C_WHITE);pyxel.rect(x,y,r*2+1,r,C_WHITE)

def draw_platform_cloud(y):
    pyxel.rect(0,y+14,WIDTH,22,C_WHITE);pyxel.rect(8,y+10,WIDTH-16,28,C_WHITE)
    bumps=[(20,y+6,24),(58,y+1,30),(98,y+7,22),(138,y+0,34),(178,y+4,26),(216,y+8,22),(248,y+10,18)]
    for bx,by,br in bumps:pyxel.circ(bx,by,br,C_WHITE)
    pyxel.rect(6,y+32,WIDTH-12,5,C_LGRAY);pyxel.line(0,y+30,WIDTH,y+30,C_LGRAY)
    for sx in range(16,WIDTH-16,20):pyxel.circ(sx,y+24,7,C_LGRAY)

class Particle:
    def __init__(self,x,y,col,vx=None,vy=None,life=None):
        self.x=float(x);self.y=float(y);self.col=col
        self.vx=vx if vx is not None else random.uniform(-2.0,2.0)
        self.vy=vy if vy is not None else random.uniform(-3.0,-0.5)
        self.life=life if life is not None else random.randint(10,22);self.max_life=self.life
    def update(self):
        self.x+=self.vx;self.y+=self.vy;self.vy+=0.18;self.life-=1
    def draw(self):
        x,y=int(self.x),int(self.y);pyxel.pset(x,y,self.col)
        if self.life>self.max_life*0.6:pyxel.pset(x+1,y,self.col)

class WaterDrop:
    def __init__(self,x,y):
        self.x=float(x)+random.uniform(-4,4);self.y=float(y)
        self.vy=random.uniform(1.5,3.5);self.vx=random.uniform(-1.0,1.0);self.life=random.randint(14,28)
    def update(self):
        self.x+=self.vx;self.y+=self.vy;self.vy+=0.22;self.life-=1
    def draw(self):
        x,y=int(self.x),int(self.y);pyxel.pset(x,y,C_BLUE);pyxel.pset(x,y+1,C_WHITE)

class FallingIngredient:
    def __init__(self,boost_needed=None,bad_chance=0.0,speed_mult=1.0):
        self.is_bad=bad_chance>random.random()
        if self.is_bad:self.name=BAD_ITEMS[random.randint(0,len(BAD_ITEMS)-1)]
        else:
            if boost_needed and 0.55>random.random():self.name=random.choice(boost_needed)
            else:self.name=INGREDIENTS[random.randint(0,len(INGREDIENTS)-1)]
        self.x=random.randint(16,WIDTH-16);self.y=-22.0
        self.speed=(INGR_SPEED+random.uniform(0,1.0))*speed_mult
        self.alive=True;self.wobble=random.uniform(0,math.pi*2);self.flash_tick=0
    def update(self):
        self.y+=self.speed;self.wobble+=0.07;self.flash_tick+=1
        if self.y>HEIGHT+24:self.alive=False
    def draw(self):
        ox=int(math.sin(self.wobble)*2);cx=self.x+ox;cy=int(self.y)
        if self.name=="Bombe" and (self.flash_tick//5)%2==0:pyxel.circb(cx,cy,9,C_RED)
        draw_ingredient_sprite(self.name,cx,cy)
        if self.is_bad and 50>self.y:pyxel.text(cx-2,cy-16,"!",C_RED)

class Order:
    def __init__(self,name):
        self.name=name;self.needed=list(RECIPES[name]);self.timer=ORDER_TIME;self.expired=False
    def update(self):
        self.timer-=1
        if 0>=self.timer:self.expired=True
    @property
    def timer_col(self):
        r=self.timer/ORDER_TIME
        if r>0.5:return C_GREEN
        if r>0.25:return C_YELLOW
        return C_RED
    def draw(self,x,y,fulfilled=False):
        tc=C_GREEN if fulfilled else self.timer_col;w=82
        draw_panel(x,y,w,48,C_DARKBL,tc)
        pyxel.text(x+3,y+3,self.name,C_YELLOW if not fulfilled else C_GREEN)
        n=len(self.needed);spacing=min(18,(w-8)//max(n,1))
        for i,ing in enumerate(self.needed):draw_ingredient_sprite(ing,x+8+i*spacing,y+22)
        ratio=max(0,self.timer/ORDER_TIME)
        pyxel.rect(x+2,y+40,int((w-4)*ratio),5,tc);pyxel.rectb(x+2,y+40,w-4,5,C_DKGRAY)
        if fulfilled:centered_text_in(x,y+30,w,"BEREIT!",C_GREEN)

class BakingAnimator:
    def __init__(self,order,char):
        self.order=order;self.char=char;self.step=0;self.tick=0
        self.next_key=random.randint(0,len(MIX_KEYS)-1)
        self.next_drop_key=random.randint(0,len(DROP_KEYS)-1)
        self.msg="Einlegen! Drucke [{}] (1/{})".format(DROP_KEY_NAMES[self.next_drop_key],len(order.needed))
        self.drop_idx=-1;self.drop_x=0.0;self.drop_y=0.0;self.drop_tx=0.0;self.drop_ty=0.0
        self.drop_done=[]
        self.bowl_cx=WIDTH*3//4;self.bowl_cy=125;self.bowl_w=90;self.bowl_h=32
        self.mix_count=0;self.mix_angle=0.0;self.mix_anim=0
        self.water_drops=[]
        self.oven_x=10;self.oven_y=45;self.oven_w=95;self.oven_h=75
        self.bake_timer=BAKE_TIME;self.particles=[];self.wrong_key_flash=0
        self.rcol=RECIPE_COLOR.get(order.name,C_PEACH)
        self.rcol2=RECIPE_COLOR2.get(order.name,C_ORANGE)
        n=order.name
        if n in("Kuchen","Muffin"):self.bake_style="bubbles"
        elif n in("Brownie",):self.bake_style="smoke"
        elif n in("Brot","Salzbrezel"):self.bake_style="steam"
        elif n=="Pancake":self.bake_style="sizzle"
        else:self.bake_style="sparkle"

    def update(self):
        self.tick+=1
        if self.wrong_key_flash>0:self.wrong_key_flash-=1
        for d in self.water_drops:d.update()
        self.water_drops=[d for d in self.water_drops if d.life>0]
        for p in self.particles:p.update()
        self.particles=[p for p in self.particles if p.life>0]
        if self.step==2 or self.mix_anim>0:
            self.mix_angle+=(0.5 if self.mix_anim>0 else 0.18)
        if self.mix_anim>0:
            self.mix_anim-=1
            if self.mix_anim==0 and self.mix_count>=MIX_CLICKS:
                self.step=4;self.msg="Teig fertig! In den Ofen [SPACE]"
        if self.step==1:
            self.drop_x+=(self.drop_tx-self.drop_x)*0.16
            self.drop_y+=(self.drop_ty-self.drop_y)*0.16
            if 4>math.hypot(self.drop_tx-self.drop_x,self.drop_ty-self.drop_y):
                ing=self.order.needed[self.drop_idx];self.drop_done.append(ing)
                col=INGR_COLS[INGREDIENTS.index(ing)] if ing in INGREDIENTS else C_WHITE
                if ing=="Wasser":
                    for _ in range(22):self.water_drops.append(WaterDrop(self.drop_tx,self.drop_ty-8))
                for _ in range(10):
                    self.particles.append(Particle(self.drop_tx,self.drop_ty,col,vy=random.uniform(-2.5,-0.3)))
                nd=len(self.drop_done);nn=len(self.order.needed)
                if nd>=nn:
                    self.step=2;self.next_key=random.randint(0,len(MIX_KEYS)-1)
                    self.msg="Mixen! Drucke [{}]".format(MIX_KEY_NAMES[self.next_key])
                else:
                    self.step=0;self.next_drop_key=random.randint(0,len(DROP_KEYS)-1)
                    self.msg="Einlegen [{}] ({}/{})".format(DROP_KEY_NAMES[self.next_drop_key],nd+1,nn)
        if self.step==5:
            self.bake_timer-=1
            if self.tick%3==0:
                for _ in range(2):
                    hx=random.randint(self.oven_x+12,self.oven_x+self.oven_w-12)
                    if self.bake_style=="bubbles":
                        self.particles.append(Particle(hx,self.oven_y,C_PINK,
                            vx=random.uniform(-0.3,0.3),vy=random.uniform(-1.8,-0.3),life=18))
                    elif self.bake_style=="smoke":
                        self.particles.append(Particle(hx,self.oven_y,C_DKGRAY,
                            vx=random.uniform(-0.5,0.5),vy=random.uniform(-1.5,-0.2),life=25))
                    elif self.bake_style=="steam":
                        self.particles.append(Particle(hx,self.oven_y,C_WHITE,
                            vx=random.uniform(-0.2,0.2),vy=random.uniform(-2.0,-0.5),life=20))
                    elif self.bake_style=="sizzle":
                        self.particles.append(Particle(hx,self.oven_y,C_ORANGE,
                            vx=random.uniform(-1.0,1.0),vy=random.uniform(-2.5,-0.5),life=12))
                    else:
                        pcol=C_ORANGE if random.random()>0.5 else C_YELLOW
                        self.particles.append(Particle(hx,self.oven_y,pcol,
                            vx=random.uniform(-0.4,0.4),vy=random.uniform(-2.2,-0.4),life=22))
            if 0>=self.bake_timer:
                self.step=6;self.msg="Fertig! Servieren [SPACE]"
                for _ in range(40):
                    self.particles.append(Particle(self.oven_x+self.oven_w//2,self.oven_y+self.oven_h//2,
                        random.choice([C_YELLOW,C_ORANGE,self.rcol,C_GREEN,C_WHITE]),
                        vx=random.uniform(-3,3),vy=random.uniform(-4,-0.5),life=45))

    def on_drop_key(self,key_idx):
        if self.step==0:
            if key_idx==self.next_drop_key:
                n=len(self.drop_done)
                if len(self.order.needed)>n:
                    ing=self.order.needed[n];self.drop_idx=n
                    self.drop_x=WIDTH//2;self.drop_y=20.0
                    offset=-28+n*22
                    self.drop_tx=float(self.bowl_cx+offset);self.drop_ty=float(self.bowl_cy-4)
                    self.step=1;self.msg="Gebe {} hinein...".format(ing)
                return True
            else:
                self.wrong_key_flash=15
                self.msg="FALSCH! Drucke [{}]".format(DROP_KEY_NAMES[self.next_drop_key]);return False
        return False

    def on_mix_key(self,key_idx):
        if self.step==2:
            if key_idx==self.next_key:
                self.mix_count+=1;self.mix_anim=12
                self.next_key=random.randint(0,len(MIX_KEYS)-1)
                rem=MIX_CLICKS-self.mix_count
                if rem>0:self.msg="Weiter! Drucke [{}]".format(MIX_KEY_NAMES[self.next_key])
                else:self.msg="Fast fertig..."
                return True
            else:
                self.wrong_key_flash=15
                self.msg="FALSCH! Drucke [{}]".format(MIX_KEY_NAMES[self.next_key]);return False
        return False

    def on_space(self):
        if self.step==4:
            self.step=5;self.bake_timer=BAKE_TIME;self.msg="Backt... warte..."
        elif self.step==6:return True
        return False

    def draw(self):
        for gy in range(HEIGHT):
            t=gy/HEIGHT;col=C_DARKBL if 0.25>t else C_BLUE;pyxel.line(0,gy,WIDTH,gy,col)
        for ty in range(0,HEIGHT-40,20):
            for tx in range(0,WIDTH,20):pyxel.rectb(tx,ty,20,20,C_DARKBL)
        draw_platform_cloud(HEIGHT-42)
        self._draw_oven();self._draw_bowl()
        if self.step==1:
            draw_ingredient_sprite(self.order.needed[self.drop_idx],int(self.drop_x),int(self.drop_y))
        for d in self.water_drops:d.draw()
        for p in self.particles:p.draw()
        draw_chef(WIDTH-22,HEIGHT-58,self.tick,False,self.char)
        pyxel.rect(0,0,WIDTH,16,C_BLACK);pyxel.line(0,16,WIDTH,16,C_DKGRAY)
        steps_lbl=["Einlegen","Mixen","Ofen","Backen","Fertig"]
        steps_gate=[0,2,4,5,6];sw=WIDTH//len(steps_lbl)
        for i,(lbl,gate) in enumerate(zip(steps_lbl,steps_gate)):
            done=self.step>gate;curr=self.step==gate
            col=C_GREEN if done else (C_YELLOW if curr else C_DKGRAY)
            sym="+" if done else (">" if curr else "-")
            pyxel.text(4+i*sw,5,"{}{}".format(sym,lbl),col)
            if len(steps_lbl)-1>i:pyxel.line((i+1)*sw-1,1,(i+1)*sw-1,15,C_DKGRAY)
        pts=RECIPE_BONUS.get(self.order.name,100);pyxel.text(WIDTH-46,5,"+{}P".format(pts),C_YELLOW)
        if self.step==0:
            kby=HEIGHT-52;pyxel.rect(WIDTH//2-60,kby,120,36,C_BLACK)
            pyxel.rectb(WIDTH//2-60,kby,120,36,C_YELLOW if self.wrong_key_flash==0 else C_RED)
            centered_text(kby+3,"ZUTAT EINLEGEN:",C_LGRAY)
            ing_name=self.order.needed[len(self.drop_done)] if len(self.order.needed)>len(self.drop_done) else "?"
            kn=DROP_KEY_NAMES[self.next_drop_key];bx2=WIDTH//2-8;by2=kby+13
            pyxel.rect(bx2,by2,17,17,C_YELLOW if self.wrong_key_flash==0 else C_RED)
            pyxel.rectb(bx2,by2,17,17,C_WHITE);pyxel.text(bx2+5,by2+5,kn,C_BLACK)
            pyxel.text(WIDTH//2-56,kby+31,"-> {}".format(ing_name),C_WHITE)
        elif self.step==2:
            kby=HEIGHT-52;pyxel.rect(WIDTH//2-50,kby,100,36,C_BLACK)
            pyxel.rectb(WIDTH//2-50,kby,100,36,C_YELLOW if self.wrong_key_flash==0 else C_RED)
            centered_text(kby+3,"MIXEN!",C_LGRAY)
            kn=MIX_KEY_NAMES[self.next_key];bx2=WIDTH//2-8;by2=kby+13
            pyxel.rect(bx2,by2,17,17,C_YELLOW if self.wrong_key_flash==0 else C_RED)
            pyxel.rectb(bx2,by2,17,17,C_WHITE);pyxel.text(bx2+5,by2+5,kn,C_BLACK)
            ratio=self.mix_count/MIX_CLICKS
            pyxel.rect(WIDTH//2-48,kby+32,int(96*ratio),4,C_GREEN)
            pyxel.rectb(WIDTH//2-48,kby+32,96,4,C_DKGRAY)
        pyxel.rect(0,HEIGHT-16,WIDTH,16,C_BLACK);pyxel.line(0,HEIGHT-16,WIDTH,HEIGHT-16,C_DKGRAY)
        msg_col=C_RED if self.wrong_key_flash>0 else C_WHITE
        centered_text(HEIGHT-10,self.msg,msg_col)

    def _draw_oven(self):
        ox,oy=self.oven_x,self.oven_y;ow,oh=self.oven_w,self.oven_h
        pyxel.rect(ox,oy,ow,oh,C_DKGRAY);pyxel.rectb(ox,oy,ow,oh,C_BLACK)
        pyxel.rect(ox,oy-12,ow,13,C_LGRAY);pyxel.rectb(ox,oy-12,ow,13,C_BLACK)
        for pi in range(2):
            px=ox+22+pi*46;pyxel.circ(px,oy-6,10,C_DKGRAY);pyxel.circb(px,oy-6,10,C_BLACK)
            if self.step==5:
                hcol=C_RED if (self.tick//4)%2==0 else C_ORANGE;pyxel.circb(px,oy-6,8,hcol)
        pyxel.rect(ox+7,oy+8,ow-14,oh-20,C_BLACK);pyxel.rectb(ox+7,oy+8,ow-14,oh-20,C_LGRAY)
        wgx,wgy=ox+11,oy+12;wgw,wgh=ow-22,oh-30
        pyxel.rect(wgx,wgy,wgw,wgh,C_DARKBL);pyxel.rectb(wgx,wgy,wgw,wgh,C_DKGRAY)
        pyxel.line(wgx+1,wgy+1,wgx+wgw-2,wgy+1,C_INDIGO)
        pyxel.rect(ox+4,oy+oh-12,ow-8,10,C_DKGRAY);pyxel.text(ox+ow//2-8,oy+3,"OFEN",C_YELLOW)
        ratio=1-self.bake_timer/BAKE_TIME if self.step==5 else 0
        if self.step>=5:
            gx=wgx+wgw//2-16;gy=wgy+wgh//2-5
            brow=self.rcol if 0.5>ratio else self.rcol2
            pyxel.rect(gx,gy,32,12,brow);pyxel.rectb(gx,gy,32,12,C_BLACK)
            rn=self.order.name
            if rn=="Kuchen":
                pyxel.circ(gx+8,gy-3,4,C_PINK);pyxel.circ(gx+20,gy-3,4,C_RED)
            elif rn=="Muffin":
                pyxel.rect(gx+4,gy-6,8,7,self.rcol);pyxel.rectb(gx+4,gy-6,8,7,C_BROWN)
                pyxel.rect(gx+18,gy-6,8,7,self.rcol);pyxel.rectb(gx+18,gy-6,8,7,C_BROWN)
            elif rn in("Brownie","Keks"):
                pyxel.line(gx+4,gy+6,gx+28,gy+6,C_BLACK);pyxel.line(gx+16,gy+1,gx+16,gy+11,C_BLACK)
            elif rn=="Pancake":
                for pi2 in range(3):pyxel.circ(gx+7+pi2*9,gy+6,3,C_YELLOW)
            elif rn=="Salzbrezel":
                pyxel.circ(gx+10,gy+4,5,C_BROWN);pyxel.line(gx+8,gy+2,gx+22,gy+2,C_BROWN)
            pyxel.rect(wgx,wgy+wgh+2,int(wgw*ratio),4,self.rcol)
            pyxel.rectb(wgx,wgy+wgh+2,wgw,4,C_DKGRAY)
        if self.step==6:
            gx=wgx+wgw//2-16;gy=wgy+wgh//2-8
            pyxel.rect(gx,gy,32,14,self.rcol2);pyxel.rect(gx+2,gy+2,28,5,self.rcol)
            pyxel.rectb(gx,gy,32,14,C_BLACK)

    def _draw_bowl(self):
        bx=self.bowl_cx-self.bowl_w//2;by=self.bowl_cy-self.bowl_h//2
        bw=self.bowl_w;bh=self.bowl_h
        for si in range(4):pyxel.rect(bx+si+3,by+bh+si,bw-si*2-4,2,C_DKGRAY)
        pyxel.rect(bx+6,by+6,bw-12,bh,C_WHITE);pyxel.rect(bx+3,by+3,bw-6,bh+2,C_WHITE)
        pyxel.rect(bx,by,bw,bh-2,C_WHITE);pyxel.rectb(bx,by,bw,bh,C_LGRAY)
        ix=bx+8;iy=by+8;iw=bw-16;ih=bh-12
        if self.step in[2,3,4] or self.mix_anim>0:pyxel.rect(ix,iy,iw,ih,C_PEACH)
        n=len(self.drop_done)
        for i,ing in enumerate(self.drop_done):
            cx=bx+14+i*max(1,(iw//max(n,1)));cy=by+bh//2
            draw_ingredient_sprite(ing,cx,cy)
        if self.step==2 or self.mix_anim>0:
            cx_b=self.bowl_cx;cy_b=by+bh//2+2
            for i in range(8):
                angle=self.mix_angle+i*(math.pi*2/8)
                rx=cx_b+int(math.cos(angle)*28);ry=cy_b+int(math.sin(angle)*10)
                col=INGR_COLS[i%len(INGR_COLS)];pyxel.circ(rx,ry,2,col)
            lx=cx_b+int(math.cos(self.mix_angle)*20);ly=cy_b+int(math.sin(self.mix_angle)*7)
            pyxel.rect(lx-1,ly-4,3,8,C_LGRAY);pyxel.circ(lx,ly+5,4,C_LGRAY)
        if self.step>=4:
            pyxel.rect(ix,iy,iw,ih,self.rcol);pyxel.text(bx+bw//2-8,by+bh//2-2,"TEIG",self.rcol2)

class PackagingAnim:
    def __init__(self,order_name,char):
        self.order_name=order_name;self.char=char;self.tick=0;self.done=False
        self.max_ticks=FPS*3;self.rcol=RECIPE_COLOR.get(order_name,C_PEACH)
        self.rcol2=RECIPE_COLOR2.get(order_name,C_ORANGE);self.particles=[]
    def update(self):
        self.tick+=1
        for p in self.particles:p.update()
        self.particles=[p for p in self.particles if p.life>0]
        if self.tick==30:
            for _ in range(20):
                self.particles.append(Particle(WIDTH//2,HEIGHT//2-10,
                    random.choice([C_YELLOW,C_GREEN,self.rcol]),
                    vx=random.uniform(-2,2),vy=random.uniform(-3,-0.5),life=25))
        if self.tick>=self.max_ticks:self.done=True
    def draw(self):
        for gy in range(HEIGHT):
            t=gy/HEIGHT;col=C_DARKBL if 0.28>t else C_BLUE;pyxel.line(0,gy,WIDTH,gy,col)
        draw_platform_cloud(CLOUD_PLAT_Y)
        p=self.tick/self.max_ticks;cx=WIDTH//2;cy=HEIGHT//2-10
        pyxel.circ(cx,cy+8,18,C_WHITE);pyxel.circb(cx,cy+8,18,C_LGRAY);pyxel.circ(cx,cy+8,12,C_LGRAY)
        rn=self.order_name
        if rn=="Kuchen":
            pyxel.rect(cx-14,cy-4,28,14,self.rcol);pyxel.rectb(cx-14,cy-4,28,14,C_BLACK)
            pyxel.rect(cx-12,cy-4,24,4,self.rcol2)
            pyxel.circ(cx-6,cy-6,3,C_RED);pyxel.circ(cx+4,cy-7,3,C_RED)
        elif rn=="Keks":
            for ki in range(5):
                kx=cx-16+ki*8;ky=cy+int(math.sin(ki*1.3)*3)
                pyxel.circ(kx,ky,4,self.rcol);pyxel.circb(kx,ky,4,C_BROWN)
        elif rn=="Brot":
            pyxel.rect(cx-16,cy-2,32,14,self.rcol);pyxel.rectb(cx-16,cy-2,32,14,C_BLACK)
            pyxel.circ(cx-8,cy-4,5,self.rcol2);pyxel.circ(cx+8,cy-4,5,self.rcol2);pyxel.circ(cx,cy-6,6,self.rcol2)
        elif rn=="Muffin":
            for mi in range(3):
                mx=cx-12+mi*12;pyxel.rect(mx-5,cy,10,10,self.rcol);pyxel.rectb(mx-5,cy,10,10,C_BROWN)
                pyxel.circ(mx,cy-3,5,self.rcol2);pyxel.circb(mx,cy-3,5,C_BROWN)
        elif rn=="Pancake":
            for pi2 in range(3):
                py2=cy+8-pi2*5
                pyxel.ellip(cx-14,py2-2,28,4,self.rcol);pyxel.ellipb(cx-14,py2-2,28,4,C_BROWN)
        elif rn=="Brownie":
            pyxel.rect(cx-16,cy-4,32,14,self.rcol);pyxel.rectb(cx-16,cy-4,32,14,C_BLACK)
            pyxel.line(cx-14,cy+3,cx+14,cy+3,C_BLACK);pyxel.line(cx,cy-4,cx,cy+9,C_BLACK)
        elif rn=="Salzbrezel":
            pyxel.circ(cx,cy+2,12,self.rcol);pyxel.circb(cx,cy+2,12,C_BLACK)
            pyxel.circ(cx,cy+2,6,C_DARKBL)
            pyxel.line(cx-6,cy-4,cx-2,cy+6,self.rcol2);pyxel.line(cx+6,cy-4,cx+2,cy+6,self.rcol2)
        if p>0.4:
            pp=(p-0.4)/0.3;bw2=int(48*min(1.0,pp));bh2=int(36*min(1.0,pp))
            bxx=cx-bw2//2;byy=cy-bh2//2+4;pyxel.rectb(bxx,byy,bw2,bh2,C_YELLOW)
            if pp>0.8:
                pyxel.line(bxx+4,byy+4,bxx+bw2-4,byy+4,C_YELLOW)
                pyxel.line(bxx+bw2//2,byy,bxx+bw2//2,byy+bh2,C_YELLOW)
        if p>0.65:
            chef_x=int(cx-50+(p-0.65)/0.35*20);chef_y=CLOUD_PLAT_Y-20
            draw_chef(chef_x,chef_y,self.tick,True,self.char)
            if p>0.8:
                bx2=chef_x+18;by2=chef_y-22
                pyxel.rect(bx2,by2,58,14,C_WHITE);pyxel.rectb(bx2,by2,58,14,C_BLACK)
                pyxel.text(bx2+3,by2+4,"Wird serviert!",C_BLACK)
        for pt in self.particles:pt.draw()
        pyxel.rect(0,0,WIDTH,14,C_BLACK);pyxel.line(0,14,WIDTH,14,C_DKGRAY)
        centered_text(4,"{} WIRD VERPACKT!".format(self.order_name.upper()),C_YELLOW)
        pyxel.rect(0,HEIGHT-12,WIDTH,12,C_BLACK);pyxel.line(0,HEIGHT-12,WIDTH,HEIGHT-12,C_DKGRAY)
        if self.tick>FPS*2:centered_text(HEIGHT-8,"[SPACE] Labyrinth starten",C_GREEN)
        else:centered_text(HEIGHT-8,"Endprodukt wird vorbereitet...",C_LGRAY)

def generate_maze(rows,cols):
    h_walls=[[True]*cols for _ in range(rows)];v_walls=[[True]*cols for _ in range(rows)]
    visited=[[False]*cols for _ in range(rows)]
    def neighbors(r,c):
        res=[]
        if r>0:res.append((r-1,c,'N'))
        if rows-1>r:res.append((r+1,c,'S'))
        if c>0:res.append((r,c-1,'W'))
        if cols-1>c:res.append((r,c+1,'E'))
        return res
    stack=[(0,0)];visited[0][0]=True
    while stack:
        r,c=stack[-1]
        unvisited=[(nr,nc,d) for nr,nc,d in neighbors(r,c) if not visited[nr][nc]]
        if not unvisited:stack.pop()
        else:
            nr,nc,d=random.choice(unvisited)
            if d=='S':h_walls[r][c]=False
            if d=='N':h_walls[nr][nc]=False
            if d=='E':v_walls[r][c]=False
            if d=='W':v_walls[nr][nc]=False
            visited[nr][nc]=True;stack.append((nr,nc))
    return h_walls,v_walls

class ServingMaze:
    def __init__(self,order_name,time_limit,char):
        self.order_name=order_name;self.char=char
        self.rows=MAZE_ROWS;self.cols=MAZE_COLS
        self.h_walls,self.v_walls=generate_maze(self.rows,self.cols)
        self.px=0;self.py=0;self.gx=self.cols-1;self.gy=self.rows-1
        self.done=False;self.failed=False;self.timer=time_limit;self.max_timer=time_limit
        self.move_cd=0;self.particles=[];self.tick=0;self.won=False;self.trail=[]
        self.rcol=RECIPE_COLOR.get(order_name,C_PEACH)
    def update(self):
        self.tick+=1;self.timer-=1
        for p in self.particles:p.update()
        self.particles=[p for p in self.particles if p.life>0]
        if 0>=self.timer and not self.done:self.done=True;self.failed=True;return
        if self.move_cd>0:self.move_cd-=1;return
        moved=False;r,c=self.py,self.px
        if pyxel.btn(pyxel.KEY_UP) or pyxel.btn(pyxel.KEY_W):
            if r>0 and not self.h_walls[r-1][c]:self.py-=1;moved=True
        elif pyxel.btn(pyxel.KEY_DOWN) or pyxel.btn(pyxel.KEY_S):
            if self.rows-1>r and not self.h_walls[r][c]:self.py+=1;moved=True
        elif pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.KEY_A):
            if c>0 and not self.v_walls[r][c-1]:self.px-=1;moved=True
        elif pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D):
            if self.cols-1>c and not self.v_walls[r][c]:self.px+=1;moved=True
        if moved:
            self.move_cd=4;self.trail.append((self.px,self.py))
            if len(self.trail)>20:self.trail.pop(0)
            wx=MAZE_OX+self.px*CELL_W+CELL_W//2;wy=MAZE_OY+self.py*CELL_H+CELL_H//2
            for _ in range(3):
                self.particles.append(Particle(wx,wy,self.char["hat_col"],
                    vx=random.uniform(-0.5,0.5),vy=random.uniform(-0.8,-0.1),life=7))
        if self.px==self.gx and self.py==self.gy and not self.done:
            self.done=True;self.won=True
            wx=MAZE_OX+self.gx*CELL_W+CELL_W//2;wy=MAZE_OY+self.gy*CELL_H+CELL_H//2
            for _ in range(70):
                self.particles.append(Particle(wx,wy,
                    random.choice([C_YELLOW,C_GREEN,C_ORANGE,C_WHITE,self.rcol]),
                    vx=random.uniform(-3,3),vy=random.uniform(-4,-0.5),life=45))
    def draw(self):
        ox=MAZE_OX;oy=MAZE_OY;cw=CELL_W;ch=CELL_H
        pyxel.rect(0,0,WIDTH,HEIGHT,C_BROWN)
        for tx in range(0,WIDTH,8):
            pyxel.line(tx,0,tx,HEIGHT,C_DKGRAY if tx%16==0 else C_BROWN)
        for ty in range(0,HEIGHT,20):pyxel.line(0,ty,WIDTH,ty,C_DKGRAY)
        pyxel.rect(0,0,WIDTH,oy-2,C_DKGRAY)
        for wx2 in range(0,WIDTH,12):pyxel.rectb(wx2,3,10,oy-7,C_LGRAY)
        pyxel.rect(0,oy+self.rows*ch+1,WIDTH,HEIGHT-(oy+self.rows*ch+1),C_ORANGE)
        pyxel.line(0,oy+self.rows*ch+1,WIDTH,oy+self.rows*ch+1,C_BROWN)
        for lx in range(0,WIDTH,16):
            pyxel.rectb(lx,oy+self.rows*ch+3,14,HEIGHT-(oy+self.rows*ch+5),C_BROWN)
        for r in range(self.rows):
            for c in range(self.cols):
                cx2=ox+c*cw;cy2=oy+r*ch;pyxel.rect(cx2+1,cy2+1,cw-1,ch-1,C_BLACK)
        for i,(tx,ty) in enumerate(self.trail):
            alpha=(i+1)/len(self.trail)
            if alpha>0.3:
                tc=ox+tx*cw+cw//2;tr=oy+ty*ch+ch//2;pyxel.rect(tc-2,tr-2,5,5,C_INDIGO)
        pyxel.rect(ox+1,oy+1,cw-1,ch-1,C_DKGRN);pyxel.text(ox+2,oy+ch//2-2,"ST",C_YELLOW)
        gx_px=ox+self.gx*cw;gy_px=oy+self.gy*ch
        pyxel.rect(gx_px+1,gy_px+1,cw-1,ch-1,C_DKGRN)
        mx=gx_px+cw//2;my=gy_px+ch//2
        pyxel.circ(mx,my,5,C_WHITE);pyxel.circb(mx,my,5,C_LGRAY);pyxel.circ(mx,my,2,self.rcol)
        for si in range(2):
            sx=mx-1+si*2;sy=my-6-int(abs(math.sin(self.tick*0.25+si*1.5))*2);pyxel.pset(sx,sy,C_LGRAY)
        for r in range(self.rows):
            for c in range(self.cols):
                cx2=ox+c*cw;cy2=oy+r*ch
                if r==0:pyxel.line(cx2,cy2,cx2+cw,cy2,C_WHITE)
                if c==0:pyxel.line(cx2,cy2,cx2,cy2+ch,C_WHITE)
                if self.h_walls[r][c]:
                    pyxel.line(cx2,cy2+ch,cx2+cw,cy2+ch,C_WHITE)
                    pyxel.line(cx2+1,cy2+ch+1,cx2+cw,cy2+ch+1,C_LGRAY)
                if self.v_walls[r][c]:
                    pyxel.line(cx2+cw,cy2,cx2+cw,cy2+ch,C_WHITE)
                    pyxel.line(cx2+cw+1,cy2+1,cx2+cw+1,cy2+ch,C_LGRAY)
        for p in self.particles:p.draw()
        px_scr=ox+self.px*cw+cw//2;py_scr=oy+self.py*ch+ch//2
        sc=self.char["skin_col"];sr=self.char["skin_rim"]
        hc=self.char["hat_col"];hr=self.char["hat_rim"];bc=self.char["body_col"]
        pyxel.rect(px_scr-2,py_scr,5,4,bc);pyxel.rectb(px_scr-2,py_scr,5,4,hr)
        pyxel.circ(px_scr,py_scr-3,3,sc);pyxel.circb(px_scr,py_scr-3,3,sr)
        pyxel.pset(px_scr-1,py_scr-4,self.char["eye_col"]);pyxel.pset(px_scr+1,py_scr-4,self.char["eye_col"])
        pyxel.rect(px_scr-2,py_scr-8,5,4,hc);pyxel.rect(px_scr-3,py_scr-6,7,2,hc)
        pyxel.rectb(px_scr-3,py_scr-6,7,2,hr)
        pyxel.rect(px_scr+2,py_scr-1,6,1,C_LGRAY);pyxel.circ(px_scr+5,py_scr-2,2,self.rcol)
        if hc==C_RED and 2>self.tick%4:pyxel.pset(px_scr,py_scr-9,C_ORANGE)
        pyxel.rect(0,0,WIDTH,MAZE_OY-1,C_BLACK);pyxel.line(0,MAZE_OY-1,WIDTH,MAZE_OY-1,C_DKGRAY)
        centered_text(4,"SERVIERE: {}!".format(self.order_name.upper()),C_YELLOW)
        pyxel.text(3,4,self.char["name"],self.char["name_col"])
        pyxel.text(WIDTH-40,4,"Ziel:unten",C_LGRAY)
        ratio=max(0,self.timer/self.max_timer)
        tc_bar=C_GREEN if ratio>0.5 else (C_YELLOW if ratio>0.25 else C_RED)
        bar_y=oy+self.rows*ch+4
        pyxel.rect(0,bar_y-2,WIDTH,HEIGHT-(bar_y-2),C_BLACK);pyxel.line(0,bar_y-2,WIDTH,bar_y-2,C_DKGRAY)
        pyxel.rect(4,bar_y,int((WIDTH-8)*ratio),5,tc_bar);pyxel.rectb(4,bar_y,WIDTH-8,5,C_DKGRAY)
        secs=self.timer//FPS;pyxel.text(4,bar_y+7,"Zeit: {}s".format(secs),tc_bar)
        pyxel.text(WIDTH-88,bar_y+7,"Pfeile/WASD: Laufen",C_DKGRAY)
        if self.done:
            ow2=140;oh2=50;ox2=(WIDTH-ow2)//2;oy2=(HEIGHT-oh2)//2
            pyxel.rect(ox2,oy2,ow2,oh2,C_BLACK)
            pyxel.rectb(ox2,oy2,ow2,oh2,C_GREEN if self.won else C_RED)
            if self.won:
                centered_text(oy2+8,"SERVIERT!",C_GREEN);centered_text(oy2+20,"Zeitbonus erhalten!",C_YELLOW)
            else:
                centered_text(oy2+8,"ZEIT ABGELAUFEN!",C_RED);centered_text(oy2+20,"Kein Zeitbonus.",C_LGRAY)
            centered_text(oy2+34,"[SPACE] Weiter",C_WHITE)

class SkyBakery:
    def __init__(self):
        pyxel.init(WIDTH,HEIGHT,title="Sky Bakery",fps=FPS)
        self._setup_sounds()
        self.bg_clouds=[BgCloud() for _ in range(8)]
        self.char_idx=0;self.char_tick=0
        self.reset_game()
        self.state="title";self.title_tick=0
        self.served_msg="";self.served_timer=0
        pyxel.run(self.update,self.draw)

    def _setup_sounds(self):
        pyxel.sound(0).set(notes="c3e3g3c4",tones="pppp",volumes="7654",effects="nnnn",speed=6)
        pyxel.sound(1).set(notes="g2a2g2f2",tones="nnnn",volumes="5544",effects="ffff",speed=7)
        pyxel.sound(2).set(notes="c2b1a1g1",tones="pppp",volumes="6543",effects="nnnn",speed=8)
        pyxel.sound(3).set(notes="a2a2a2a2a2a2a2a2",tones="nnnnnnnn",volumes="76543210",effects="ffffffff",speed=4)
        pyxel.sound(4).set(notes="c3e3g3c4e4g4c4r",tones="pppppppn",volumes="56677770",effects="nnnnnnnn",speed=7)
        pyxel.sound(5).set(notes="c3b2a2g2f2e2",tones="pppppp",volumes="765432",effects="nnnnnn",speed=9)
        pyxel.sound(6).set(notes="e3g3c4r",tones="pppn",volumes="7530",effects="nnnn",speed=7)
        pyxel.sound(7).set(notes="c3a2",tones="pp",volumes="64",effects="nn",speed=8)
        pyxel.sound(8).set(notes="c3b2a2g2e2c2",tones="pppppp",volumes="765432",effects="nnnnnn",speed=12)
        pyxel.sound(9).set(notes="c2a1",tones="nn",volumes="75",effects="ff",speed=6)

    def reset_game(self):
        self.score=0;self.lives=3
        self.basket_x=float(WIDTH//2-BASKET_W//2);self.basket_y=CLOUD_PLAT_Y-BASKET_H-2
        self.chef_tick=0;self.inventory=[];self.fallers=[];self.particles=[]
        self.spawn_timer=0;self.spawn_rate=FPS*2;self.orders=[];self.order_timer=FPS*3
        self.active_order=None;self.baker=None;self.packaging=None;self.maze=None
        self.flash_timer=0;self.flash_msg="";self.flash_col=C_GREEN
        self.difficulty=1.0;self.orders_done=0;self.game_tick=0
        self.served_msg="";self.served_timer=0

    def flash(self,msg,col=C_GREEN,dur=55):
        self.flash_msg=msg;self.flash_col=col;self.flash_timer=dur

    def show_served(self,msg):
        self.served_msg=msg;self.served_timer=FPS*4

    @property
    def char(self):return CHARS[self.char_idx]

    def update(self):
        for c in self.bg_clouds:c.update()
        self.char_tick+=1
        if self.served_timer>0:self.served_timer-=1
        if self.state=="title":
            self.title_tick+=1
            if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):self.state="charselect"
        elif self.state=="charselect":self._update_charselect()
        elif self.state=="catch":self._update_catch()
        elif self.state=="baking":self._update_baking()
        elif self.state=="packaging":self._update_packaging()
        elif self.state=="serving":self._update_serving()
        elif self.state=="gameover":
            if pyxel.btnp(pyxel.KEY_R):self.reset_game();self.state="charselect"

    def _update_charselect(self):
        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):self.char_idx=(self.char_idx-1)%len(CHARS)
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):self.char_idx=(self.char_idx+1)%len(CHARS)
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):self.state="catch"

    def _speed_mult(self):return 1.0+(self.difficulty-1.0)*0.45+self.game_tick/(FPS*100)
    def _bad_chance(self):return min(0.40,0.08+(self.difficulty-1.0)*0.08)

    def _update_catch(self):
        self.game_tick+=1;self.chef_tick+=1
        move=0
        if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.KEY_A):move-=1
        if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D):move+=1
        self.basket_x=max(0.0,min(WIDTH-BASKET_W,self.basket_x+move*PLAYER_SPEED))
        self.spawn_timer+=1
        if self.spawn_timer>=self.spawn_rate:
            self.spawn_timer=0;needed=[]
            for o in self.orders:needed.extend(o.needed)
            count=2 if self.difficulty>2.5 and 0.3>random.random() else 1
            for _ in range(count):
                self.fallers.append(FallingIngredient(boost_needed=needed or None,
                    bad_chance=self._bad_chance(),speed_mult=self._speed_mult()))
            self.spawn_rate=max(FPS//2,int(FPS*1.8/self.difficulty)-2)
        bcx=self.basket_x+BASKET_W//2;bcy=self.basket_y+BASKET_H//2
        for f in self.fallers:
            f.update()
            if f.alive and BASKET_W//2+6>abs(f.x-bcx) and BASKET_H//2+8>abs(f.y-bcy):
                f.alive=False
                if f.is_bad:
                    self.lives-=1;self.flash("FALSCH! -1 Leben!",C_RED);pyxel.play(3,9)
                    for _ in range(20):
                        self.particles.append(Particle(f.x,f.y,C_RED,
                            vx=random.uniform(-3,3),vy=random.uniform(-3,-0.5)))
                else:
                    self.inventory.append(f.name)
                    if len(self.inventory)>MAX_INV:self.inventory.pop(0)
                    col=INGR_COLS[INGREDIENTS.index(f.name)]
                    for _ in range(12):self.particles.append(Particle(f.x,f.y,col))
                    pyxel.play(3,0)
        self.fallers=[f for f in self.fallers if f.alive]
        for p in self.particles:p.update()
        self.particles=[p for p in self.particles if p.life>0]
        self.order_timer-=1
        if 0>=self.order_timer and MAX_ORDERS>len(self.orders):
            self.order_timer=FPS*10;existing={o.name for o in self.orders}
            pool=[k for k in RECIPES if k not in existing]
            if pool:self.orders.append(Order(random.choice(pool)))
        for o in self.orders:o.update()
        expired=[o for o in self.orders if o.expired]
        for _ in expired:
            self.lives-=1;self.flash("Bestellung abgelaufen!",C_RED);pyxel.play(3,5)
        self.orders=[o for o in self.orders if not o.expired]
        if self.flash_timer>0:self.flash_timer-=1
        if 0>=self.lives:pyxel.play(3,8);self.state="gameover";return
        if pyxel.btnp(pyxel.KEY_SPACE):self._try_start_baking()

    def _try_start_baking(self):
        inv_set=set(self.inventory)
        ready=sorted([o for o in self.orders if set(o.needed).issubset(inv_set)],key=lambda o:o.timer)
        if not ready:self.flash("Nicht genug Zutaten!",C_YELLOW);pyxel.play(3,7);return
        o=ready[0];tmp=list(self.inventory)
        for ing in o.needed:tmp.remove(ing)
        self.inventory=tmp;self.active_order=o;self.orders.remove(o)
        self.baker=BakingAnimator(o,self.char);self.state="baking"

    def _update_baking(self):
        self.baker.update()
        if self.baker.step==0:
            for i,key in enumerate(DROP_KEYS):
                if pyxel.btnp(key):self.baker.on_drop_key(i)
        if self.baker.step==2:
            for i,key in enumerate(MIX_KEYS):
                if pyxel.btnp(key):self.baker.on_mix_key(i)
        if pyxel.btnp(pyxel.KEY_SPACE):
            if self.baker.on_space():
                self.packaging=PackagingAnim(self.active_order.name,self.char);self.state="packaging"

    def _update_packaging(self):
        self.packaging.update()
        if self.packaging.tick>FPS*2 and pyxel.btnp(pyxel.KEY_SPACE):
            maze_time=max(FPS*14,FPS*24-int(self.difficulty*FPS*2))
            self.maze=ServingMaze(self.active_order.name,maze_time,self.char);self.state="serving"
        elif self.packaging.done:
            maze_time=max(FPS*14,FPS*24-int(self.difficulty*FPS*2))
            self.maze=ServingMaze(self.active_order.name,maze_time,self.char);self.state="serving"

    def _update_serving(self):
        self.maze.update()
        if self.maze.done:
            if pyxel.btnp(pyxel.KEY_SPACE):
                pts=RECIPE_BONUS.get(self.active_order.name,100)
                if self.maze.won:
                    time_bonus=int((self.maze.timer/self.maze.max_timer)*60);pts+=time_bonus;pyxel.play(3,4)
                else:pyxel.play(3,7)
                self.score+=pts;self.orders_done+=1
                if self.orders_done%3==0:self.difficulty+=0.3
                self.show_served("Kunde #{}: {} serviert! +{}Pkt".format(
                    self.orders_done,self.active_order.name,pts))
                self.active_order=None;self.baker=None;self.packaging=None;self.maze=None
                self.state="catch"

    def draw(self):
        if self.state=="baking":self.baker.draw();return
        if self.state=="packaging":self.packaging.draw();return
        if self.state=="serving":self.maze.draw();return
        for gy in range(HEIGHT):
            t=gy/HEIGHT;col=C_DARKBL if 0.28>t else C_BLUE;pyxel.line(0,gy,WIDTH,gy,col)
        for c in self.bg_clouds:c.draw()
        if self.state=="title":self._draw_title()
        elif self.state=="charselect":self._draw_charselect()
        elif self.state=="catch":self._draw_catch()
        elif self.state=="gameover":self._draw_gameover()

    def _draw_title(self):
        draw_platform_cloud(CLOUD_PLAT_Y)
        for i in range(0,WIDTH,32):
            pyxel.pset(i+int(math.sin(self.title_tick*0.04+i)*3),18,C_YELLOW)
        lbx=WIDTH//2-62
        pyxel.rect(lbx,10,124,26,C_BLACK);pyxel.rect(lbx+1,11,122,24,C_DARKBL)
        pyxel.rectb(lbx,10,124,26,C_YELLOW);pyxel.line(lbx+2,23,lbx+121,23,C_DKGRAY)
        pyxel.text(WIDTH//2-35+1,13+1,"SKY BAKERY",C_BROWN)
        pyxel.text(WIDTH//2-35,13,"SKY BAKERY",C_YELLOW)
        centered_text(24,"Backen in den Wolken!",C_ORANGE)
        if (self.title_tick//18)%2==0:centered_text(40,"[ SPACE ] START",C_GREEN)
        else:centered_text(40,"[ SPACE ] START",C_DKGRAY)
        draw_panel(2,52,90,68,C_DARKBL,C_LGRAY)
        pyxel.text(5,55,"STEUERUNG",C_YELLOW);pyxel.line(2,62,91,62,C_DKGRAY)
        pyxel.text(5,64,"A/D: Bewegen",C_WHITE);pyxel.text(5,72,"SPACE: Backen",C_WHITE)
        pyxel.text(5,80,"A-H: Einlegen",C_WHITE);pyxel.text(5,88,"Q-I: Mixen",C_WHITE)
        pyxel.text(5,96,"Pfeile: Labyrinth",C_WHITE);pyxel.text(5,110,"3 Leben",C_RED)
        draw_panel(96,52,68,68,C_DARKBL,C_LGRAY)
        pyxel.text(99,55,"GUT:",C_GREEN);pyxel.line(96,62,163,62,C_DKGRAY)
        for i in range(4):draw_ingredient_sprite(INGREDIENTS[i],106+i*16,72)
        for i in range(4):draw_ingredient_sprite(INGREDIENTS[i+4],106+i*16,88)
        pyxel.text(99,98,"SCHLECHT:",C_RED);pyxel.line(96,104,163,104,C_DKGRAY)
        for i in range(4):draw_ingredient_sprite(BAD_ITEMS[i],106+i*16,111)
        draw_panel(168,52,86,68,C_DARKBL,C_LGRAY)
        pyxel.text(171,55,"REZEPTE",C_YELLOW);pyxel.line(168,62,253,62,C_DKGRAY)
        for idx,(rname,ings) in enumerate(RECIPES.items()):
            ry=64+idx*8;short="+".join([ii[:3] for ii in ings])
            pyxel.text(171,ry,rname[:7]+":",C_ORANGE)
        char_positions=[WIDTH//4-10,WIDTH//2,WIDTH*3//4+10]
        for i,(chdef,cpx) in enumerate(zip(CHARS,char_positions)):
            draw_chef(cpx,CLOUD_PLAT_Y-18,self.title_tick+i*20,False,chdef)
            nc=chdef["name_col"];nm=chdef["name"]
            nx=cpx-len(nm)*2-1;ny=CLOUD_PLAT_Y+5
            pyxel.rect(nx,ny,len(nm)*4+2,7,C_DARKBL)
            pyxel.text(nx+1,ny+1,nm,nc)

    def _draw_charselect(self):
        draw_platform_cloud(CLOUD_PLAT_Y)
        pyxel.rect(WIDTH//2-72,6,144,18,C_BLACK);pyxel.rectb(WIDTH//2-72,6,144,18,C_YELLOW)
        centered_text(10,"CHARAKTER WAHLEN",C_YELLOW)
        if (self.char_tick//12)%2==0:
            pyxel.text(12,HEIGHT//2-10,"< A",C_WHITE);pyxel.text(WIDTH-24,HEIGHT//2-10,"D >",C_WHITE)
        card_w=60;card_h=110;spacing=14;total_w=3*card_w+2*spacing;start_x=(WIDTH-total_w)//2
        for i,chdef in enumerate(CHARS):
            cx=start_x+i*(card_w+spacing);cy=28;selected=(i==self.char_idx)
            pyxel.rect(cx,cy,card_w,card_h,C_DARKBL)
            if selected:
                pyxel.rectb(cx-1,cy-1,card_w+2,card_h+2,chdef["hat_col"])
                pyxel.rectb(cx,cy,card_w,card_h,C_WHITE);pyxel.rect(cx+1,cy+1,card_w-2,card_h-2,C_INDIGO)
            else:pyxel.rectb(cx,cy,card_w,card_h,C_DKGRAY)
            chef_x=cx+card_w//2;chef_y=cy+50
            draw_chef_big(chef_x,chef_y,self.char_tick if selected else 0,chdef)
            nc=chdef["name_col"];nm=chdef["name"]
            pyxel.text(cx+(card_w-len(nm)*4)//2,cy+78,nm,nc)
            desc_col=C_WHITE if selected else C_DKGRAY
            words=chdef["desc"].split();line1=" ".join(words[:3]);line2=" ".join(words[3:]) if len(words)>3 else ""
            pyxel.text(cx+2,cy+88,line1,desc_col)
            if line2:pyxel.text(cx+2,cy+96,line2,desc_col)
            if selected:centered_text_in(cx,cy+104,card_w,"GEWHLT",C_GREEN)
        if (self.char_tick//15)%2==0:centered_text(HEIGHT-18,"[ SPACE ] Bestatigen",C_GREEN)
        centered_text(HEIGHT-9,"A / D oder Pfeile: Wechseln",C_LGRAY)

    def _draw_catch(self):
        draw_platform_cloud(CLOUD_PLAT_Y)
        for p in self.particles:p.draw()
        for f in self.fallers:f.draw()
        chef_cx=int(self.basket_x)+BASKET_W//2;chef_cy=self.basket_y-14
        draw_chef(chef_cx,chef_cy,self.chef_tick,False,self.char)
        bx=int(self.basket_x);by=self.basket_y
        bc_col=self.char["basket_col"];bp_col=self.char["basket_pat"]
        pyxel.rect(bx,by,BASKET_W,BASKET_H,bc_col)
        for row in range(BASKET_H):
            for ci in range(1,BASKET_W-1):
                if (row+ci)%2==0:pyxel.pset(bx+ci,by+row,bp_col)
        for ry in range(0,BASKET_H,4):pyxel.line(bx+1,by+ry,bx+BASKET_W-2,by+ry,C_ORANGE)
        pyxel.rectb(bx,by,BASKET_W,BASKET_H,C_BLACK)
        pyxel.rect(0,0,WIDTH,14,C_BLACK);pyxel.line(0,14,WIDTH,14,C_DKGRAY)
        pyxel.text(4,3,"Score: {}".format(self.score),C_WHITE)
        pyxel.text(90,3,"Kunden: {}".format(self.orders_done),C_YELLOW)
        pyxel.text(168,3,self.char["name"],self.char["name_col"])
        for i in range(3):
            col=C_RED if self.lives>i else C_DKGRAY;hx=WIDTH-12-i*13
            pyxel.pset(hx,4,col);pyxel.pset(hx+1,3,col);pyxel.pset(hx+2,3,col)
            pyxel.pset(hx+3,3,col);pyxel.pset(hx+4,4,col);pyxel.pset(hx+1,5,col)
            pyxel.pset(hx+2,6,col);pyxel.pset(hx+3,5,col);pyxel.pset(hx+5,4,col)
        if self.served_timer>0:
            sw2=min(WIDTH-4,len(self.served_msg)*4+10);sx=(WIDTH-sw2)//2;sy=16
            pyxel.rect(sx,sy,sw2,13,C_DKGRN);pyxel.rectb(sx,sy,sw2,13,C_GREEN)
            pyxel.text(sx+3,sy+4,self.served_msg,C_WHITE)
        best_y=16 if 0>=self.served_timer else 30
        inv_set=set(self.inventory)
        for i,o in enumerate(self.orders):o.draw(2+i*86,best_y,set(o.needed).issubset(inv_set))
        inv_y=HEIGHT-30;pyxel.rect(0,inv_y-2,WIDTH,32,C_BLACK);pyxel.line(0,inv_y-2,WIDTH,inv_y-2,C_DKGRAY)
        pyxel.text(3,inv_y+1,"Korb:",C_LGRAY)
        for i,ing in enumerate(self.inventory[-8:]):draw_ingredient_sprite(ing,40+i*26,inv_y+12)
        pyxel.text(4,HEIGHT-6,"[SPACE]=Backen",C_LGRAY)
        if any(set(o.needed).issubset(inv_set) for o in self.orders):
            if (pyxel.frame_count//10)%2==0:centered_text(HEIGHT-44,">> BEREIT ZUM BACKEN! <<",C_GREEN)
        if self.flash_timer>0:
            fw=min(WIDTH-8,len(self.flash_msg)*4+10);fx=(WIDTH-fw)//2;fy=HEIGHT//2-8
            pyxel.rect(fx,fy,fw,14,C_BLACK);pyxel.rectb(fx,fy,fw,14,self.flash_col)
            pyxel.text(fx+4,fy+4,self.flash_msg,self.flash_col)

    def _draw_gameover(self):
        for gy in range(0,HEIGHT,2):pyxel.line(0,gy,WIDTH,gy,C_BLACK)
        draw_platform_cloud(CLOUD_PLAT_Y)
        draw_panel(WIDTH//2-68,28,136,108,C_BLACK,C_RED)
        centered_text(36,"GAME  OVER",C_RED);pyxel.line(WIDTH//2-62,48,WIDTH//2+62,48,C_DKGRAY)
        centered_text(54,"Score:  {}".format(self.score),C_YELLOW)
        centered_text(66,"Kunden: {}".format(self.orders_done),C_LGRAY)
        centered_text(78,"Level:  {}".format(int(self.difficulty)),C_LGRAY)
        pyxel.line(WIDTH//2-62,92,WIDTH//2+62,92,C_DKGRAY)
        centered_text(98,"[R]  Nochmal spielen",C_WHITE)
        draw_chef(WIDTH//2,122,0,False,self.char)

SkyBakery()