import pygame
import math
import random

# 初始化
pygame.init()
W, H = 1200, 700
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("荒野大陆RPG")
clock = pygame.time.Clock()
FPS = 60

# 颜色定义
SKY_DAY = (130, 200, 240)
SKY_NIGHT = (12, 12, 45)
GRASS = (62, 165, 55)
WATER = (40, 120, 180)
STONE = (90, 95, 100)
TREE = (60, 38, 20)
TREE_LEAF = (22, 110, 30)
PLAYER = (20, 120, 220)
PLAYER_OUT = (255,255,255)
MONSTER_SLIME = (60, 220, 90)
MONSTER_GOBLIN = (180, 140, 30)
MONSTER_WOLF = (60,50,45)
UI_BG = (20,20,20,185)
WHITE = (255,255,255)
RED = (220,30,30)
GREEN_HP = (20,200,40)
BLUE_MP = (20,120,220)
GOLD = (255, 210, 0)
BLACK = (0,0,0)

# 地图尺寸
MAP_W = 4000
MAP_H = 3200
TILE_SIZE = 80

# 玩家属性
player = {
    "x": MAP_W//2,
    "y": MAP_H//2,
    "vx":0, "vy":0,
    "speed": 220,
    "hp": 100, "max_hp": 100,
    "mp": 60, "max_mp": 60,
    "atk": 18, "defen": 5,
    "level": 1, "exp": 0, "exp_need": 80,
    "gold": 120,
    "stat_point": 3, # 升级加点
    "dir": 1, # 1右 -1左
    "skill_cd": 0,
    "inv": [], # 背包
    "equip": {"weapon":None,"armor":None}
}

# 物品配置
ITEM_DATA = {
    "hp_potion":{"name":"生命药水","type":"consume","hp":40,"price":15},
    "mp_potion":{"name":"魔力药水","type":"consume","mp":30,"price":12},
    "iron_sword":{"name":"铁剑","type":"weapon","atk":8,"price":120},
    "cloth_armor":{"name":"布衣","type":"armor","defen":4,"price":90},
    "gold_bag":{"name":"钱袋","type":"gold","gold":50,"price":0}
}

# 怪物模板
MONSTER_TEMP = {
    "slime":{"hp":45,"atk":7,"speed":70,"exp":22,"gold":12,"color":MONSTER_SLIME},
    "goblin":{"hp":70,"atk":12,"speed":90,"exp":38,"gold":24,"color":MONSTER_GOBLIN},
    "wolf":{"hp":95,"atk":18,"speed":130,"exp":55,"gold":36,"color":MONSTER_WOLF}
}

# 全局列表
tree_list = []
rock_list = []
water_list = []
monster_list = []
chest_list = []
npc_list = []
dialog_active = False
dialog_text = ""
shop_open = False
task_list = [{"name":"猎杀史莱姆","target":"slime","count":5,"now":0,"reward_gold":100,"reward_exp":120}]
day_time = 0
day_len = 50
is_night = False
cam_x = 0
cam_y = 0
hit_flash = 0 #受伤闪烁

