import pygame
import sys
import random
import math

# ========== 初始化 ==========
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("⛏️ Gold Miner – Deluxe Edition")
clock = pygame.time.Clock()

# ========== 华丽色彩 ==========
SKY_TOP = (70, 130, 200)
SKY_BOTTOM = (160, 210, 255)
SUN_COLOR = (255, 240, 180)
CLOUD = (255, 255, 255)
GRASS_GREEN = (90, 180, 70)
GRASS_DARK = (60, 140, 40)
DIRT_TOP = (210, 180, 140)
DIRT_MID = (170, 140, 100)
DIRT_DEEP = (110, 80, 50)
GOLD_SMALL = (255, 215, 0)
GOLD_BIG = (255, 180, 0)
DIAMOND_COLOR = (180, 240, 255)
STONE_COLOR = (160, 160, 170)
ROPE_COLOR = (220, 200, 160)
HOOK_METAL = (140, 140, 150)
WOOD_DARK = (100, 70, 40)
WHITE = (255, 255, 255)
BLACK = (20, 20, 20)
RED = (220, 60, 60)
GREEN = (80, 210, 80)
GOLD = (255, 215, 0)
CYAN = (0, 255, 255)
PURPLE = (180, 100, 255)
UI_BG = (30, 25, 20)
UI_PANEL = (50, 40, 35)

# ========== 字体 ==========
font_small = pygame.font.Font(None, 26)
font_med = pygame.font.Font(None, 34)
font_large = pygame.font.Font(None, 50)
font_title = pygame.font.Font(None, 64)

# ========== 常量 ==========
GROUND_Y = 160
HOOK_START_X = 400
HOOK_START_Y = GROUND_Y + 35
MAX_ROPE_LENGTH = 420
SWING_SPEED = 2.8
HOOK_SPEED_DOWN = 6
BASE_RETRACT_SPEED = 3

# ========== 粒子特效 ==========
class Sparkle:
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.vx = random.uniform(-3, 3)
        self.vy = random.uniform(-8, -2)
        self.color = color
        self.life = 30
        self.max_life = 30

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

    def draw(self, surface):
        if self.life <= 0: return
        alpha = int(255 * self.life / self.max_life)
        r = 4 * self.life / self.max_life
        pygame.draw.circle(surface, (*self.color, alpha), (int(self.x), int(self.y)), r)

