#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Ultimate Minecraft-Inspired Survival Game (Fixed)
-------------------------------------------------
All must-have crafting & smelting features are included, and the Play-button
crash is fixed by using a single Tk() instance. The menu frame is destroyed
when “Start” is clicked, and the Game UI is built on the same window.
Run with Python 3.6+ (Tkinter required).
"""

import tkinter as tk
from tkinter import ttk
import math, random, time, pickle

# ----------------------------
# GLOBAL SETTINGS
# ----------------------------
OVERWORLD_WIDTH, OVERWORLD_HEIGHT = 500, 100
NETHER_WIDTH, NETHER_HEIGHT   = 120,  60
END_WIDTH, END_HEIGHT         = 150,  80

VIEWPORT_WIDTH, VIEWPORT_HEIGHT = 80, 20
CELL_SIZE                       = 8

DAY_LENGTH    = 120    # seconds per day
SEASON_LENGTH = 300    # seconds per season
START_TIME    = time.time()
STACK_LIMIT   = 64

# ----------------------------
# BLOCKS, ITEMS & COLOURS
# ----------------------------
BLOCKS = {
    "AIR":             "#FFFFFF",
    "GRASS":           "#00AA00",
    "DIRT":            "#8B4513",
    "STONE":           "#808080",
    "COAL_ORE":        "#333333",
    "IRON_ORE":        "#CC6600",
    "DIAMOND_ORE":     "#55FFFF",
    "WOOD":            "#A0522D",
    "LEAF":            "#66CC66",
    "OBSIDIAN":        "#440044",
    "FURNACE":         "#CC6600",
    "CRAFTING_TABLE":  "#D2691E",
    "CHEST":           "#DEB887",
    "TORCH":           "#FFFF00",
    "wood_plank":      "#DEB887",
    "wooden_pickaxe":  "#CD853F",
    "diamond_pickaxe": "#00FFFF",
    "apple":           "#FF0000",
    "NETHERRACK":      "#A52A2A",
    "SOUL_SAND":       "#806040",
    "GLOWSTONE":       "#FFFFE0",
    "END_STONE":       "#D2B48C",
    "PORTAL":          "#0000FF",
    "END_PORTAL_FRAME":"#550088",
    "SAND":            "#EDC9AF",
    "SANDSTONE":       "#F5DEB3",
    "GRAVEL":          "#999999",
    "WATER":           "#0000FF",
    "LAVA":            "#FF4500",
    "BUCKET":          "#AAAAAA",
    "WATER_BUCKET":    "#6666FF",
    "LAVA_BUCKET":     "#FF8C00",
    "FLINT":           "#BFBFBF",
    "FLINT_AND_STEEL": "#AAAAAA",
    "BLAZE_ROD":       "#FFD27F",
    "BLAZE_POWDER":    "#FFDC73",
    "EYE_OF_ENDER":    "#FFD700",
    "NETHER_STAR":     "#FFFF33",
    "GLASS":           "#BEC8C8",
    "RAW_MEAT":        "#8B0000",
    "COOKED_MEAT":     "#FFA07A",
    "REDSTONE_ORE":    "#FF0000",
    "LADDER":          "#8B4513",
}

PLACEABLE = set([
    "GRASS","DIRT","STONE","COAL_ORE","IRON_ORE","DIAMOND_ORE",
    "WOOD","LEAF","OBSIDIAN","FURNACE","CRAFTING_TABLE","CHEST","TORCH",
    "wood_plank","SAND","SANDSTONE","GRAVEL","WATER","LAVA",
    "SOUL_SAND","GLOWSTONE","END_PORTAL_FRAME","GLASS","LADDER"
])

# ----------------------------
# CRAFTING & SMELTING RECIPES
# ----------------------------
crafting_recipes = {
    "wood_plank":      {"WOOD":1},
    "stick":           {"wood_plank":2},
    "wooden_pickaxe":  {"wood_plank":3,"stick":2},
    "stone_pickaxe":   {"COBBLESTONE":3,"stick":2},
    "iron_pickaxe":    {"IRON_INGOT":3,"stick":2},
    "diamond_pickaxe": {"DIAMOND":3,"stick":2},
    "bucket":          {"IRON_INGOT":3},
    "flint_and_steel": {"IRON_INGOT":1,"FLINT":1},
    "torch":           {"stick":1,"COAL_ORE":1},
    "furnace":         {"COBBLESTONE":8},
    "crafting_table":  {"wood_plank":4},
    "chest":           {"wood_plank":8},
    "ladder":          {"stick":7},
    "BLAZE_POWDER":    {"BLAZE_ROD":1},
    "EYE_OF_ENDER":    {"BLAZE_POWDER":1,"ENDER_PEARL":1},
    "END_PORTAL_FRAME":{"EYE_OF_ENDER":1},
    "NETHER_STAR":     {"END_PORTAL_FRAME":1},
    "GLASS":           {"SAND":4},
}

smelting_recipes = {
    "IRON_ORE":    "IRON_INGOT",
    "GOLD_ORE":    "GOLD_INGOT",
    "COBBLESTONE": "STONE",
    "SAND":        "GLASS",
    "RAW_MEAT":    "COOKED_MEAT",
}

# ----------------------------
# BIOME & WEATHER
# ----------------------------
BIOMES = ["plains","desert","mountains","forest","snowy_tundra","swamp"]

def choose_biome(x,y):
    r=random.random()
    if r<0.15: return "desert"
    if r<0.30: return "mountains"
    if r<0.55: return "plains"
    if r<0.75: return "forest"
    if r<0.90: return "swamp"
    return "snowy_tundra"

def choose_weather(biome,season):
    if season=="Winter" and biome in ["snowy_tundra","mountains","forest"]:
        return random.choices(["clear","snow"],weights=[0.4,0.6])[0]
    if biome=="desert":
        return random.choices(["clear","rain"],weights=[0.8,0.2])[0]
    return random.choices(["clear","rain","thunderstorm"],weights=[0.6,0.3,0.1])[0]

# ----------------------------
# DIMENSIONS
# ----------------------------
DIMENSIONS = ["overworld","nether","end"]
def get_dimension_sizes(dim):
    return {
        "overworld":(OVERWORLD_WIDTH,OVERWORLD_HEIGHT),
        "nether":   (NETHER_WIDTH,NETHER_HEIGHT),
        "end":      (END_WIDTH,END_HEIGHT),
    }[dim]

# ----------------------------
# PLAYER & MOB
# ----------------------------
class Player:
    def __init__(self,x,y):
        self.x,self.y=x,y
        self.health=100; self.hunger=100
        self.dimension="overworld"
        self.inventory={k:0 for k in BLOCKS if k!="AIR"}
        for itm in ["stick","COBBLESTONE","IRON_INGOT","GOLD_INGOT",
                    "GLASS","RAW_MEAT","COOKED_MEAT","BLAZE_ROD","BLAZE_POWDER",
                    "ENDER_PEARL","NETHER_STAR","bucket","water_bucket","lava_bucket"]:
            self.inventory.setdefault(itm,0)
        self.active_item=None
        self.orientation=0; self.vy=0
        self.tool_durability={"wooden_pickaxe":60,"diamond_pickaxe":1561}

class Mob:
    def __init__(self,x,y,kind="zombie"):
        self.x,self.y=x,y; self.kind=kind
        mapping={
            "zombie":(20,"Z"),"skeleton":(20,"S"),"creeper":(20,"C"),
            "cow":(15,"O"),"pig":(15,"P"),"chicken":(10,"H"),
            "ender_dragon":(200,"D"),"wither":(300,"W")
        }
        self.health,self.symbol=mapping.get(kind,(10,"?"))

# ----------------------------
# MAIN GAME ENGINE
# ----------------------------
class Game:
    def __init__(self,root):
        self.root=root
        root.title("Ultimate Minecraft Survival")
        # Canvas
        self.canvas=tk.Canvas(root,
            width=VIEWPORT_WIDTH*CELL_SIZE,
            height=VIEWPORT_HEIGHT*CELL_SIZE,
            bg="black")
        self.canvas.pack()
        # Hotbar & status
        self.hotbar_frame=tk.Frame(root); self.hotbar_frame.pack(fill=tk.X)
        self.status_var=tk.StringVar()
        ttk.Label(root,textvariable=self.status_var,font=("Arial",12)).pack(fill=tk.X)

        # Bind inputs
        self.canvas.bind("<KeyPress>",self.on_key)
        self.canvas.bind("<Button-1>",self.on_click)
        self.canvas.bind("<Motion>",self.on_mouse_move)
        self.canvas.bind("<MouseWheel>",self.on_scroll)
        self.canvas.bind("<Button-4>",self.on_scroll)
        self.canvas.bind("<Button-5>",self.on_scroll)
        for i in range(1,10):
            root.bind(str(i),self.on_number)
        root.bind("o",lambda e:self.auto_sort())
        root.bind("p",lambda e:self.save_game())
        root.bind("l",lambda e:self.load_game())
        root.bind("e",lambda e:self.use_eye())
        root.bind("d",lambda e:self.use_bucket())

        # World & player
        self.current_dimension="overworld"
        self.world=self.generate_world("overworld")
        self.ww,self.wh=get_dimension_sizes("overworld")
        self.biome_map=[choose_biome(x,0) for x in range(self.ww)]
        self.weather={b:choose_weather(b,"Spring") for b in set(self.biome_map)}
        self.season="Spring"
        # spawn at surface
        sy=39
        sx=next((x for x in range(self.ww)
                 if self.world[40][x] in ["GRASS","SAND"]),self.ww//2)
        self.player=Player(sx,sy)
        self.mobs=[]
        self.last=time.time()
        self.portal_cd=0

        self.canvas.focus_set()
        self.update_hotbar()
        self.update_game()

    # -------------
    # WORLD GENERATION
    # -------------
    def generate_world(self,dim):
        w,h=get_dimension_sizes(dim)
        world=[]
        if dim=="overworld":
            for y in range(h):
                row=[]
                for x in range(w):
                    if y<40: row.append("AIR")
                    elif y==40:
                        bio=self.biome_map[x]
                        if bio=="desert":
                            row.append("SANDSTONE" if random.random()<0.3 else "SAND")
                        elif bio=="snowy_tundra":
                            row.append("SNOW")
                        else:
                            row.append("GRASS")
                    elif y<45:
                        row.append("DIRT")
                    else:
                        r=random.random()
                        if   r<0.05: row.append("COAL_ORE")
                        elif r<0.08: row.append("IRON_ORE")
                        elif r<0.09: row.append("DIAMOND_ORE")
                        elif r<0.16: row.append("GRAVEL")
                        else:        row.append("STONE")
                world.append(row)
            # Trees
            for _ in range(50):
                x=random.randint(0,w-1); bio=self.biome_map[x]
                if bio in ["plains","forest","swamp"]:
                    y=40
                    if world[y][x] in ["GRASS","SAND"]:
                        ht=random.randint(3,5)
                        for i in range(1,ht+1): world[y-i][x]="WOOD"
                        for dx in (-1,0,1):
                            for dy in (-2,-1):
                                nx,ny=x+dx,y-ht+dy
                                if 0<=nx<w and 0<=ny<h: world[ny][nx]="LEAF"
        elif dim=="nether":
            for y in range(h):
                row=[]
                for x in range(w):
                    r=random.random()
                    if   r<0.05: row.append("GLOWSTONE")
                    elif r<0.15: row.append("SOUL_SAND")
                    elif r<0.18: row.append("OBSIDIAN")
                    else:        row.append("NETHERRACK")
                world.append(row)
            for _ in range(10):
                x=random.randint(0,w-1); y=random.randint(0,h-1)
                world[y][x]="PORTAL"
        else:  # end
            for y in range(h):
                row=[]
                for x in range(w):
                    row.append("END_STONE" if y>h-8 else "AIR")
                world.append(row)
            world[h-9][w//2]="PORTAL"
        return world

    # -------------
    # WEATHER & SEASONS
    # -------------
    def update_weather_and_season(self):
        elapsed=time.time()-START_TIME
        self.season=["Spring","Summer","Fall","Winter"][int(elapsed//SEASON_LENGTH)%4]
        for b in set(self.biome_map):
            self.weather[b]=choose_weather(b,self.season)

    # -------------
    # SAVE/LOAD
    # -------------
    def save_game(self,event=None):
        data={
            "player":self.player,"world":self.world,
            "dimension":self.current_dimension,"inventory":self.player.inventory,
            "mobs":self.mobs,"biome_map":self.biome_map,
            "weather":self.weather,"season":self.season
        }
        with open("savegame.dat","wb") as f: pickle.dump(data,f)
        self.status_var.set("Game Saved!")

    def load_game(self,event=None):
        try:
            with open("savegame.dat","rb") as f: data=pickle.load(f)
            self.player       = data["player"]
            self.world        = data["world"]
            self.current_dimension = data["dimension"]
            self.player.inventory  = data["inventory"]
            self.mobs         = data["mobs"]
            self.biome_map    = data["biome_map"]
            self.weather      = data["weather"]
            self.season       = data["season"]
            self.ww,self.wh   = get_dimension_sizes(self.current_dimension)
            self.update_hotbar(); self.draw_world(); self.update_status()
            self.status_var.set("Game Loaded!")
        except Exception as e:
            print("Load failed:",e)
            self.status_var.set("Load Failed!")

    # -------------
    # VIEWPORT
    # -------------
    def get_camera_offset(self):
        cx=self.player.x-VIEWPORT_WIDTH//2
        cy=self.player.y-VIEWPORT_HEIGHT//2
        cx=max(0,min(cx,self.ww-VIEWPORT_WIDTH))
        cy=max(0,min(cy,self.wh-VIEWPORT_HEIGHT))
        return cx,cy

    # -------------
    # DRAWING
    # -------------
    def draw_world(self):
        self.canvas.delete("all")
        cx,cy=self.get_camera_offset()
        # brightness
        t=(time.time()-START_TIME)%DAY_LENGTH
        b=1.0 if t<=DAY_LENGTH/2 else 0.5
        wb=self.weather[self.biome_map[self.player.x]]
        eff={"clear":1.0,"rain":0.8,"thunderstorm":0.6,"snow":0.9}[wb]
        b*=eff
        for y in range(VIEWPORT_HEIGHT):
            for x in range(VIEWPORT_WIDTH):
                wx,wy=cx+x,cy+y
                blk=self.world[wy][wx]
                col=BLOCKS.get(blk,"#000000")
                if b<1.0:
                    r=int(int(col[1:3],16)*b)
                    g=int(int(col[3:5],16)*b)
                    bl=int(int(col[5:],16)*b)
                    col=f"#{r:02x}{g:02x}{bl:02x}"
                self.canvas.create_rectangle(
                    x*CELL_SIZE,y*CELL_SIZE,
                    (x+1)*CELL_SIZE,(y+1)*CELL_SIZE,
                    fill=col,outline="gray")
        # player
        px,py=self.player.x-cx,self.player.y-cy
        pad=2
        self.canvas.create_oval(
            px*CELL_SIZE+pad,py*CELL_SIZE+pad,
            (px+1)*CELL_SIZE-pad,(py+1)*CELL_SIZE-pad,
            fill="red",outline="white")
        # mobs
        for m in self.mobs:
            if cx<=m.x<cx+VIEWPORT_WIDTH and cy<=m.y<cy+VIEWPORT_HEIGHT:
                vx,vy=m.x-cx,m.y-cy
                self.canvas.create_oval(
                    vx*CELL_SIZE+pad,vy*CELL_SIZE+pad,
                    (vx+1)*CELL_SIZE-pad,(vy+1)*CELL_SIZE-pad,
                    fill="black",outline="white")
        self.canvas.update()

    # -------------
    # HOTBAR & STATUS
    # -------------
    def update_hotbar(self):
        for w in self.hotbar_frame.winfo_children(): w.destroy()
        frm=tk.Frame(self.hotbar_frame); frm.pack()
        items=[i for i in PLACEABLE if self.player.inventory.get(i,0)>0]
        items.sort()
        for idx in range(9):
            if idx<len(items):
                itm=items[idx]; cnt=self.player.inventory[itm]
                dur=""
                if itm in self.player.tool_durability:
                    dur=f" D:{self.player.tool_durability[itm]}"
                txt=f"{idx+1}:{itm}({cnt}){dur}"
                btn=tk.Button(frm,text=txt,width=12,
                              command=lambda i=itm:self.set_active_item(i))
                btn.bind("<Enter>",lambda e,i=itm:self.status_var.set(f"Hover: {i}"))
                btn.bind("<Leave>",lambda e:self.update_status())
                if self.player.active_item==itm:
                    btn.config(relief="sunken",bg="yellow")
            else:
                btn=tk.Button(frm,text=f"{idx+1}:----",width=12,state="disabled")
            btn.grid(row=0,column=idx,padx=2,pady=2)

    def set_active_item(self,itm):
        self.player.active_item=itm
        self.update_hotbar(); self.update_status()

    def update_status(self):
        phase="Day" if (time.time()-START_TIME)%DAY_LENGTH<=DAY_LENGTH/2 else "Night"
        act=self.player.active_item or "None"
        self.status_var.set(
            f"HP:{int(self.player.health)} Hunger:{int(self.player.hunger)} "
            f"Active:{act} Dim:{self.player.dimension} {phase} "
            f"Season:{self.season} Weather:{self.weather[self.biome_map[self.player.x]]}"
        )
        self.update_hotbar()

    # -------------
    # INPUT HANDLERS
    # -------------
    def on_key(self,e):
        k=e.keysym.lower()
        if k in ["w","a","s","d"]: self.move_player(k)
        elif k=="space": self.jump()
        elif k=="i": self.open_inventory()
        elif k=="c": self.open_crafting()
        elif k=="f": self.open_furnace()
        elif k=="h": self.open_help()
        elif k=="t": self.travel_dimension()
        elif k=="o": self.auto_sort()
        self.draw_world(); self.update_status()

    def on_click(self,e):
        cx,cy=self.get_camera_offset()
        gx,gy=e.x//CELL_SIZE,e.y//CELL_SIZE
        wx,wy=cx+gx,cy+gy
        if not(0<=wx<self.ww and 0<=wy<self.wh): return
        if abs(wx-self.player.x)<=1 and abs(wy-self.player.y)<=1:
            blk=self.world[wy][wx]
            if blk=="GRAVEL":
                drop=("FLINT" if random.random()<0.2 else "GRAVEL")
                self.player.inventory[drop]=min(self.player.inventory.get(drop,0)+1,STACK_LIMIT)
                self.world[wy][wx]="AIR"
            elif blk=="OBSIDIAN":
                if self.player.active_item!="diamond_pickaxe": return
                self.player.tool_durability["diamond_pickaxe"]-=1
                if self.player.tool_durability["diamond_pickaxe"]<=0:
                    self.player.inventory["diamond_pickaxe"]=0; self.player.active_item=None
                self.player.inventory["OBSIDIAN"]=self.player.inventory.get("OBSIDIAN",0)+1
                self.world[wy][wx]="AIR"
            elif blk!="AIR":
                self.player.inventory[blk]=min(self.player.inventory.get(blk,0)+1,STACK_LIMIT)
                self.world[wy][wx]="AIR"
            else:
                itm=self.player.active_item
                if itm and itm in PLACEABLE:
                    self.world[wy][wx]=itm
                    self.player.inventory[itm]-=1
                    if self.player.inventory[itm]==0: self.player.active_item=None
        self.draw_world(); self.update_status()

    def on_mouse_move(self,e):
        cx,cy=self.get_camera_offset()
        wx,wy=cx+e.x//CELL_SIZE,cy+e.y//CELL_SIZE
        dx,dy=wx-self.player.x,self.player.y-wy
        if dx!=0 or dy!=0:
            self.player.orientation=math.degrees(math.atan2(dy,dx))
        self.update_status()

    def on_scroll(self,e):
        items=[i for i in PLACEABLE if self.player.inventory.get(i,0)>0]
        items.sort()
        if not items: return
        try: idx=items.index(self.player.active_item)
        except: idx=-1
        if e.num==4 or getattr(e,"delta",0)>0: idx=(idx+1)%len(items)
        else: idx=(idx-1)%len(items)
        self.player.active_item=items[idx]
        self.update_status()

    def on_number(self,e):
        idx=int(e.char)-1
        items=[i for i in PLACEABLE if self.player.inventory.get(i,0)>0]
        items.sort()
        if 0<=idx<len(items): self.player.active_item=items[idx]
        self.update_status()

    # -------------
    # MOVEMENT & PHYSICS
    # -------------
    def move_player(self,dir):
        dx,dy=0,0
        if dir=="w": dy=-1
        if dir=="s": dy=1
        if dir=="a": dx=-1
        if dir=="d": dx=1
        nx,ny=self.player.x+dx,self.player.y+dy
        if 0<=nx<self.ww and 0<=ny<self.wh and self.world[ny][nx]=="AIR":
            self.player.x,self.player.y=nx,ny

    def jump(self):
        if self.world[self.player.y+1][self.player.x]!="AIR":
            self.player.vy=-3

    def apply_gravity(self,dt):
        self.player.vy+=0.5*dt
        newy=self.player.y+int(self.player.vy)
        if newy>=self.wh-1:
            newy=self.wh-1; self.player.vy=0
        if self.world[newy][self.player.x]=="AIR":
            self.player.y=newy
        else:
            self.player.vy=0

    # -------------
    # INVENTORY UI
    # -------------
    def open_inventory(self):
        win=tk.Toplevel(self.root); win.title("Inventory"); win.geometry("400x500")
        ttk.Label(win,text="Click to pick & swap. Press O to sort.",font=("Arial",12)).pack(pady=10)
        frm=tk.Frame(win); frm.pack(fill=tk.BOTH,expand=True)
        self.inv_buttons={}
        items=list(self.player.inventory.items())
        for i,(itm,cnt) in enumerate(items):
            b=tk.Button(frm,text=f"{itm}\n({cnt})",width=12,height=3)
            b.grid(row=i//4,column=i%4,padx=5,pady=5)
            b.bind("<Button-1>",lambda e,itm=itm:self.inv_click(itm,win))
            self.inv_buttons[itm]=b
        ttk.Button(win,text="Done",command=win.destroy).pack(pady=10)

    def inv_click(self,item,win):
        if not hasattr(self,"dragged"):
            self.dragged=item; self.status_var.set(f"Picked up {item}")
        else:
            a,b=self.dragged,item
            self.player.inventory[a],self.player.inventory[b]=\
                self.player.inventory[b],self.player.inventory[a]
            self.player.active_item=a
            del self.dragged
            self.status_var.set(f"Swapped {a} & {b}")
        self.update_hotbar(); self.update_status()

    def auto_sort(self):
        self.player.inventory=dict(sorted(self.player.inventory.items()))
        self.status_var.set("Inventory sorted")
        self.update_hotbar(); self.update_status()

    # -------------
    # CRAFTING UI
    # -------------
    def open_crafting(self):
        win=tk.Toplevel(self.root); win.title("Crafting"); win.geometry("400x500")
        ttk.Label(win,text="Crafting",font=("Arial",12)).pack(pady=10)
        frm=tk.Frame(win); frm.pack(fill=tk.BOTH,expand=True)
        for itm,req in crafting_recipes.items():
            if all(self.player.inventory.get(k,0)>=v for k,v in req.items()):
                txt=f"{itm}: " + ", ".join(f"{k}:{v}" for k,v in req.items())
                b=ttk.Button(frm,text=txt,command=lambda i=itm:self.craft(i,win))
                b.pack(fill=tk.X,padx=5,pady=5)
        ttk.Button(frm,text="Close",command=win.destroy).pack(pady=10)

    def craft(self,item,win):
        req=crafting_recipes[item]
        if all(self.player.inventory.get(k,0)>=v for k,v in req.items()):
            for k,v in req.items(): self.player.inventory[k]-=v
            self.player.inventory[item]=min(self.player.inventory.get(item,0)+1,STACK_LIMIT)
        self.update_status()
        win.destroy()

    # -------------
    # FURNACE UI
    # -------------
    def open_furnace(self):
        win=tk.Toplevel(self.root); win.title("Furnace"); win.geometry("400x300")
        ttk.Label(win,text="Furnace",font=("Arial",12)).pack(pady=10)
        ttk.Label(win,text="Input:").pack(); inp=tk.Entry(win); inp.pack()
        ttk.Label(win,text="Fuel:").pack(); fuel=tk.Entry(win); fuel.pack()
        out_lbl=ttk.Label(win,text=""); out_lbl.pack(pady=5)
        def smelt():
            i=inp.get().upper(); f=fuel.get().upper()
            if i in smelting_recipes and self.player.inventory.get(f,0)>0:
                o=smelting_recipes[i]
                self.player.inventory[o]+=1
                self.player.inventory[i]-=1
                self.player.inventory[f]-=1
                out_lbl.config(text=f"Smelted +1 {o}")
            else:
                out_lbl.config(text="Failed")
            self.update_status()
        ttk.Button(win,text="Smelt",command=smelt).pack(pady=10)

    # -------------
    # BUCKET LOGIC
    # -------------
    def use_bucket(self):
        itm=self.player.active_item
        if itm=="bucket":
            self.player.inventory["bucket"]-=1
            self.player.inventory["water_bucket"]+=1
            self.player.active_item="water_bucket"
        elif itm=="water_bucket":
            for dx,dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                x,y=self.player.x+dx,self.player.y+dy
                if 0<=x<self.ww and 0<=y<self.wh and self.world[y][x]=="LAVA":
                    self.world[y][x]="OBSIDIAN"
            self.player.active_item="bucket"
        elif itm=="lava_bucket":
            for dx,dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                x,y=self.player.x+dx,self.player.y+dy
                if 0<=x<self.ww and 0<=y<self.wh and self.world[y][x]=="AIR":
                    self.world[y][x]="LAVA"
            self.player.inventory["lava_bucket"]-=1
            self.player.inventory["bucket"]+=1
            self.player.active_item="bucket"
        self.update_status()

    # -------------
    # PORTALS & EYE OF ENDER
    # -------------
    def travel_dimension(self):
        if time.time()<self.portal_cd: return
        idx=DIMENSIONS.index(self.player.dimension)
        newd=DIMENSIONS[(idx+1)%3]
        self.player.dimension=newd
        self.world=self.generate_world(newd)
        self.ww,self.wh=get_dimension_sizes(newd)
        if newd=="overworld":
            sx=next((x for x in range(self.ww)
                     if self.world[40][x] in ["GRASS","SAND"]),self.ww//2)
            sy=39
        else:
            sx,sy=self.ww//2,self.wh//2
            self.mobs=[]
            if newd=="nether": self.mobs.append(Mob(sx,sy,"wither"))
            if newd=="end":    self.mobs.append(Mob(sx,sy,"ender_dragon"))
        self.player.x,self.player.y=sx,sy
        self.portal_cd=time.time()+3
        self.update_status(); self.draw_world()

    def use_eye(self):
        self.status_var.set("Eye of Ender flies... (stub)")

    # -------------
    # GAME LOOP
    # -------------
    def update_game(self):
        now=time.time(); dt=now-self.last; self.last=now
        self.apply_gravity(dt); self.update_weather_and_season()
        # hunger/health
        self.player.hunger=max(0,self.player.hunger-0.05*dt)
        if self.player.hunger>80 and self.player.health<100:
            self.player.health=min(100,self.player.health+0.02*dt)
        if self.player.hunger<20: self.player.health-=0.05*dt
        # spawn mobs
        if self.player.dimension=="overworld" and random.random()<0.01 and len(self.mobs)<10:
            ex,ey=random.randint(0,self.ww-1),random.randint(40,self.wh-1)
            if abs(ex-self.player.x)>5 and abs(ey-self.player.y)>5:
                bio=self.biome_map[ex]
                mtyp=("skeleton" if bio=="desert"
                     else random.choice(["zombie","cow","pig","chicken"])
                     if bio in ["plains","forest"] else "zombie")
                self.mobs.append(Mob(ex,ey,mtyp))
        # mob AI
        for m in self.mobs:
            if m.x<self.player.x and self.world[m.y][m.x+1]=="AIR": m.x+=1
            elif m.x>self.player.x and self.world[m.y][m.x-1]=="AIR": m.x-=1
            if m.y<self.player.y and self.world[m.y+1][m.x]=="AIR": m.y+=1
            elif m.y>self.player.y and self.world[m.y-1][m.x]=="AIR": m.y-=1
            if abs(m.x-self.player.x)<=1 and abs(m.y-self.player.y)<=1:
                self.player.health-=2*dt
        if self.player.health<=0:
            return self.game_over()
        self.draw_world(); self.update_status()
        self.root.after(100, self.update_game)

    def game_over(self):
        self.canvas.delete("all")
        self.canvas.create_text(
            VIEWPORT_WIDTH*CELL_SIZE//2, VIEWPORT_HEIGHT*CELL_SIZE//2,
            text="GAME OVER", fill="red", font=("Arial",32))
        self.status_var.set("Game Over")

    # -------------
    # HELP WINDOW
    # -------------
    def open_help(self):
        w=tk.Toplevel(self.root); w.title("Help"); w.geometry("400x400")
        txt=(
            "Controls:\n"
            " WASD: Move  Space: Jump\n"
            " Click: Mine/Place  1-9/Wheel: Hotbar\n"
            " I: Inventory  O: Sort  C: Crafting  F: Furnace\n"
            " T: Portal  E: Eye of Ender  D: Bucket\n"
            " P: Save  L: Load\n\n"
            "Features:\n"
            "- Gravel→Flint, Flint&Steel\n"
            "- Buckets (water/lava)\n"
            "- Furnace smelting (Iron, Gold, Meat, Sand→Glass)\n"
            "- Crafting Table, Chest, Ladder\n"
            "- Blaze/Eye of Ender, End Portal Frame, Nether Star stub\n"
            "- Nether & End portals with boss stubs\n"
            "- Mobs, weather, seasons, save/load"
        )
        ttk.Label(w,text=txt,wraplength=380,justify="left").pack(padx=10,pady=10)
        ttk.Button(w,text="Close",command=w.destroy).pack(pady=10)

# ----------------------------
# STARTUP MENU & LAUNCHER
# ----------------------------
def startup_menu():
    root=tk.Tk()
    root.title("Ultimate Minecraft Survival")
    menu_frame=tk.Frame(root)
    menu_frame.pack(fill=tk.BOTH,expand=True)

    ttk.Label(menu_frame,
        text="Ultimate Minecraft Survival",
        font=("Arial",18)).pack(pady=20)

    def start():
        menu_frame.destroy()
        Game(root)

    ttk.Button(menu_frame,text="Start",command=start).pack(pady=5)
    ttk.Button(menu_frame,text="Help",
        command=lambda: Game(root).open_help()).pack(pady=5)
    ttk.Button(menu_frame,text="Quit",command=root.destroy).pack(pady=5)

    root.mainloop()

if __name__=="__main__":
    startup_menu()