# 生成地图障碍物、怪物、NPC、宝箱
def generate_world():
    # 树木
    for _ in range(220):
        x = random.randint(100, MAP_W-100)
        y = random.randint(100, MAP_H-100)
        tree_list.append([x,y,random.randint(28,45)])
    # 岩石
    for _ in range(160):
        x = random.randint(100, MAP_W-100)
        y = random.randint(100, MAP_H-100)
        rock_list.append([x,y,random.randint(22,40)])
    # 水域
    for _ in range(40):
        x = random.randint(200, MAP_W-200)
        y = random.randint(200, MAP_H-200)
        r = random.randint(60,140)
        water_list.append([x,y,r])
    # 怪物
    for mtype in MONSTER_TEMP:
        for _ in range(18):
            x = random.randint(200, MAP_W-200)
            y = random.randint(200, MAP_H-200)
            t = MONSTER_TEMP[mtype]
            monster_list.append({
                "x":x,"y":y,"type":mtype,
                "hp":t["hp"],"max_hp":t["hp"],
                "atk":t["atk"],"speed":t["speed"],
                "exp":t["exp"],"gold":t["gold"],
                "color":t["color"],
                "cd":0, "alert":False
            })
    # 宝箱
    for _ in range(30):
        x = random.randint(150, MAP_W-150)
        y = random.randint(150, MAP_H-150)
        chest_list.append([x,y,False]) # false未开启
    # NPC商人+任务NPC
    npc_list.append({"x":MAP_W//2+300,"y":MAP_H//2,"type":"shop","name":"商人老王"})
    npc_list.append({"x":MAP_W//2-300,"y":MAP_H//2,"type":"task","name":"猎人"})

generate_world()

# 字体兼容兜底
try:
    font = pygame.font.SysFont("msyh",20)
    font_small = pygame.font.SysFont("msyh",15)
    font_big = pygame.font.SysFont("msyh",28)
except:
    font = pygame.font.Font(None,20)
    font_small = pygame.font.Font(None,15)
    font_big = pygame.font.Font(None,28)

# 碰撞检测
def circle_collide(x1,y1,r1,x2,y2,r2):
    return math.hypot(x1-x2,y1-y2) < r1+r2

# 绘制玩家
def draw_player():
    px = player["x"] - cam_x
    py = player["y"] - cam_y
    # 受伤闪烁
    if hit_flash > 0 and int(hit_flash*8)%2 == 0:
        return
    # 身体
    pygame.draw.circle(screen, PLAYER, (int(px),int(py)), 18)
    pygame.draw.circle(screen, PLAYER_OUT, (int(px),int(py)), 18,2)
    # 朝向眼睛
    eye_x = px + player["dir"]*7
    pygame.draw.circle(screen, WHITE, (int(eye_x), int(py-6)),4)
    pygame.draw.circle(screen, BLACK, (int(eye_x+player["dir"]*1.5), int(py-6)),2)

# 绘制怪物
def draw_monsters(dt):
    for m in monster_list:
        mx = m["x"] - cam_x
        my = m["y"] - cam_y
        # 血条
        bar_w = 36
        bar_h = 5
        fill = bar_w * (m["hp"]/m["max_hp"])
        pygame.draw.rect(screen, RED, [mx-bar_w//2, my-28, bar_w, bar_h])
        pygame.draw.rect(screen, GREEN_HP, [mx-bar_w//2, my-28, fill, bar_h])
        # 怪物本体
        pygame.draw.circle(screen, m["color"], (int(mx),int(my)), 16)
        # 追击红线提示
        if m["alert"]:
            pygame.draw.circle(screen, RED, (int(mx),int(my)), 19, 2)

# 绘制障碍物
def draw_map_obj():
    # 水域底层
    for x,y,r in water_list:
        sx = x - cam_x
        sy = y - cam_y
        pygame.draw.circle(screen, WATER, (int(sx),int(sy)), r)
    # 树木
    for x,y,sz in tree_list:
        sx = x - cam_x
        sy = y - cam_y
        pygame.draw.rect(screen, TREE, [sx-6, sy, 12, sz*0.7])
        pygame.draw.circle(screen, TREE_LEAF, (int(sx), int(sy-sz*0.3)), sz)
    # 岩石
    for x,y,sz in rock_list:
        sx = x - cam_x
        sy = y - cam_y
        pygame.draw.circle(screen, STONE, (int(sx),int(sy)), sz)
    # 宝箱
    for x,y,open_flag in chest_list:
        sx = x - cam_x
        sy = y - cam_y
        if not open_flag:
            pygame.draw.rect(screen, GOLD, [sx-16, sy-12,32,24])
            pygame.draw.rect(screen, BLACK, [sx-16, sy-12,32,24],2)
        else:
            pygame.draw.rect(screen, (80,60,20), [sx-16, sy-12,32,24])
    # NPC
    for npc in npc_list:
        nx = npc["x"] - cam_x
        ny = npc["y"] - cam_y
        pygame.draw.circle(screen, (220,100,160), (int(nx),int(ny)), 17)
        pygame.draw.circle(screen, WHITE, (int(nx),int(ny)),17,2)
        name_txt = font_small.render(npc["name"],True,WHITE)
        screen.blit(name_txt, (nx-30, ny-30))

# 玩家攻击技能
def player_attack():
    player["skill_cd"] = 0.6
    atk_range = 90
    atk_x = player["x"] + player["dir"] * 50
    damage = player["atk"]
    # 装备加成
    eq_weapon = player["equip"]["weapon"]
    if eq_weapon:
        damage += ITEM_DATA[eq_weapon]["atk"]
    # 遍历怪物造成伤害
    for m in monster_list:
        if circle_collide(atk_x, player["y"], atk_range, m["x"], m["y"], 16):
            m["hp"] -= damage
            m["alert"] = True
            m["cd"] = 0.8

# 打开宝箱
def open_chest(cx,cy):
    for ch in chest_list:
        x,y,opened = ch
        if x == cx and y == cy and not opened:
            ch[2] = True
            # 随机奖励
            reward = random.choice(["hp_potion","mp_potion","gold_bag"])
            player["inv"].append(reward)
            if reward == "gold_bag":
                player["gold"] += ITEM_DATA["gold_bag"]["gold"]

# 使用消耗品
def use_item(name):
    if name not in player["inv"]:
        return
    data = ITEM_DATA[name]
    if data["type"] == "consume":
        if "hp" in data:
            player["hp"] = min(player["max_hp"], player["hp"] + data["hp"])
        if "mp" in data:
            player["mp"] = min(player["max_mp"], player["mp"] + data["mp"])
        player["inv"].remove(name)

# 升级判定
def check_level_up():
    while player["exp"] >= player["exp_need"]:
        player["exp"] -= player["exp_need"]
        player["level"] += 1
        player["stat_point"] += 3
        player["exp_need"] = int(player["exp_need"] * 1.4)
        player["max_hp"] += 12
        player["max_mp"] += 6
        player["hp"] = player["max_hp"]
        player["mp"] = player["max_mp"]

# UI绘制：状态、背包、商店、任务、对话
def draw_ui():
    # 血蓝条
    bar_x = 15
    bar_y = 12
    bar_w = 240
    bar_h = 22
    # HP
    pygame.draw.rect(screen, RED, [bar_x, bar_y, bar_w, bar_h])
    hp_fill = bar_w * (player["hp"] / player["max_hp"])
    pygame.draw.rect(screen, GREEN_HP, [bar_x, bar_y, hp_fill, bar_h])
    # MP
    mp_y = bar_y + 26
    pygame.draw.rect(screen, BLACK, [bar_x, mp_y, bar_w, bar_h])
    mp_fill = bar_w * (player["mp"] / player["max_mp"])
    pygame.draw.rect(screen, BLUE_MP, [bar_x, mp_y, mp_fill, bar_h])
    pygame.draw.rect(screen, WHITE, [bar_x, bar_y, bar_w, bar_h],2)
    pygame.draw.rect(screen, WHITE, [bar_x, mp_y, bar_w, bar_h],2)
    # 等级金币
    stat_txt = font.render(f"Lv.{player['level']} | 金币:{player['gold']} | 加点:{player['stat_point']}", True, GOLD)
    screen.blit(stat_txt, (bar_x, mp_y+30))
    exp_txt = font_small.render(f"经验 {player['exp']}/{player['exp_need']}", True, WHITE)
    screen.blit(exp_txt, (bar_x, mp_y+55))

    # 任务面板
    task = task_list[0]
    task_surf = pygame.Surface((260,90), pygame.SRCALPHA)
    task_surf.fill(UI_BG)
    pygame.draw.rect(task_surf, WHITE, (0,0,260,90),2)
    t1 = font.render(f"任务:{task['name']}", True, WHITE)
    t2 = font_small.render(f"进度:{task['now']}/{task['count']}", True, WHITE)
    t3 = font_small.render(f"奖励:{task['reward_gold']}金 {task['reward_exp']}经验", True, GOLD)
    task_surf.blit(t1,(10,8))
    task_surf.blit(t2,(10,32))
    task_surf.blit(t3,(10,56))
    screen.blit(task_surf, (W-270, 10))

    # 操作提示底部
    hint = font_small.render("WASD移动 | 空格攻击 | E交互宝箱/NPC | 1/2用药 | U加点 | B背包 | S商店", True, WHITE)
    screen.blit(hint, (20, H-28))

    # 对话弹窗
    if dialog_active:
        dia_surf = pygame.Surface((600,130), pygame.SRCALPHA)
        dia_surf.fill(UI_BG)
        pygame.draw.rect(dia_surf, WHITE, (0,0,600,130),3)
        dia_txt = font.render(dialog_text, True, WHITE)
        dia_hint = font_small.render("按ESC关闭对话", True, WHITE)
        dia_surf.blit(dia_txt, (20,20))
        dia_surf.blit(dia_hint, (20,70))
        screen.blit(dia_surf, ((W-600)//2, H-160))

    # 商店弹窗
    if shop_open:
        shop_surf = pygame.Surface((320,360), pygame.SRCALPHA)
        shop_surf.fill(UI_BG)
        pygame.draw.rect(shop_surf, WHITE, (0,0,320,360),3)
        shop_surf.blit(font_big.render("商店",True,GOLD),(10,8))
        y = 45
        buy_list = ["hp_potion","mp_potion","iron_sword","cloth_armor"]
        for key in buy_list:
            d = ITEM_DATA[key]
            txt = font.render(f"{d['name']}  {d['price']}G", True, WHITE)
            shop_surf.blit(txt,(10,y))
            y +=36
        shop_surf.blit(font_small.render("数字键1-4购买 | ESC关闭",True,WHITE),(10,320))
        screen.blit(shop_surf, (W-330, 120))

# 主循环
running = True
while running:
    dt = clock.tick(FPS) / 1000
    mx, my = pygame.mouse.get_pos()
    keys = pygame.key.get_pressed()

    # 昼夜
    day_time += dt
    if day_time > day_len:
        day_time = 0
    is_night = day_time > day_len * 0.65
    screen.fill(SKY_NIGHT if is_night else SKY_DAY)

    # 玩家移动输入
    move_x = 0
    move_y = 0
    if keys[pygame.K_a]: move_x -=1; player["dir"] = -1
    if keys[pygame.K_d]: move_x +=1; player["dir"] = 1
    if keys[pygame.K_w]: move_y -=1
    if keys[pygame.K_s]: move_y +=1
    # 归一化斜向速度
    if move_x !=0 and move_y !=0:
        move_x *= 0.707
        move_y *= 0.707
    player["vx"] = move_x * player["speed"]
    player["vy"] = move_y * player["speed"]
    # 更新坐标
    player["x"] += player["vx"] * dt
    player["y"] += player["vy"] * dt
    # 世界边界限制
    player["x"] = max(30, min(MAP_W-30, player["x"]))
    player["y"] = max(30, min(MAP_H-30, player["y"]))

    # 相机跟随
    cam_x = player["x"] - W//2
    cam_y = player["y"] - H//2

    # 受伤闪烁计时
    if hit_flash >0:
        hit_flash -= dt
    # 技能冷却
    if player["skill_cd"] >0:
        player["skill_cd"] -= dt

    # 怪物AI逻辑
    for m in monster_list:
        dist_p = math.hypot(player["x"]-m["x"], player["y"]-m["y"])
        # 进入警戒范围追击
        if dist_p < 320:
            m["alert"] = True
        if m["alert"]:
            ang = math.atan2(player["y"] - m["y"], player["x"] - m["x"])
            m["x"] += math.cos(ang) * m["speed"] * dt
            m["y"] += math.sin(ang) * m["speed"] * dt
            # 怪物攻击玩家
            if dist_p < 45 and m["cd"] <=0:
                def_equip = player["equip"]["armor"]
                defen = player["defen"]
                if def_equip:
                    defen += ITEM_DATA[def_equip]["defen"]
                real_dmg = max(1, m["atk"] - defen)
                player["hp"] -= real_dmg
                hit_flash = 0.4
                m["cd"] = 1.2
        if m["cd"] >0:
            m["cd"] -= dt
        # 怪物死亡掉落经验金币
        if m["hp"] <=0:
            player["exp"] += m["exp"]
            player["gold"] += m["gold"]
            # 任务计数
            if task_list[0]["target"] == m["type"]:
                task_list[0]["now"] +=1
            monster_list.remove(m)
    # 等级提升检查
    check_level_up()

    # 事件监听
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            running = False
        if ev.type == pygame.KEYDOWN:
            # ESC关闭弹窗
            if ev.key == pygame.K_ESCAPE:
                dialog_active = False
                shop_open = False
            # 空格攻击
            if ev.key == pygame.K_SPACE and player["skill_cd"] <=0:
                player_attack()
            # E交互 NPC/宝箱
            if ev.key == pygame.K_e:
                # 宝箱交互
                for x,y,op in chest_list:
                    if circle_collide(player["x"],player["y"],30,x,y,20) and not op:
                        open_chest(x,y)
                # NPC对话
                for npc in npc_list:
                    if circle_collide(player["x"],player["y"],40,npc["x"],npc["y"],30):
                        dialog_active = True
                        if npc["type"] == "shop":
                            dialog_text = "商人：按S打开商店买卖道具"
                        else:
                            task = task_list[0]
                            if task["now"] >= task["count"]:
                                dialog_text = f"猎人：任务完成！奖励{task['reward_gold']}金币 {task['reward_exp']}经验"
                                player["gold"] += task["reward_gold"]
                                player["exp"] += task["reward_exp"]
                                task_list.pop(0)
                                task_list.append({"name":"猎杀史莱姆","target":"slime","count":5,"now":0,"reward_gold":100,"reward_exp":120})
                            else:
                                dialog_text = f"猎人任务：击杀{task['target']} 进度{task['now']}/{task['count']}"
            # S商店开关
            if ev.key == pygame.K_s:
                shop_open = not shop_open
            # 道具快捷键
            if ev.key == pygame.K_1:
                use_item("hp_potion")
            if ev.key == pygame.K_2:
                use_item("mp_potion")
            # U属性加点
            if ev.key == pygame.K_u and player["stat_point"]>0:
                player["stat_point"] -=1
                player["max_hp"] += 8
                player["hp"] = player["max_hp"]
            # 商店购买 1-4
            buy_keys = {pygame.K_1:"hp_potion",pygame.K_2:"mp_potion",pygame.K_3:"iron_sword",pygame.K_4:"cloth_armor"}
            if shop_open and ev.key in buy_keys:
                item_id = buy_keys[ev.key]
                cost = ITEM_DATA[item_id]["price"]
                if player["gold"] >= cost:
                    player["gold"] -= cost
                    data = ITEM_DATA[item_id]
                    if data["type"] in ("weapon","armor"):
                        player["equip"][data["type"]] = item_id
                    else:
                        player["inv"].append(item_id)

    # 绘制渲染层级
    draw_map_obj()
    draw_monsters(dt)
    draw_player()
    draw_ui()

    pygame.display.update()
pygame.quit()