# ========== 矿物类 ==========
class Mineral:
    def __init__(self, x, y, type_):
        self.x = x
        self.y = y
        self.type = type_
        self.caught = False
        if type_ == "small_gold":
            self.width, self.height = 36, 30
            self.value = random.randint(50, 150)
            self.weight = 1.0
            self.color = GOLD_SMALL
        elif type_ == "big_gold":
            self.width, self.height = 56, 46
            self.value = random.randint(200, 400)
            self.weight = 2.5
            self.color = GOLD_BIG
        elif type_ == "diamond":
            self.width, self.height = 28, 28
            self.value = random.randint(500, 800)
            self.weight = 0.8
            self.color = DIAMOND_COLOR
        elif type_ == "stone":
            self.width, self.height = 40, 34
            self.value = 0
            self.weight = 3.0
            self.color = STONE_COLOR

    def get_rect(self):
        return pygame.Rect(self.x - self.width//2, self.y - self.height//2, self.width, self.height)

    def draw(self, surface):
        rect = self.get_rect()
        if self.type == "small_gold":
            pygame.draw.ellipse(surface, GOLD_SMALL, rect)
            pygame.draw.ellipse(surface, BLACK, rect, 2)
            shine = pygame.Surface((self.width//2, self.height//2), pygame.SRCALPHA)
            shine.fill((255,255,200,120))
            surface.blit(shine, (rect.x+self.width//4, rect.y+self.height//4))
        elif self.type == "big_gold":
            pygame.draw.ellipse(surface, GOLD_BIG, rect)
            pygame.draw.ellipse(surface, BLACK, rect, 3)
            shine = pygame.Surface((self.width//2, self.height//2), pygame.SRCALPHA)
            shine.fill((255,255,200,140))
            surface.blit(shine, (rect.x+self.width//4, rect.y+self.height//4))
            mark = font_small.render("$$", True, BLACK)
            surface.blit(mark, (rect.x+8, rect.y+5))
        elif self.type == "diamond":
            cx, cy = self.x, self.y
            w2, h2 = self.width//2, self.height//2
            pts = [(cx, cy-h2), (cx+w2, cy), (cx, cy+h2), (cx-w2, cy)]
            pygame.draw.polygon(surface, DIAMOND_COLOR, pts)
            pygame.draw.polygon(surface, BLACK, pts, 2)
            t = pygame.time.get_ticks()
            sx = cx + int(math.sin(t*0.01)*5)
            sy = cy - h2 + int(math.cos(t*0.02)*3)
            pygame.draw.circle(surface, WHITE, (sx, sy), 4)
        elif self.type == "stone":
            pygame.draw.ellipse(surface, STONE_COLOR, rect)
            pygame.draw.ellipse(surface, BLACK, rect, 2)
            for _ in range(3):
                sx = rect.x + random.randint(4, rect.width-8)
                sy = rect.y + random.randint(4, rect.height-8)
                ex = sx + random.randint(-8,8)
                ey = sy + random.randint(-8,8)
                pygame.draw.line(surface, (80,80,80), (sx, sy), (ex, ey), 2)

# ========== 钩爪 ==========
class Hook:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.angle = 0
        self.swing_dir = 1
        self.state = "swing"
        self.rope_length = 0
        self.caught_mineral = None
        self.retract_speed = BASE_RETRACT_SPEED

    def update(self, minerals):
        if self.state == "swing":
            self.angle += SWING_SPEED * self.swing_dir * 0.01
            if self.angle > 0.8:
                self.swing_dir = -1
            elif self.angle < -0.8:
                self.swing_dir = 1
        elif self.state == "extending":
            self.rope_length += HOOK_SPEED_DOWN
            tip_x = self.x + self.rope_length * math.sin(self.angle)
            tip_y = self.y + self.rope_length * math.cos(self.angle)
            if self.rope_length >= MAX_ROPE_LENGTH or tip_y >= HEIGHT - 20:
                self.state = "retracting"
                return
            for mineral in minerals:
                if not mineral.caught and mineral.get_rect().collidepoint(tip_x, tip_y):
                    self.caught_mineral = mineral
                    mineral.caught = True
                    self.state = "retracting"
                    break
        elif self.state == "retracting":
            weight = self.caught_mineral.weight if self.caught_mineral else 0.5
            speed = max(1, self.retract_speed / weight)
            self.rope_length -= speed
            if self.rope_length <= 5:
                self.rope_length = 0
                self.state = "swing"
                self.caught_mineral = None

    def launch(self):
        if self.state == "swing":
            self.state = "extending"
            self.rope_length = 10

    def use_dynamite(self):
        if self.state == "retracting" and self.caught_mineral:
            self.state = "swing"
            self.rope_length = 0
            self.caught_mineral = None
            return True
        return False

    def draw(self, surface):
        tip_x = self.x + self.rope_length * math.sin(self.angle)
        tip_y = self.y + self.rope_length * math.cos(self.angle)

        # 摆动指示虚线
        if self.state == "swing":
            indicator_len = MAX_ROPE_LENGTH * 0.7
            for i in range(20):
                t = i / 20
                px = self.x + indicator_len * t * math.sin(self.angle)
                py = self.y + indicator_len * t * math.cos(self.angle)
                if i % 2 == 0:
                    pygame.draw.circle(surface, (255,255,255, 100), (int(px), int(py)), 2)

        pygame.draw.line(surface, ROPE_COLOR, (self.x, self.y), (tip_x, tip_y), 5)
        hook_rect = pygame.Rect(tip_x-8, tip_y-8, 16, 16)
        pygame.draw.rect(surface, HOOK_METAL, hook_rect, border_radius=3)
        pygame.draw.rect(surface, BLACK, hook_rect, 2, border_radius=3)

        if self.caught_mineral and self.state == "retracting":
            self.caught_mineral.x = tip_x
            self.caught_mineral.y = tip_y + 12
            self.caught_mineral.draw(surface)

# ========== 主游戏 ==========
class GoldMiner:
    def __init__(self):
        self.reset()

    def reset(self):
        self.money = 0
        self.level = 1
        self.target = 300
        self.dynamite = 0
        self.strength_potion = 0
        self.lucky_charm = False
        self.state = "playing"
        self._prev_hook_state = "swing"
        self._last_caught = None
        self.hook = Hook(HOOK_START_X, HOOK_START_Y)
        self.minerals = []
        self.sparks = []
        self.generate_level()

    def generate_level(self):
        self.minerals = []
        n_small = 6 + self.level
        n_big = 3 + self.level//2
        n_diamond = 1 + self.level//3
        n_stone = 3 + self.level
        if self.lucky_charm:
            n_diamond += 2
            self.lucky_charm = False

        for _ in range(n_small):
            x = random.randint(100, 700)
            y = random.randint(GROUND_Y + 50, HEIGHT - 50)
            self.minerals.append(Mineral(x, y, "small_gold"))
        for _ in range(n_big):
            x = random.randint(100, 700)
            y = random.randint(GROUND_Y + 80, HEIGHT - 80)
            self.minerals.append(Mineral(x, y, "big_gold"))
        for _ in range(n_diamond):
            x = random.randint(150, 650)
            y = random.randint(GROUND_Y + 100, HEIGHT - 100)
            self.minerals.append(Mineral(x, y, "diamond"))
        for _ in range(n_stone):
            x = random.randint(100, 700)
            y = random.randint(GROUND_Y + 60, HEIGHT - 60)
            self.minerals.append(Mineral(x, y, "stone"))
        self.hook = Hook(HOOK_START_X, HOOK_START_Y)
        self.hook.retract_speed = BASE_RETRACT_SPEED + self.strength_potion * 0.3

    def update(self):
        if self.state != "playing": return
        self.hook.update(self.minerals)
        # 结算
        if self._prev_hook_state == "retracting" and self.hook.state == "swing":
            if self._last_caught is not None and self._last_caught in self.minerals:
                self.money += self._last_caught.value
                # 粒子
                for _ in range(15):
                    self.sparks.append(Sparkle(self._last_caught.x, self._last_caught.y, GOLD))
                self.minerals.remove(self._last_caught)
        self._prev_hook_state = self.hook.state
        self._last_caught = self.hook.caught_mineral

        # 粒子更新
        for spark in self.sparks[:]:
            spark.update()
            if spark.life <= 0:
                self.sparks.remove(spark)

        if self.money >= self.target:
            self.go_to_shop()

    def launch_hook(self):
        self.hook.launch()

    def use_dynamite(self):
        if self.dynamite > 0 and self.hook.state == "retracting":
            if self.hook.use_dynamite():
                self.dynamite -= 1
                return True
        return False

    def go_to_shop(self):
        self.state = "shop"

    def buy_item(self, item):
        if item == "dynamite" and self.money >= 100:
            self.money -= 100
            self.dynamite += 1
        elif item == "strength" and self.money >= 150:
            self.money -= 150
            self.strength_potion += 1
            self.hook.retract_speed += 0.3
        elif item == "lucky" and self.money >= 80:
            self.money -= 80
            self.lucky_charm = True
        elif item == "next":
            self.level += 1
            self.target = 300 + (self.level-1)*200
            self.generate_level()
            self.state = "playing"

    def draw_background(self):
        # 天空
        for y in range(GROUND_Y):
            r = int(SKY_TOP[0] + (SKY_BOTTOM[0]-SKY_TOP[0]) * y/GROUND_Y)
            g = int(SKY_TOP[1] + (SKY_BOTTOM[1]-SKY_TOP[1]) * y/GROUND_Y)
            b = int(SKY_TOP[2] + (SKY_BOTTOM[2]-SKY_TOP[2]) * y/GROUND_Y)
            pygame.draw.line(screen, (r,g,b), (0,y), (WIDTH,y))
        # 太阳
        pygame.draw.circle(screen, SUN_COLOR, (680, 60), 40)
        # 云
        for i in range(4):
            cx = 150 + i*200 + int(math.sin(pygame.time.get_ticks()*0.002 + i)*30)
            pygame.draw.ellipse(screen, CLOUD, (cx, 30+i*5, 70, 35))
        # 草地
        pygame.draw.rect(screen, GRASS_GREEN, (0, GROUND_Y-8, WIDTH, 15))
        # 泥土
        for y in range(GROUND_Y, HEIGHT):
            ratio = (y-GROUND_Y)/(HEIGHT-GROUND_Y)
            r = int(DIRT_TOP[0] - 100*ratio)
            g = int(DIRT_TOP[1] - 100*ratio)
            b = int(DIRT_TOP[2] - 80*ratio)
            pygame.draw.line(screen, (max(r,20), max(g,20), max(b,20)), (0,y), (WIDTH,y))

    def draw_ui(self):
        panel = pygame.Surface((WIDTH, 65), pygame.SRCALPHA)
        panel.fill((0,0,0,160))
        screen.blit(panel, (0,0))
        money_txt = font_med.render(f"Money: ${self.money} / Target: ${self.target}", True, GOLD)
        screen.blit(money_txt, (25, 18))
        x_pos = WIDTH - 250
        if self.dynamite > 0:
            screen.blit(font_small.render(f"💣 x{self.dynamite}", True, WHITE), (x_pos, 20))
            x_pos += 80
        if self.strength_potion > 0:
            screen.blit(font_small.render(f"💪 Lv.{self.strength_potion}", True, WHITE), (x_pos, 20))
            x_pos += 80
        if self.lucky_charm:
            screen.blit(font_small.render("🍀", True, WHITE), (x_pos, 20))
        if self.hook.state == "swing":
            screen.blit(font_small.render("SPACE / Click to launch", True, WHITE), (WIDTH//2-120, HEIGHT-25))
        elif self.hook.state == "retracting" and self.dynamite > 0:
            screen.blit(font_small.render("Press D for Dynamite!", True, RED), (WIDTH//2-120, HEIGHT-25))

    def draw_shop(self):
        overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        overlay.fill((0,0,0,210))
        screen.blit(overlay, (0,0))
        screen.blit(font_title.render("SHOP", True, GOLD), (WIDTH//2-80, 70))
        screen.blit(font_med.render(f"Your Money: ${self.money}", True, WHITE), (WIDTH//2-100, 140))
        items = [
            ("Dynamite ($100)", "dynamite", "Instantly pull up hook"),
            ("Strength Potion ($150)", "strength", "Permanently increase pull speed"),
            ("Lucky Clover ($80)", "lucky", "More diamonds next level"),
            ("▶ Next Level", "next", "Continue to next level")
        ]
        for i, (name, key, desc) in enumerate(items):
            y = 210 + i*75
            rect = pygame.Rect(130, y, 540, 60)
            color = (60,55,50)
            pygame.draw.rect(screen, color, rect, border_radius=12)
            pygame.draw.rect(screen, WHITE, rect, 2, border_radius=12)
            screen.blit(font_med.render(name, True, GOLD), (160, y+5))
            screen.blit(font_small.render(desc, True, WHITE), (160, y+32))
            mx, my = pygame.mouse.get_pos()
            if rect.collidepoint(mx, my):
                pygame.draw.rect(screen, (120,100,80), rect, 3, border_radius=12)
        screen.blit(font_small.render("Click to buy, ESC to skip", True, WHITE), (WIDTH//2-120, HEIGHT-40))

    def draw_gameover(self):
        overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        overlay.fill((0,0,0,210))
        screen.blit(overlay, (0,0))
        screen.blit(font_title.render("GAME OVER", True, RED), (WIDTH//2-150, HEIGHT//2-60))
        screen.blit(font_large.render(f"Final Money: ${self.money}", True, WHITE), (WIDTH//2-120, HEIGHT//2+10))
        screen.blit(font_med.render("Press R to restart", True, WHITE), (WIDTH//2-100, HEIGHT//2+70))

    def draw(self):
        self.draw_background()
        for mineral in self.minerals:
            if not mineral.caught:
                mineral.draw(screen)
        self.hook.draw(screen)
        for spark in self.sparks:
            spark.draw(screen)
        self.draw_ui()
        if self.state == "shop":
            self.draw_shop()
        elif self.state == "gameover":
            self.draw_gameover()

    def run(self):
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                    if self.state == "playing":
                        self.launch_hook()
                    elif self.state == "shop":
                        mx, my = pygame.mouse.get_pos()
                        items = [
                            (pygame.Rect(130,210,540,60), "dynamite"),
                            (pygame.Rect(130,285,540,60), "strength"),
                            (pygame.Rect(130,360,540,60), "lucky"),
                            (pygame.Rect(130,435,540,60), "next"),
                        ]
                        for rect, key in items:
                            if rect.collidepoint(mx, my):
                                self.buy_item(key)
                if event.type == pygame.KEYDOWN:
                    if self.state == "playing":
                        if event.key == pygame.K_SPACE:
                            self.launch_hook()
                        elif event.key == pygame.K_d:
                            self.use_dynamite()
                    elif self.state == "shop":
                        if event.key == pygame.K_ESCAPE:
                            self.buy_item("next")
                    elif self.state == "gameover":
                        if event.key == pygame.K_r:
                            self.reset()
            if self.state == "playing":
                self.update()
            self.draw()
            pygame.display.flip()
            clock.tick(60)
        pygame.quit()
        sys.exit()

if __name__ == "__main__":
    game = GoldMiner()
    game.run()