from flask import Flask, request, jsonify, send_from_directory, abort, render_template_string, Response, stream_with_context, redirect
import requests
import os
import json

app = Flask(__name__)

GAME_SERVER_URL = "http://127.0.0.1:8080/gameapi/"
DOWNLOAD_DIR = r"D:\game"

os.makedirs(DOWNLOAD_DIR, exist_ok=True)

RESOURCE_MAP = {
    "Wood": "木材",
    "Stone": "石头",
    "Clay": "粘土",
    "Coal": "煤炭",
    "Sand": "沙子",
    "Lime": "石灰",
    "IronOre": "铁矿",
    "CopperOre": "铜矿",
    "AluminumOre": "铝矿",
    "SilverOre": "银矿",
    "TungstenOre": "钨矿",
    "Magnetite": "磁铁矿",
    "Cryolite": "冰晶石",
    "Monazite": "独居石",
    "Saltpeter": "硝石",
    "Sulfur": "硫磺",
    "Silicon": "硅",
    "ZincOre": "锌矿",
    "LeadOre": "铅矿",
    "Chromite": "铬矿",
    "MercuryOre": "汞矿",
    "NickelOre": "镍矿",
    "ManganeseOre": "锰矿",
    "Pyrolusite": "软锰矿",
    "Diamond": "金刚石",
    "Chalcanthite": "胆矾",
    "Copperas": "绿矾",
    
    "Iron": "铁",
    "Copper": "铜",
    "Aluminum": "铝",
    "Silver": "银",
    "Lead": "铅",
    "Chromium": "铬",
    "Nickel": "镍",
    "Mercury": "汞",
    "Tungsten": "钨",
    "Manganese": "锰",
    "Zinc": "锌",
    
    "SulfuricAcid": "硫酸",
    "NitricAcid": "硝酸",
    "Hydrogen": "氢气",
    "Bromine": "溴",
    "Methane": "甲烷",
    "Bromomethane": "一溴甲烷",
    "Toluene": "甲苯",
    "TNT": "TNT",
    
    "Linen": "亚麻",
    "SpringWheat": "春小麦",
    "WinterWheat": "冬小麦",
    "Cabbage": "白菜",
    
    "Flour": "面粉",
    "FermentedDough": "发酵面糊",
    "Bread": "面包",
    "Steel": "钢",
    
    "Pork": "猪肉",
    "Beef": "牛肉",
    "Leather": "皮革",
    
    "Cannon": "火炮",
    "Cannonball": "炮弹",
    
    "CoinCopper": "铜币",
    "CoinAluminum": "铝币",
    "CoinSilver": "银币",
    
    "StoneAxe": "石斧",
    "StonePickaxe": "石镐",
    "IronAxe": "铁斧",
    "IronPickaxe": "铁镐",
    "Saw": "锯",
    "Drill": "钻头",
    
    "WoodenBoat": "木船",
    "AdvancedWoodenBoat": "高级木船",
    "SteamAdvancedWoodenBoat": "蒸汽高级木船",
    "MotorAdvancedWoodenBoat": "电机高级木船",
    "BatteryAdvancedWoodenBoat": "蓄电池高级木船",
    "Ironclad": "铁甲舰",
    "SteamIronclad": "蒸汽铁甲舰",
    "MotorIronclad": "电机铁甲舰",
    "BatteryMotorIronclad": "蓄电池电机铁甲舰",
    "DualMotorIronclad": "双电机铁甲舰",
    "DualMotorBatteryIronclad": "双电机蓄电池铁甲舰",
    "WeatherShip": "测风船（木船）",
    "WeatherShipFromSteamAdvanced": "测风船（蒸汽高级）",
    "WeatherShipFromMotorAdvanced": "测风船（电机高级）",
    "WeatherShipFromBatteryAdvanced": "测风船（蓄电池高级）",
    "WeatherShipFromSteamIronclad": "测风船（蒸汽铁甲）",
    "WeatherShipFromMotorIronclad": "测风船（电机铁甲）",
    "WeatherShipFromBatteryMotorIronclad": "测风船（蓄电池电机铁甲）",
    "WeatherShipFromDualMotorIronclad": "测风船（双电机铁甲）",
    "WeatherShipFromDualMotorBatteryIronclad": "测风船（双电机蓄电池铁甲）",
    
    "Kiln": "窑",
    "BlastFurnace": "高炉",
    "Smelter": "熔炉",
    "Loom": "织布机",
    "WoodenHouse": "木房",
    "StoneWoodHouse": "木石房",
    "SteamEngine": "蒸汽机",
    "Motor": "电机",
    "WireSwitch": "导线开关",
    "WindWheel": "风轮",
    "Tower": "塔架",
    "WindGenerator": "风力发电机",
    "Gunpowder": "黑火药",
    "WoodenBucket": "木桶",
    "IronBucket": "铁桶",
    "LinenCloth": "麻布",
    "WindSock": "风向袋",
    "Hub": "轮毂",
    "WindCup": "风杯",
    "ThermometerScreen": "百叶箱",
    "Condenser": "冷凝管",
    "Glass": "玻璃",
    "Thermometer": "温度计",
    "Millstone": "石磨",
    "FlourProduct": "面粉产品",
    "ExplosivePack": "炸药包",
    "SolarPanel": "太阳能板",
    "ManganeseDioxide": "二氧化锰",
    "ZincManganeseBattery": "锌锰电池",
    "Accumulator": "蓄电池",
    "StainlessSteel": "不锈钢",
    "Electrolyzer": "电解池",
    "SmallWaterTank": "小储水罐",
    "WaterTank": "储水罐",
    "LargeWaterTank": "大储水罐",
    "Hygrometer": "湿度计",
    "RainGauge": "雨量计",
    "TungstenFilament": "钨丝",
    "Bulb": "灯泡",
    "CoinMold": "铸币模具",
    
    "Lumbermill1": "一级伐木场",
    "Quarry1": "一级采石场",
    "Lumbermill2": "二级伐木场",
    "Quarry2": "二级采石场",
    "ChemicalPlant1": "一级化工厂",
    "WeatherStationWind": "测风气象站",
    "WeatherStationTempHum": "温湿度测站",
    "Sawmill": "锯木厂",
    "StoneCutter": "切石机",
    "Foundry": "铸造厂",
    "RollingMill": "轧钢厂",
    
    "Ranch": "牧场",
    "CharcoalKiln": "炭窑",
    "WindTurbine": "风力涡轮机",
    "WaterWell": "水井",
    "Bakery": "面包房",
    "Tannery": "制革厂",
    "Apiary": "养蜂场",
    "Smokehouse": "熏制房",
    "CheeseFactory": "奶酪厂",
    "OreWasher": "洗矿机",
    "ChemicalLab": "化学实验室",
    "BatteryFactory": "电池厂",
    "WireMill": "拉丝机",
    "MachineShop": "机械车间",
    "Factory": "工厂"
}

def is_mobile_device(user_agent):
    if not user_agent:
        return False
    ua = user_agent.lower()
    mobile_keywords = ['mobile', 'android', 'iphone', 'ipod', 'ipad', 'phone', 'tablet']
    for kw in mobile_keywords:
        if kw in ua:
            return True
    return False

MOBILE_RULES_HTML = """
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>国家争霸赛 · 手机规则</title>
    <style>
        body {
            font-family: 'Segoe UI', Roboto, sans-serif;
            background: #0b1a2f;
            color: #e0e0e0;
            margin: 0;
            padding: 15px;
            line-height: 1.5;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
        }
        h1 {
            color: #ffd966;
            font-size: 1.8rem;
            text-align: center;
            border-bottom: 2px solid #ff9800;
            padding-bottom: 10px;
        }
        h2 {
            font-size: 1.4rem;
            color: #ffd966;
            border-left: 4px solid #ff9800;
            padding-left: 10px;
            margin-top: 25px;
        }
        h3 {
            font-size: 1.2rem;
            color: #ffb74d;
            margin-top: 15px;
        }
        .rule-section {
            background: rgba(255,255,255,0.05);
            border-radius: 12px;
            padding: 15px;
            margin-bottom: 20px;
        }
        .btn-group {
            display: flex;
            gap: 10px;
            justify-content: center;
            margin: 20px 0;
        }
        .btn {
            background: #2a3f5e;
            border: 1px solid #ff9800;
            color: white;
            padding: 12px 20px;
            border-radius: 40px;
            text-decoration: none;
            text-align: center;
            flex: 1;
            font-weight: bold;
        }
        .btn.primary {
            background: #ff9800;
            color: black;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 10px 0;
            background: rgba(255,255,255,0.1);
            color: white;
            font-size: 0.9rem;
        }
        th, td {
            border: 1px solid #ff9800;
            padding: 6px;
            text-align: left;
        }
        th {
            background-color: #ff9800;
            color: black;
        }
        ul {
            padding-left: 20px;
        }
        li {
            margin-bottom: 5px;
        }
        .footer {
            text-align: center;
            margin-top: 30px;
            font-size: 0.8rem;
            color: #aaa;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>📜 国家争霸赛 · 手机规则</h1>
        <div class="btn-group">
            <a href="/play" class="btn primary">🎮 开始游戏</a>
            <a href="/?force_pc=1" class="btn">💻 电脑版规则与游戏与源代码</a>
        </div>

        <div class="rule-section">
            <h2>一、游戏时间</h2>
            <p>时间点系统：每现实天对应32时间点，每游戏日4时间点（每6小时一个时间点）。游戏日期从元年1月1日0时开始，每4时间点增加一天。季节：春3~5月、夏6~8月、秋9~11月、冬12~次年2月。部分种植受季节限制。</p>
        </div>

        <div class="rule-section">
            <h2>二、国家属性</h2>
            <ul>
                <li><strong>民心</strong>：影响人口增长和税收，低民心可能导致叛乱。</li>
                <li><strong>军心</strong>：影响军队士气和战斗力。</li>
                <li><strong>威望</strong>：影响外交和事件。</li>
                <li><strong>权利</strong>：影响政策执行和权臣出现。</li>
                <li><strong>人口</strong>：影响最大行动次数和建筑条件。最大人口由住房决定。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>三、资源、建筑、工具总览</h2>
            <p>以下列出了游戏中所有可通过操作获得的物品（包括资源、建筑、工具），中文名对应客户端按钮显示，英文名为代码中的标识：</p>
            <table>
                <tr><th>中文名</th><th>英文标识</th></tr>
"""

sorted_items = sorted(RESOURCE_MAP.items(), key=lambda x: x[1])
for eng, chn in sorted_items:
    MOBILE_RULES_HTML += f"<tr><td>{chn}</td><td>{eng}</td></tr>"

MOBILE_RULES_HTML += """
            </table>
        </div>

        <div class="rule-section">
            <h2>四、工具与采集</h2>
            <ul>
                <li>工具类型：石斧、石镐、铁斧、铁镐、锯、钻头等。工具可减少对应资源的采集时间。</li>
                <li>采集基础时间：木材/石头20，粘土/沙子16，铁矿/铜矿24，钨矿/金刚石40，其他参见客户端提示。</li>
                <li>工具减时：石制工具-4，铁制工具-8，锯/钻头-12。</li>
                <li>每次采集固定产出20单位资源。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>五、操作类型</h2>
            <p>所有操作均可附加工资（铜币/铝币/银币），工资总额每5点使产出倍数+1（不足5按1倍计算）。不发工资则无加成。</p>
            <ul>
                <li><strong>采集</strong>：基础耗时20-40，受工具影响，产出20资源。</li>
                <li><strong>冶炼</strong>：需要熔炉、煤炭、石灰/冰晶石，耗时固定20时间点（无论数量），产出金属。</li>
                <li><strong>建造</strong>：消耗资源，获得建筑或工具，耗时1-24，具体成本见客户端按钮提示。</li>
                <li><strong>化工</strong>：需要特定建筑和原料，产出化工产品（硫酸、硝酸、TNT等）。</li>
                <li><strong>种植</strong>：需要种子和合适季节，耗时320-880，收获20作物。</li>
                <li><strong>狩猎</strong>：需要斧头，耗时40，获得肉和皮革（野猪得猪肉，野牛得牛肉+皮革）。</li>
                <li><strong>驯化</strong>：需要食物，耗时20，获得动物（狼变狗，野猫变家猫）。</li>
                <li><strong>民心行动</strong>：耗时4，提升民心/军心/威望等，具体效果见按钮描述。</li>
                <li><strong>铸币</strong>：需要铸币模具，消耗1金属产出5硬币，耗时1。</li>
                <li><strong>备战/宣战</strong>：耗时8，标记目标国家。</li>
                <li><strong>进攻</strong>：对宣战国发动普通进攻（耗时8，每轮一次，造成15伤害）。</li>
                <li><strong>轰炸</strong>：消耗2炸药包，造成30伤害，耗时8。</li>
                <li><strong>火炮</strong>：消耗1火炮、1炮弹、1黑火药，造成40伤害，耗时8。</li>
                <li><strong>制造</strong>：制造火炮（消耗20钢，耗时16）或炮弹（消耗1钢，耗时16）。</li>
                <li><strong>裁兵</strong>：裁撤士兵（先普通后精兵），精兵被裁降低军心威望。</li>
                <li><strong>训练精兵</strong>：将普通兵转为精兵，精兵每天消耗1面包。</li>
                <li><strong>撤销精兵</strong>：精兵降为普通兵。</li>
                <li><strong>停工/复工</strong>：停止或恢复自动生产建筑的运作。</li>
                <li><strong>征兵</strong>：招募新兵，耗时28+（每超过100人后每50人多4时间点），征兵后总兵不得超过人口的四分之三。若总兵超过“维持不混乱上限（100+2*精兵数）”，则军队陷入混乱，战斗力衰减。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>六、军队混乱与战斗力</h2>
            <ul>
                <li>维持不混乱上限 U = 100 + 2 × 精兵数。</li>
                <li>若总兵数 ≤ U，有效战斗力 = 普通兵数 + 2 × 精兵数。</li>
                <li>若总兵数 > U，有效战斗力 = U × 3/4 + 分段衰减：超出部分每100人依次按1/8、1/16、1/32...计算。</li>
                <li>示例：总兵350，无精兵，U=100，有效战力 = 100×3/4 + 100/8 + 100/16 + 50/32 ≈ 95.3125。</li>
                <li>精兵一个顶两个普通兵。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>七、建筑列表（部分）</h2>
            <ul>
                <li>工具类：石斧、石镐、铁斧、铁镐、锯、钻头</li>
                <li>船类：木船、高级木船（及多种动力型）、铁甲舰（及多种动力型）、测风船（多种母船型）</li>
                <li>生产建筑：窑、高炉、熔炉、织布机、伐木场(1/2级)、采石场(1/2级)、锯木厂、切石机、铸造厂、轧钢厂</li>
                <li>发电：蒸汽机、电机、风力发电机、太阳能板</li>
                <li>民生：木房、木石房</li>
                <li>气象：百叶箱、风向袋、风杯、温度计、湿度计、雨量计、测风站等</li>
                <li>军事：火炮（资源）、炮弹（资源）</li>
                <li>特殊：铸币模具</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>八、自动生产建筑</h2>
            <ul>
                <li>伐木场、采石场、锯木厂、切石机、铸造厂、轧钢厂等会每4时间点自动产出对应资源。</li>
                <li>可以手动停工（停止产出，也不发工资）或复工。</li>
                <li>每月15日自动支付工资（每个建筑基础30货币），若国库不足则民心威望大幅下降。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>九、税收与公司</h2>
            <ul>
                <li>可设置公司税和个人所得税，支持线性或累进税率。</li>
                <li>公司每月产生利润，每年1月1日分红，政府收取公司税，个人收取红利税。</li>
                <li>个人所得税可选择是否对收入（每月民间货币增量）征税。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>十、战争机制</h2>
            <ul>
                <li>必须先备战，再宣战，才能发动进攻。</li>
                <li>每现实天只能发动一次进攻（包括普通、轰炸、火炮）。</li>
                <li>每次进攻增加对方投降进度，达到100时对方投降（当前版本仅移除敌对方）。</li>
                <li>宣战半年后需支付承诺的军饷，否则军心大降。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>十一、精兵系统</h2>
            <ul>
                <li>精兵由普通兵训练而来，训练消耗普通兵名额，不额外消耗资源。</li>
                <li>每个精兵每天消耗1面包，若面包不足，则未获得面包的精兵被裁撤，并降低军心威望。</li>
                <li>精兵被裁撤时同样降低军心威望。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>十二、事件与惩罚</h2>
            <ul>
                <li>过度建造（每现实天3.5天检查，超过人口/20的建造数）会降低民心威望。</li>
                <li>过度宣战（每60时间点检查，超过2次）会降低民心军心威望。</li>
                <li>软弱无能（威望<50且无为而治超过1.5年）持续降低威望军心。</li>
                <li>权臣（权利<30时有概率出现）影响国家稳定。</li>
                <li>随机事件：丰收、瘟疫、野兽袭击、发现矿脉等。</li>
            </ul>
        </div>

        <div class="rule-section">
            <h2>十三、操作限制</h2>
            <ul>
                <li>每个时间点内（即每个游戏小时）最多行动次数：人口<500为1，≥500为2。</li>
                <li>每轮（现实天）最多行动次数：基础8次，人口每200增加1次，最多16次。若无任何货币，则降为2次。</li>
            </ul>
        </div>

        <div class="footer">
            <p>—— 国家争霸赛开发组 ——</p>
        </div>
    </div>
</body>
</html>
"""

MOBILE_GAME_HTML = """
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
    <title>国家争霸赛 · 移动版</title>
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        body { font-family: 'Segoe UI', Roboto, sans-serif; background: #0b1a2f; color: #e0e0e0; padding: 10px; padding-bottom: 80px; line-height: 1.4; }
        .container { max-width: 600px; margin: 0 auto; }
        .card { background: rgba(255,255,255,0.05); backdrop-filter: blur(4px); border-radius: 16px; padding: 16px; margin-bottom: 16px; border: 1px solid rgba(255,215,0,0.2); box-shadow: 0 4px 12px rgba(0,0,0,0.5); }
        h2 { font-size: 1.2rem; margin-bottom: 10px; color: #ffd966; border-left: 4px solid #ff9800; padding-left: 10px; }
        .flex-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
        button { background: #2a3f5e; border: none; color: white; padding: 10px 16px; border-radius: 30px; font-size: 0.9rem; font-weight: 500; box-shadow: 0 2px 5px #00000055; border: 1px solid #ff980055; cursor: pointer; touch-action: manipulation; }
        button.primary { background: #ff9800; color: black; font-weight: bold; }
        button.small { padding: 6px 12px; font-size: 0.8rem; }
        button:active { transform: scale(0.97); background: #1e2f44; }
        .stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; text-align: center; margin: 12px 0; }
        .stat-item { background: #162b3a; padding: 8px 4px; border-radius: 12px; border: 1px solid #3a5a7a; }
        .stat-label { font-size: 0.7rem; color: #aaa; }
        .stat-value { font-size: 1.1rem; font-weight: bold; color: #ffd966; }
        .resources { display: flex; flex-wrap: wrap; gap: 5px 10px; max-height: 120px; overflow-y: auto; background: #0f1e2c; padding: 8px; border-radius: 12px; margin: 8px 0; font-size: 0.9rem; }
        .resource-badge { background: #1e3f5a; padding: 4px 10px; border-radius: 20px; border: 1px solid #3f7a9a; }
        .ops-list { background: #0f1e2c; border-radius: 12px; padding: 8px; max-height: 150px; overflow-y: auto; font-size: 0.85rem; }
        .op-item { border-bottom: 1px solid #2a4a6a; padding: 5px 0; }
        .tab-bar { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0; background: #112233; border-radius: 40px; padding: 5px; }
        .tab { flex: 1 0 auto; text-align: center; padding: 8px 0; border-radius: 30px; background: transparent; color: #ccc; font-size: 0.8rem; border: none; box-shadow: none; }
        .tab.active { background: #ff9800; color: black; font-weight: bold; }
        .action-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: 8px; margin-top: 10px; }
        .action-btn { background: #1e3f5a; padding: 10px 2px; border-radius: 20px; font-size: 0.75rem; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); display: none; justify-content: center; align-items: center; z-index: 1000; }
        .modal { background: #1e2f3f; width: 90%; max-width: 400px; border-radius: 24px; padding: 20px; border: 2px solid #ff9800; }
        .modal h3 { margin-bottom: 15px; color: #ffd966; }
        .wage-inputs { display: flex; gap: 10px; margin: 15px 0; flex-wrap: wrap; }
        .wage-inputs label { display: flex; flex-direction: column; flex: 1; font-size: 0.8rem; }
        .wage-inputs input { background: #0f1e2c; border: 1px solid #3f7a9a; color: white; padding: 8px; border-radius: 12px; margin-top: 4px; }
        .map-container { margin: 10px 0; text-align: center; background: #102030; border-radius: 20px; padding: 10px; }
        .map-img { max-width: 100%; max-height: 200px; border-radius: 12px; cursor: pointer; }
        .nation-list { max-height: 200px; overflow-y: auto; background: #0f1e2c; border-radius: 12px; padding: 5px; }
        .nation-item { padding: 8px 12px; border-bottom: 1px solid #2a4a6a; display: flex; justify-content: space-between; }
        .nation-item.free { color: #8bc34a; }
        .nation-item.taken { color: #ff8a80; }
        .footer { text-align: center; margin-top: 20px; color: #aaa; font-size: 0.7rem; }
        .country-list { max-height: 300px; overflow-y: auto; margin: 10px 0; }
        .country-item { display: flex; align-items: center; padding: 8px; border-bottom: 1px solid #3a5a7a; }
        .country-item input { margin-right: 10px; }
        .chaos-normal { color: #8bc34a; }
        .chaos-chaotic { color: #ff8a80; }
    </style>
</head>
<body>
    <div class="container">
        <div class="card" id="playerCard">
            <div class="flex-row" style="justify-content: space-between;">
                <span id="playerLabel">未登录</span>
                <span id="nationLabel"></span>
                <button class="small" id="refreshBtn">刷新</button>
            </div>
            <div class="stat-grid">
                <div class="stat-item"><span class="stat-label">民心</span><span class="stat-value" id="morale">0</span></div>
                <div class="stat-item"><span class="stat-label">军心</span><span class="stat-value" id="military">0</span></div>
                <div class="stat-item"><span class="stat-label">威望</span><span class="stat-value" id="prestige">0</span></div>
                <div class="stat-item"><span class="stat-label">权利</span><span class="stat-value" id="power">0</span></div>
            </div>
            <div class="flex-row" style="justify-content: space-between;">
                <span id="gameTime">时间计算中...</span>
                <span id="troops">普通0 精0</span>
            </div>
            <div class="flex-row" style="justify-content: space-between;">
                <span>总兵: <span id="totalTroops">0</span></span>
                <span>有效战力: <span id="effectivePower">0.00</span></span>
                <span id="chaosStatus" class="chaos-normal">正常</span>
            </div>
            <div class="flex-row" style="justify-content: space-between; margin-top:5px;">
                <span>国库:</span>
                <span id="treasury">铜0 铝0 银0</span>
            </div>
            <div class="flex-row" style="justify-content: space-between; margin-top:5px;">
                <span>本轮行动: <span id="actionsRound">0</span> / <span id="maxActionsRound">0</span></span>
                <span>本时间点: <span id="actionsSlot">0</span> / <span id="maxActionsSlot">0</span></span>
            </div>
        </div>

        <div class="card">
            <h2>📦 资源/建筑/工具</h2>
            <div id="resources" class="resources">加载中...</div>
        </div>

        <div class="card">
            <h2>⏳ 操作队列</h2>
            <div id="operations" class="ops-list">无</div>
            <button id="deleteOpBtn" style="margin-top:8px; width:100%;">删除可取消的操作</button>
        </div>

        <div class="card map-container">
            <h2>🗺️ 实时地图</h2>
            <img id="mapImage" class="map-img" src="" alt="地图加载中...">
            <div style="font-size:0.7rem; margin-top:5px;">点击图片查看大图</div>
        </div>

        <div class="card">
            <h2>🌍 国家列表</h2>
            <div id="nationList" class="nation-list">加载中...</div>
            <div class="flex-row" style="margin-top:10px;">
                <input type="text" id="regUsername" placeholder="用户名" style="flex:1; background:#0f1e2c; border:1px solid #ff9800; border-radius:30px; padding:10px; color:white; margin-right:5px;">
                <input type="text" id="nationChoice" placeholder="国家名" style="flex:1; background:#0f1e2c; border:1px solid #ff9800; border-radius:30px; padding:10px; color:white;">
                <button id="chooseNationBtn" class="primary">注册</button>
            </div>
            <div class="flex-row" style="margin-top:10px;">
                <input type="text" id="loginUsername" placeholder="用户名登录" style="flex:1; background:#0f1e2c; border:1px solid #ff9800; border-radius:30px; padding:10px; color:white;">
                <button id="loginBtn" class="primary">登录</button>
            </div>
        </div>

        <div class="card">
            <h2>📢 系统消息</h2>
            <div id="messages" class="ops-list" style="max-height:120px;">无</div>
        </div>

        <div class="card" style="padding:10px;">
            <div class="tab-bar" id="tabBar">
                <button class="tab active" data-tab="collect">采集</button>
                <button class="tab" data-tab="build">建造</button>
                <button class="tab" data-tab="smelt">冶炼</button>
                <button class="tab" data-tab="chemical">化工</button>
                <button class="tab" data-tab="plant">种植</button>
                <button class="tab" data-tab="hunt">狩猎</button>
                <button class="tab" data-tab="morale">民心</button>
                <button class="tab" data-tab="military">战争</button>
                <button class="tab" data-tab="other">其他</button>
            </div>
            <div id="actionPanel" style="min-height:200px;">
                <!-- 动态生成操作按钮 -->
            </div>
        </div>

        <!-- 工资模态框 -->
        <div class="modal-overlay" id="wageModal">
            <div class="modal">
                <h3>💰 支付工资 (总额≥5，每5点倍数+1倍产出)</h3>
                <div class="wage-inputs">
                    <label>铜币 <input type="number" id="wageCu" min="0" value="0"></label>
                    <label>铝币 <input type="number" id="wageAl" min="0" value="0"></label>
                    <label>银币 <input type="number" id="wageAg" min="0" value="0"></label>
                </div>
                <div class="flex-row" style="justify-content: space-around;">
                    <button id="wagePay" class="primary">支付并执行</button>
                    <button id="wageNoPay">不发工资</button>
                    <button id="wageCancel">取消</button>
                </div>
            </div>
        </div>

        <!-- 国家选择模态框（用于备战/宣战/进攻等） -->
        <div class="modal-overlay" id="countrySelectModal">
            <div class="modal">
                <h3 id="countrySelectTitle">选择国家</h3>
                <div id="countryListContainer" class="country-list"></div>
                <div class="flex-row" style="justify-content: space-around;">
                    <button id="countrySelectOk" class="primary">确定</button>
                    <button id="countrySelectCancel">取消</button>
                </div>
            </div>
        </div>

        <!-- 大图查看模态框 -->
        <div class="modal-overlay" id="mapModal" style="background:black;" onclick="this.style.display='none'">
            <img id="mapLarge" style="max-width:100%; max-height:100%; object-fit:contain;" src="">
        </div>
    </div>

    <script>
        // ---------- 配置 ----------
        const API_BASE = '/api/proxy';
        let playerId = localStorage.getItem('playerId') || '';
        let currentNationName = '';
        let gameState = null;
        let nationsList = [];
        let messages = [];
        let mapEventSource = null;
        let stateEventSource = null;
        let currentTab = 'collect';
        let maxCu = 0, maxAl = 0, maxAg = 0;

        // 国家选择回调
        let countrySelectCallback = null;
        let countrySelectMulti = false;

        // 资源中文对照（完全汉化）
        const resourceChinese = {
            "Wood":"木材","Stone":"石头","Clay":"粘土","Coal":"煤炭","Sand":"沙子","Lime":"石灰",
            "IronOre":"铁矿","CopperOre":"铜矿","AluminumOre":"铝矿","SilverOre":"银矿","TungstenOre":"钨矿",
            "Magnetite":"磁铁矿","Cryolite":"冰晶石","Monazite":"独居石","Saltpeter":"硝石","Sulfur":"硫磺",
            "Silicon":"硅","ZincOre":"锌矿","LeadOre":"铅矿","Chromite":"铬矿","MercuryOre":"汞矿",
            "NickelOre":"镍矿","ManganeseOre":"锰矿","Pyrolusite":"软锰矿","Diamond":"金刚石",
            "Chalcanthite":"胆矾","Copperas":"绿矾","Iron":"铁","Copper":"铜","Aluminum":"铝","Silver":"银",
            "Lead":"铅","Chromium":"铬","Nickel":"镍","Mercury":"汞","Tungsten":"钨","Manganese":"锰","Zinc":"锌",
            "SulfuricAcid":"硫酸","NitricAcid":"硝酸","Hydrogen":"氢气","Bromine":"溴","Methane":"甲烷",
            "Bromomethane":"一溴甲烷","Toluene":"甲苯","TNT":"TNT","Linen":"亚麻","SpringWheat":"春小麦",
            "WinterWheat":"冬小麦","Cabbage":"白菜","Flour":"面粉","Dough":"面团","Bread":"面包",
            "LinenCloth":"麻布","Glass":"玻璃","Gunpowder":"黑火药","ExplosivePack":"炸药包",
            "SteamEngine":"蒸汽机","Motor":"电机","WireSwitch":"导线开关","WindWheel":"风轮","Tower":"塔架",
            "WindGenerator":"风力发电机","WoodenBucket":"木桶","IronBucket":"铁桶","WindSock":"风向袋",
            "Hub":"轮毂","WindCup":"风杯","ThermometerScreen":"百叶箱","Condenser":"冷凝管","Thermometer":"温度计",
            "Millstone":"石磨","FlourProduct":"面粉产品","ManganeseDioxide":"二氧化锰","ZincManganeseBattery":"锌锰电池",
            "Drill":"钻头","Accumulator":"蓄电池","StainlessSteel":"不锈钢","Electrolyzer":"电解池",
            "SmallWaterTank":"小储水罐","WaterTank":"储水罐","LargeWaterTank":"大储水罐","Hygrometer":"湿度计",
            "RainGauge":"雨量计","TungstenFilament":"钨丝","Bulb":"灯泡","FermentedDough":"发酵面糊",
            "WoodenBoat":"木船","AdvancedWoodenBoat":"高级木船","SteamAdvancedWoodenBoat":"蒸汽高级木船",
            "MotorAdvancedWoodenBoat":"电机高级木船","BatteryAdvancedWoodenBoat":"蓄电池高级木船",
            "Ironclad":"铁甲舰","SteamIronclad":"蒸汽铁甲舰","MotorIronclad":"电机铁甲舰",
            "BatteryMotorIronclad":"蓄电池电机铁甲舰","DualMotorIronclad":"双电机铁甲舰",
            "DualMotorBatteryIronclad":"双电机蓄电池铁甲舰","WeatherShip":"测风船（木船）",
            "WeatherShipFromSteamAdvanced":"测风船（蒸汽高级）","WeatherShipFromMotorAdvanced":"测风船（电机高级）",
            "WeatherShipFromBatteryAdvanced":"测风船（蓄电池高级）","WeatherShipFromSteamIronclad":"测风船（蒸汽铁甲）",
            "WeatherShipFromMotorIronclad":"测风船（电机铁甲）","WeatherShipFromBatteryMotorIronclad":"测风船（蓄电池电机铁甲）",
            "WeatherShipFromDualMotorIronclad":"测风船（双电机铁甲）","WeatherShipFromDualMotorBatteryIronclad":"测风船（双电机蓄电池铁甲）",
            "SolarPanel":"太阳能板","StoneAxe":"石斧","StonePickaxe":"石镐","IronAxe":"铁斧","IronPickaxe":"铁镐",
            "Saw":"锯","CoinCopper":"铜币","CoinAluminum":"铝币","CoinSilver":"银币","CoinMold":"铸币模具",
            "Pork":"猪肉","Beef":"牛肉","Milk":"牛奶","Leather":"皮革","Population":"人口","IronOxide":"氧化铁",
            "Quicklime":"生石灰","Charcoal":"木炭","Water":"水","Oxygen":"氧气","Seawater":"海水","Benzene":"苯",
            "Meat":"肉","Fish":"鱼","Steel":"钢","Cannon":"火炮","Cannonball":"炮弹"
        };
        function chineseName(eng) { return resourceChinese[eng] || eng; }

        // ---------- 通用请求 ----------
        async function callAPI(action, extra = {}) {
            const payload = { action, playerId: playerId || undefined, ...extra };
            const resp = await fetch(API_BASE, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });
            if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
            const text = await resp.text();
            try { return JSON.parse(text); } catch { return text; }
        }

        async function callGET(action, params = {}) {
            const url = new URL(API_BASE, window.location.origin);
            url.search = new URLSearchParams({ action, playerId, ...params }).toString();
            const resp = await fetch(url.toString());
            if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
            const text = await resp.text();
            try { return JSON.parse(text); } catch { return text; }
        }

        // ---------- 页面初始化 ----------
        window.onload = () => {
            setupTabs();
            loadNations();
            if (playerId) {
                document.getElementById('playerLabel').innerText = `玩家: ${playerId.slice(0,8)}...`;
                refreshState();
                startSSE();
            } else {
                document.getElementById('playerLabel').innerText = '未登录';
            }
            refreshMessages();

            document.getElementById('refreshBtn').addEventListener('click', () => {
                refreshState(); refreshMessages(); loadNations();
            });
            document.getElementById('chooseNationBtn').addEventListener('click', registerFromInput);
            document.getElementById('deleteOpBtn').addEventListener('click', deleteOperation);
            document.getElementById('mapImage').addEventListener('click', () => {
                if (document.getElementById('mapImage').src) {
                    document.getElementById('mapLarge').src = document.getElementById('mapImage').src;
                    document.getElementById('mapModal').style.display = 'flex';
                }
            });

            document.getElementById('wagePay').addEventListener('click', () => closeWageModal('pay'));
            document.getElementById('wageNoPay').addEventListener('click', () => closeWageModal('nopay'));
            document.getElementById('wageCancel').addEventListener('click', () => closeWageModal('cancel'));

            document.getElementById('countrySelectOk').addEventListener('click', () => {
                if (countrySelectCallback) {
                    const selected = [];
                    document.querySelectorAll('#countryListContainer input:checked').forEach(cb => {
                        selected.push(cb.value);
                    });
                    countrySelectCallback(selected);
                }
                document.getElementById('countrySelectModal').style.display = 'none';
            });
            document.getElementById('countrySelectCancel').addEventListener('click', () => {
                document.getElementById('countrySelectModal').style.display = 'none';
            });
        };

        // ---------- 国家选择弹窗 ----------
        function showCountrySelect(title, nations, multi, callback) {
            const container = document.getElementById('countryListContainer');
            let html = '';
            nations.forEach(n => {
                html += `<div class="country-item">
                    <input type="${multi ? 'checkbox' : 'radio'}" name="countrySelect" value="${n.name}" id="country_${n.name}">
                    <label for="country_${n.name}">${n.name} ${n.playerId ? '(已占)' : '(空闲)'}</label>
                </div>`;
            });
            container.innerHTML = html;
            document.getElementById('countrySelectTitle').innerText = title;
            countrySelectCallback = callback;
            countrySelectMulti = multi;
            document.getElementById('countrySelectModal').style.display = 'flex';
        }

        // ---------- SSE 连接 ----------
        function startSSE() {
            if (mapEventSource) mapEventSource.close();
            if (stateEventSource) stateEventSource.close();
            mapEventSource = new EventSource(API_BASE + '?action=map_sse');
            mapEventSource.addEventListener('map', (e) => {
                document.getElementById('mapImage').src = 'data:image/png;base64,' + e.data;
            });
            if (playerId) {
                stateEventSource = new EventSource(API_BASE + `?action=state_sse&playerId=${playerId}`);
                stateEventSource.addEventListener('state', (e) => {
                    try { updateUIFromState(JSON.parse(e.data)); } catch (err) {}
                });
            }
        }

        async function refreshState() {
            if (!playerId) return;
            try {
                const data = await callGET('state');
                if (data.error) { alert('获取状态失败: ' + data.error); return; }
                updateUIFromState(data);
            } catch (e) { console.error(e); }
        }

        function updateUIFromState(data) {
            if (!data || !data.State) return;
            const s = data.State;
            gameState = s;
            currentNationName = data.Name;
            document.getElementById('nationLabel').innerText = '国家: ' + data.Name;
            document.getElementById('gameTime').innerText = '时间: ' + (data.GameDateTime || '');
            document.getElementById('morale').innerText = s.Morale;
            document.getElementById('military').innerText = s.MilitaryMorale;
            document.getElementById('prestige').innerText = s.Prestige;
            document.getElementById('power').innerText = s.Power;
            document.getElementById('troops').innerText = `普通${s.NormalTroops||0} 精${s.EliteTroops||0}`;
            maxCu = s.CoinCopperAmount || 0; maxAl = s.CoinAluminumAmount || 0; maxAg = s.CoinSilverAmount || 0;
            document.getElementById('treasury').innerText = `铜${maxCu} 铝${maxAl} 银${maxAg}`;
            document.getElementById('actionsRound').innerText = s.ActionsUsedThisRound || 0;

            const totalTroops = s.TotalTroops || 0;
            const effectivePower = s.EffectivePower || 0;
            const isChaotic = s.IsChaotic || false;
            document.getElementById('totalTroops').innerText = totalTroops;
            document.getElementById('effectivePower').innerText = effectivePower.toFixed(2);
            const chaosSpan = document.getElementById('chaosStatus');
            chaosSpan.innerText = isChaotic ? '混乱' : '正常';
            chaosSpan.className = isChaotic ? 'chaos-chaotic' : 'chaos-normal';
            document.getElementById('actionsRound').innerText = s.ActionsUsedThisRound || 0;
            document.getElementById('maxActionsRound').innerText = s.MaxActionsPerRound || 0;
            document.getElementById('actionsSlot').innerText = s.TimeSlotActionsUsed || 0;
            document.getElementById('maxActionsSlot').innerText = s.MaxActionsPerTimeSlot || 0;

            let resHtml = '';
            if (s.ResourceList && s.ResourceList.length) {
                s.ResourceList.forEach(r => {
                    if (r.Amount > 0) resHtml += `<span class="resource-badge">${chineseName(r.Type)}:${r.Amount}</span>`;
                });
            }
            if (s.Buildings && s.Buildings.length) {
                resHtml += '<br><strong>建筑:</strong> ' + s.Buildings.map(b => chineseName(b)).join(' ');
            }
            if (s.Tools && Object.keys(s.Tools).length) {
                resHtml += '<br><strong>工具:</strong> ';
                for (let [tool, cnt] of Object.entries(s.Tools)) {
                    resHtml += `${chineseName(tool)}:${cnt} `;
                }
            }
            document.getElementById('resources').innerHTML = resHtml || '无';

            let opsHtml = '';
            if (s.Operations && s.Operations.length) {
                s.Operations.forEach(op => {
                    let remain = (op.StartTime + op.Duration - s.CurrentTimePoints).toFixed(1);
                    let desc = op.Type;
                    if (op.Resource) desc += ' ' + chineseName(op.Resource);
                    else if (op.Building) desc += ' ' + chineseName(op.Building);
                    else if (op.ActionName) desc += ' ' + op.ActionName;
                    opsHtml += `<div class="op-item">${desc} 剩余${remain}点</div>`;
                });
            } else opsHtml = '无';
            document.getElementById('operations').innerHTML = opsHtml;
        }

        async function refreshMessages() {
            try {
                const list = await callGET('message');
                if (Array.isArray(list)) {
                    document.getElementById('messages').innerHTML = list.map(m => `<div>${m}</div>`).join('') || '无';
                }
            } catch (e) {}
        }

        async function loadNations() {
            try {
                const list = await callGET('nations');
                nationsList = list;
                const container = document.getElementById('nationList');
                container.innerHTML = '';
                list.forEach(n => {
                    const div = document.createElement('div');
                    div.className = `nation-item ${n.playerId ? 'taken' : 'free'}`;
                    div.innerHTML = `<span>${n.name}</span><span>${n.playerId ? (n.playerId===playerId?'我':'已占') : '空闲'}</span>`;
                    div.addEventListener('dblclick', () => {
                        if (!n.playerId) registerToNation(n.name);
                        else alert('该国家已被占用');
                    });
                    container.appendChild(div);
                });
            } catch (e) {}
        }

        async function registerToNation(nation) {
            try {
                const res = await callAPI('register', { nation });
                if (res.error) { alert('注册失败: ' + res.error); return; }
                playerId = res.playerId;
                localStorage.setItem('playerId', playerId);
                document.getElementById('playerLabel').innerText = '玩家: ' + playerId.slice(0,8) + '...';
                startSSE();
                refreshState();
                loadNations();
            } catch (e) { alert('注册出错'); }
        }

        function registerFromInput() {
            const nation = document.getElementById('nationChoice').value.trim();
            if (!nation) return;
            registerToNation(nation);
        }

        // ---------- 操作选项卡 ----------
        function setupTabs() {
            const tabs = document.querySelectorAll('.tab');
            tabs.forEach(t => {
                t.addEventListener('click', () => {
                    document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
                    t.classList.add('active');
                    currentTab = t.dataset.tab;
                    renderActionPanel();
                });
            });
            renderActionPanel();
        }

        const actionDefs = {
            collect: ['Wood','Stone','Clay','Coal','Sand','Lime','IronOre','CopperOre','AluminumOre','SilverOre','TungstenOre','Magnetite','Cryolite','Monazite','Saltpeter','Sulfur','Silicon','ZincOre','LeadOre','Chromite','MercuryOre','NickelOre','ManganeseOre','Pyrolusite','Diamond','Chalcanthite','Copperas'],
            build: ['StoneAxe','StonePickaxe','IronAxe','IronPickaxe','Saw','Drill','WoodenBoat','AdvancedWoodenBoat','SteamAdvancedWoodenBoat','MotorAdvancedWoodenBoat','BatteryAdvancedWoodenBoat','Ironclad','SteamIronclad','MotorIronclad','BatteryMotorIronclad','DualMotorIronclad','DualMotorBatteryIronclad','WeatherShip','Kiln','BlastFurnace','Smelter','Loom','WoodenHouse','StoneWoodHouse','SteamEngine','Motor','WireSwitch','WindWheel','Tower','WindGenerator','Gunpowder','WoodenBucket','IronBucket','LinenCloth','WindSock','Hub','WindCup','ThermometerScreen','Condenser','Glass','Thermometer','Millstone','FlourProduct','ExplosivePack','SolarPanel','ManganeseDioxide','ZincManganeseBattery','Accumulator','StainlessSteel','Electrolyzer','SmallWaterTank','WaterTank','LargeWaterTank','Hygrometer','RainGauge','TungstenFilament','Bulb','Bread','Lumbermill1','Quarry1','Lumbermill2','Quarry2','ChemicalPlant1','WeatherStationWind','WeatherStationTempHum','CoinMold','Sawmill','StoneCutter','Foundry','RollingMill'],
            smelt: ['IronOre','CopperOre','AluminumOre','SilverOre','LeadOre','Chromite','NickelOre','MercuryOre','TungstenOre','ManganeseOre','ZincOre'],
            chemical: ['硫酸','硝酸','氢气','溴','甲烷','一溴甲烷','甲苯','TNT'],
            plant: ['SpringWheat','WinterWheat','Linen','Cabbage'],
            hunt: [{ name:'猎野猪', param:'野猪' }, { name:'猎野牛', param:'野牛' }, { name:'驯狼', param:'狼' }, { name:'驯野猫', param:'野猫' }],
            morale: ['娱乐','演练','监督','清扫','团结军民','阅兵仪式','访问民间','大赦天下','取缔权臣','开国大典'],
            military: [
                { name:'征兵', action:'recruit', input:true },
                { name:'备战', action:'prepareWar', multi:true },
                { name:'宣战', action:'declareWar', multi:true },
                { name:'普通进攻', action:'attack_normal', single:true },
                { name:'轰炸', action:'attack_bomb', bomb:true },
                { name:'发射火炮', action:'fire_cannon', single:true },
                { name:'造火炮', action:'manufacture', param:'Cannon' },
                { name:'造炮弹', action:'manufacture', param:'Cannonball' },
                { name:'裁兵', action:'reduce_troops', input:true },
                { name:'养精兵', action:'train_elite', input:true },
                { name:'撤销精兵', action:'cancel_elite', input:true }
            ],
            other: [
                { name:'铸铜币', action:'castcoin', param:'CoinCopper' },
                { name:'铸铝币', action:'castcoin', param:'CoinAluminum' },
                { name:'铸银币', action:'castcoin', param:'CoinSilver' },
                { name:'停工', action:'shutdown', param:'all' },
                { name:'复工', action:'resume', param:'all' },
                { name:'收税', action:'tax' }
            ]
        };

        function renderActionPanel() {
            const panel = document.getElementById('actionPanel');
            const defs = actionDefs[currentTab];
            if (!defs) return;
            let html = '<div class="action-grid">';
            defs.forEach(item => {
                if (typeof item === 'string') {
                    let actionType = currentTab;
                    if (currentTab === 'collect') actionType = 'collect';
                    else if (currentTab === 'build') actionType = 'build';
                    else if (currentTab === 'smelt') actionType = 'smelt';
                    else if (currentTab === 'chemical') actionType = 'chemical';
                    else if (currentTab === 'plant') actionType = 'plant';
                    else if (currentTab === 'hunt') actionType = 'hunt';
                    else if (currentTab === 'morale') actionType = 'morale';
                    html += `<button class="action-btn" data-action="${actionType}" data-param="${item}">${chineseName(item)}</button>`;
                } else {
                    let action = item.action || currentTab;
                    let param = item.param || item.name;
                    let name = item.name || param;
                    html += `<button class="action-btn" data-action="${action}" data-param="${param}" ${item.multi?'data-multi=true':''} ${item.single?'data-single=true':''} ${item.bomb?'data-bomb=true':''} ${item.input?'data-input=true':''}>${name}</button>`;
                }
            });
            html += '</div>';
            panel.innerHTML = html;

            document.querySelectorAll('#actionPanel .action-btn').forEach(btn => {
                btn.addEventListener('click', () => {
                    let action = btn.dataset.action;
                    let param = btn.dataset.param;

                    if (btn.dataset.multi) {
                        showCountrySelect('选择备战/宣战国家（可多选）', nationsList, true, (selected) => {
                            if (selected.length === 0) return;
                            param = selected.join(',');
                            executeAfterCountry(action, param);
                        });
                        return;
                    } else if (btn.dataset.single) {
                        if (!gameState || !gameState.DeclaredNations || !gameState.DeclaredNations.length) {
                            alert('没有已宣战的国家');
                            return;
                        }
                        const declaredNations = gameState.DeclaredNations.map(name => ({ name, playerId: '已占' }));
                        showCountrySelect('选择进攻国家', declaredNations, false, (selected) => {
                            if (selected.length === 0) return;
                            param = selected[0];
                            executeAfterCountry(action, param);
                        });
                        return;
                    } else if (btn.dataset.bomb) {
                        if (!gameState || !gameState.DeclaredNations || !gameState.DeclaredNations.length) {
                            alert('没有已宣战的国家');
                            return;
                        }
                        const declaredNations = gameState.DeclaredNations.map(name => ({ name, playerId: '已占' }));
                        showCountrySelect('选择轰炸目标', declaredNations, false, (selected) => {
                            if (selected.length === 0) return;
                            let target = selected[0];
                            let type = prompt('轰炸方式: explosive 或 tnt', 'explosive');
                            if (type !== 'explosive' && type !== 'tnt') type = 'explosive';
                            param = target + '|' + type;
                            executeAfterCountry(action, param);
                        });
                        return;
                    } else if (btn.dataset.input) {
                        let count = prompt('输入数量:', '1');
                        if (!count || isNaN(count) || parseInt(count)<=0) return;
                        param = count;
                    }

                    if (action === 'tax') {
                        alert('请在电脑端设置税率');
                        return;
                    }
                    // 判断是否需要工资
                    const noWageActions = ['prepareWar','declareWar','attack_normal','attack_bomb','fire_cannon','reduce_troops','train_elite','cancel_elite','recruit','shutdown','resume','tax','manufacture'];
                    if (noWageActions.includes(action)) {
                        executeAction(action, param, 0, 0, 0);
                    } else {
                        showWageModal(action, param);
                    }
                });
            });
        }

        function executeAfterCountry(action, param) {
            const noWageActions = ['prepareWar','declareWar','attack_normal','attack_bomb','fire_cannon'];
            if (noWageActions.includes(action)) {
                executeAction(action, param, 0, 0, 0);
            } else {
                showWageModal(action, param);
            }
        }

        let pendingAction = null, pendingParam = null;
        function showWageModal(action, param) {
            pendingAction = action;
            pendingParam = param;
            document.getElementById('wageCu').value = 0;
            document.getElementById('wageAl').value = 0;
            document.getElementById('wageAg').value = 0;
            document.getElementById('wageModal').style.display = 'flex';
        }
        function closeWageModal(choice) {
            document.getElementById('wageModal').style.display = 'none';
            if (choice === 'cancel' || !pendingAction) return;
            let cu = parseInt(document.getElementById('wageCu').value) || 0;
            let al = parseInt(document.getElementById('wageAl').value) || 0;
            let ag = parseInt(document.getElementById('wageAg').value) || 0;
            if (choice === 'pay' && cu+al+ag < 5) {
                alert('支付总额至少为5');
                return;
            }
            if (choice === 'pay') {
                executeAction(pendingAction, pendingParam, cu, al, ag);
            } else {
                executeAction(pendingAction, pendingParam, 0, 0, 0);
            }
            pendingAction = null;
        }

        async function executeAction(actionType, param, wageCu, wageAl, wageAg) {
            if (!playerId) { alert('请先注册'); return; }
            if (!confirm(`执行操作: ${actionType} ${param}\\n工资: 铜${wageCu} 铝${wageAl} 银${wageAg}\\n确定吗？`)) return;
            try {
                const res = await callAPI('action', {
                    actionType,
                    params: {
                        type: actionType,
                        param: param,
                        wage: wageCu + wageAl + wageAg,
                        currency: 'CoinCopper',
                        wageCopper: wageCu,
                        wageAluminum: wageAl,
                        wageSilver: wageAg
                    }
                });
                if (res.success) refreshState();
                else alert('操作失败: ' + (res.error || '未知错误'));
            } catch (e) { alert('请求失败'); }
        }

        async function deleteOperation() {
            if (!gameState || !gameState.Operations) return;
            let now = gameState.CurrentTimePoints;
            let deletable = [];
            gameState.Operations.forEach((op, idx) => {
                if (Math.abs(op.StartTime - now) < 0.001) deletable.push({idx, desc: op.Type + (op.Resource||op.Building||op.ActionName||'')});
            });
            if (deletable.length === 0) { alert('没有可删除的操作'); return; }
            let msg = '选择要删除的操作:\\n';
            deletable.forEach((d,i) => msg += `${i}: ${d.desc}\\n`);
            let choice = prompt(msg, '0');
            if (choice === null) return;
            let idx = parseInt(choice);
            if (isNaN(idx) || idx<0 || idx>=deletable.length) return;
            try {
                const res = await callAPI('delete', { params: { index: deletable[idx].idx } });
                if (res.success) refreshState();
                else alert('删除失败: ' + (res.error||''));
            } catch (e) {}
        }

        setInterval(refreshMessages, 10000);
        setInterval(loadNations, 30000);
    </script>
    <div class="footer">国家争霸赛 · 移动版</div>
</body>
</html>
"""

@app.route('/')
def index():
    user_agent = request.headers.get('User-Agent', '')
    force_pc = request.args.get('force_pc') == '1'
    if not force_pc and is_mobile_device(user_agent):
        return redirect('/mobile_rules')
    return render_pc_index()

def render_pc_index():
    # 生成资源表格
    resource_rows = ""
    sorted_items = sorted(RESOURCE_MAP.items(), key=lambda x: x[1])
    for eng, chn in sorted_items:
        resource_rows += f"<tr><td>{chn}</td><td>{eng}</td></tr>"

    html = f"""<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>国家争霸赛 联机版</title>
    <style>
        body {{ font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #1e3c72, #2a5298); color: white; padding: 20px; }}
        .container {{ max-width: 1200px; margin: auto; background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); border-radius: 20px; padding: 30px; }}
        h1, h2, h3 {{ color: #ffd700; }}
        h2 {{ border-bottom: 2px solid #ff9800; padding-bottom: 5px; }}
        table {{ border-collapse: collapse; width: 100%; margin: 15px 0; background: rgba(255,255,255,0.2); color: white; }}
        th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
        th {{ background-color: #ff9800; color: black; }}
        .download-section {{ text-align: center; margin-bottom: 30px; }}
        .download-btn {{ display: inline-block; margin: 10px; padding: 15px 30px; background: #ff9800; color: white; text-decoration: none; border-radius: 50px; font-weight: bold; }}
        .download-btn:hover {{ background: #ff5722; }}
        .note {{ background-color: #e7f3fe; color: #333; border-left: 6px solid #2196F3; padding: 10px; margin: 15px 0; }}
        ul {{ columns: 2; -webkit-columns: 2; -moz-columns: 2; }}
        .footer {{ margin-top: 30px; font-size: 0.9em; text-align: center; }}
        .mobile-link {{ text-align: center; margin-bottom: 20px; }}
        .mobile-link a {{ color: #ffd700; font-size: 1.2em; }}
    </style>
</head>
<body>
    <div class="container">
        <div class="mobile-link">
            <a href="/mobile_rules">📱 手机版入口（点击进入手机版规则及游戏）</a>
        </div>
        <h1 style="text-align:center;">⚔️ 国家争霸赛 联机版 ⚔️</h1>

        <div class="download-section">
            <p>点击下方按钮下载游戏文件</p>
            <a href="/download/GameServer.exe" class="download-btn">📥 服务器端</a>
            <a href="/download/GameClient.exe" class="download-btn">📥 客户端</a>
            <a href="/download/map.png" class="download-btn">🗺️ 地图文件</a>
            <a href="/download/GameServer.cs" class="download-btn">📄 服务器源代码</a>
            <a href="/download/GameClient.cs" class="download-btn">📄 客户端源代码</a>
            <a href="/download/app.py" class="download-btn">🐍 代理源代码</a>
        </div>

        <div class="note">
            <strong>客户端设置：</strong> 代理地址请填写 <code>{request.host_url}api/proxy</code>
        </div>

        <h2>📜 游戏规则大全</h2>

        <h3>一、游戏时间</h3>
        <ul>
            <li>时间点系统：每现实天对应32时间点，每游戏日4时间点（每6小时一个时间点）。</li>
            <li>游戏日期：从元年1月1日0时开始，每4时间点增加一天。</li>
            <li>季节：春3~5月、夏6~8月、秋9~11月、冬12~次年2月。部分种植受季节限制。</li>
        </ul>

        <h3>二、国家属性</h3>
        <ul>
            <li><strong>民心</strong>：影响人口增长和税收，低民心可能导致叛乱。</li>
            <li><strong>军心</strong>：影响军队士气和战斗力。</li>
            <li><strong>威望</strong>：影响外交和事件。</li>
            <li><strong>权利</strong>：影响政策执行和权臣出现。</li>
            <li><strong>人口</strong>：影响最大行动次数和建筑条件。最大人口由住房决定。</li>
        </ul>

        <h3>三、资源、建筑、工具总览</h3>
        <p>以下列出了游戏中所有可通过操作获得的物品（包括资源、建筑、工具），中文名对应客户端按钮显示，英文名为代码中的标识：</p>
        <table>
            <tr><th>中文名</th><th>英文标识</th></tr>
            {resource_rows}
        </table>

        <h3>四、工具与采集</h3>
        <ul>
            <li>工具类型：石斧、石镐、铁斧、铁镐、锯、钻头等。工具可减少对应资源的采集时间。</li>
            <li>采集基础时间：木材/石头20，粘土/沙子16，铁矿/铜矿24，钨矿/金刚石40，其他参见客户端提示。</li>
            <li>工具减时：石制工具-4，铁制工具-8，锯/钻头-12。</li>
            <li>每次采集固定产出20单位资源。</li>
        </ul>

        <h3>五、操作类型</h3>
        <p>所有操作均可附加工资（铜币/铝币/银币），工资总额每5点使产出倍数+1（不足5按1倍计算）。不发工资则无加成。</p>
        <ul>
            <li><strong>采集</strong>：基础耗时20-40，受工具影响，产出20资源。</li>
            <li><strong>冶炼</strong>：需要熔炉、煤炭、石灰/冰晶石，耗时固定20时间点（无论数量），产出金属。</li>
            <li><strong>建造</strong>：消耗资源，获得建筑或工具，耗时1-24，具体成本见客户端按钮提示。</li>
            <li><strong>化工</strong>：需要特定建筑和原料，产出化工产品（硫酸、硝酸、TNT等）。</li>
            <li><strong>种植</strong>：需要种子和合适季节，耗时320-880，收获20作物。</li>
            <li><strong>狩猎</strong>：需要斧头，耗时40，获得肉和皮革（野猪得猪肉，野牛得牛肉+皮革）。</li>
            <li><strong>驯化</strong>：需要食物，耗时20，获得动物（狼变狗，野猫变家猫）。</li>
            <li><strong>民心行动</strong>：耗时4，提升民心/军心/威望等，具体效果见按钮描述。</li>
            <li><strong>铸币</strong>：需要铸币模具，消耗1金属产出5硬币，耗时1。</li>
            <li><strong>备战/宣战</strong>：耗时8，标记目标国家。</li>
            <li><strong>进攻</strong>：对宣战国发动普通进攻（耗时8，每轮一次，造成15伤害）。</li>
            <li><strong>轰炸</strong>：消耗2炸药包，造成30伤害，耗时8。</li>
            <li><strong>火炮</strong>：消耗1火炮、1炮弹、1黑火药，造成40伤害，耗时8。</li>
            <li><strong>制造</strong>：制造火炮（消耗20钢，耗时16）或炮弹（消耗1钢，耗时16）。</li>
            <li><strong>裁兵</strong>：裁撤士兵（先普通后精兵），精兵被裁降低军心威望。</li>
            <li><strong>训练精兵</strong>：将普通兵转为精兵，精兵每天消耗1面包。</li>
            <li><strong>撤销精兵</strong>：精兵降为普通兵。</li>
            <li><strong>停工/复工</strong>：停止或恢复自动生产建筑的运作。</li>
            <li><strong>征兵</strong>：招募新兵，耗时28+（每超过100人后每50人多4时间点），征兵后总兵不得超过人口的四分之三。若总兵超过“维持不混乱上限（100+2*精兵数）”，则军队陷入混乱，战斗力衰减。</li>
        </ul>

        <h3>六、军队混乱与战斗力</h3>
        <ul>
            <li>维持不混乱上限 U = 100 + 2 × 精兵数。</li>
            <li>若总兵数 ≤ U，有效战斗力 = 普通兵数 + 2 × 精兵数。</li>
            <li>若总兵数 > U，有效战斗力 = U × 3/4 + 分段衰减：超出部分每100人依次按1/8、1/16、1/32...计算。</li>
            <li>示例：总兵350，无精兵，U=100，有效战力 = 100×3/4 + 100/8 + 100/16 + 50/32 = 95.3125。</li>
            <li>精兵一个顶两个普通兵。</li>
        </ul>

        <h3>七、建筑列表（部分）</h3>
        <ul>
            <li>工具类：石斧、石镐、铁斧、铁镐、锯、钻头</li>
            <li>船类：木船、高级木船（及多种动力型）、铁甲舰（及多种动力型）、测风船（多种母船型）</li>
            <li>生产建筑：窑、高炉、熔炉、织布机、伐木场(1/2级)、采石场(1/2级)、锯木厂、切石机、铸造厂、轧钢厂</li>
            <li>发电：蒸汽机、电机、风力发电机、太阳能板</li>
            <li>民生：木房、木石房</li>
            <li>气象：百叶箱、风向袋、风杯、温度计、湿度计、雨量计、测风站等</li>
            <li>军事：火炮（资源）、炮弹（资源）</li>
            <li>特殊：铸币模具</li>
        </ul>

        <h3>八、自动生产建筑</h3>
        <ul>
            <li>伐木场、采石场、锯木厂、切石机、铸造厂、轧钢厂等会每4时间点自动产出对应资源。</li>
            <li>可以手动停工（停止产出，也不发工资）或复工。</li>
            <li>每月15日自动支付工资（每个建筑基础30货币），若国库不足则民心威望大幅下降。</li>
        </ul>

        <h3>九、税收与公司</h3>
        <ul>
            <li>可设置公司税和个人所得税，支持线性或累进税率。</li>
            <li>公司每月产生利润，每年1月1日分红，政府收取公司税，个人收取红利税。</li>
            <li>个人所得税可选择是否对收入（每月民间货币增量）征税。</li>
        </ul>

        <h3>十、战争机制</h3>
        <ul>
            <li>必须先备战，再宣战，才能发动进攻。</li>
            <li>每现实天只能发动一次进攻（包括普通、轰炸、火炮）。</li>
            <li>每次进攻增加对方投降进度，达到100时对方投降（当前版本仅移除敌对方）。</li>
            <li>宣战半年后需支付承诺的军饷，否则军心大降。</li>
        </ul>

        <h3>十一、精兵系统</h3>
        <ul>
            <li>精兵由普通兵训练而来，训练消耗普通兵名额，不额外消耗资源。</li>
            <li>每个精兵每天消耗1面包，若面包不足，则未获得面包的精兵被裁撤，并降低军心威望。</li>
            <li>精兵被裁撤时同样降低军心威望。</li>
        </ul>

        <h3>十二、事件与惩罚</h3>
        <ul>
            <li>过度建造（每现实天3.5天检查，超过人口/20的建造数）会降低民心威望。</li>
            <li>过度宣战（每60时间点检查，超过2次）会降低民心军心威望。</li>
            <li>软弱无能（威望<50且无为而治超过1.5年）持续降低威望军心。</li>
            <li>权臣（权利<30时有概率出现）影响国家稳定。</li>
            <li>随机事件：丰收、瘟疫、野兽袭击、发现矿脉等。</li>
        </ul>

        <h3>十三、操作限制</h3>
        <ul>
            <li>每个时间点内（即每个游戏小时）最多行动次数：人口<500为1，≥500为2。</li>
            <li>每轮（现实天）最多行动次数：基础8次，人口每200增加1次，最多16次。若无任何货币，则降为2次。</li>
        </ul>

        <div class="note">
            <p>⚠️ 以上规则基于代码整理，实际游戏时请以客户端显示为准。如有疑问，可查看客户端源代码或联系管理员。</p>
        </div>

        <div class="footer">
            —— 国家争霸赛开发组 ——
        </div>
    </div>
</body>
</html>"""
    return html

@app.route('/mobile_rules')
def mobile_rules():
    return render_template_string(MOBILE_RULES_HTML)

@app.route('/play')
def mobile_play():
    return render_template_string(MOBILE_GAME_HTML)

@app.route('/download/<filename>')
def download_file(filename):
    file_path = os.path.join(DOWNLOAD_DIR, filename)
    if os.path.exists(file_path):
        if filename.lower().endswith('.html'):
            return send_from_directory(DOWNLOAD_DIR, filename, as_attachment=False, mimetype='text/html')
        else:
            return send_from_directory(DOWNLOAD_DIR, filename, as_attachment=True)
    else:
        abort(404, description="文件不存在")

@app.route('/api/proxy', methods=['POST', 'GET'])
def proxy():
    if request.method == 'POST':
        data = request.get_json(silent=True) or {}
    else:
        data = request.args.to_dict()

    action = data.get('action')
    username = data.get('currentUsername') or data.get('username') or data.get('playerId')

    try:
        if action == 'map_sse':
            resp = requests.get(GAME_SERVER_URL + 'map-sse', stream=True, timeout=None)
            return Response(
                stream_with_context(resp.iter_content(chunk_size=1024)),
                status=resp.status_code,
                content_type=resp.headers.get('Content-Type', 'text/event-stream')
            )
        elif action == 'state_sse':
            if not username:
                return jsonify({'error': '缺少用户名'}), 400
            resp = requests.get(
                GAME_SERVER_URL + f'state-sse?username={username}',
                stream=True, timeout=None
            )
            return Response(
                stream_with_context(resp.iter_content(chunk_size=1024)),
                status=resp.status_code,
                content_type=resp.headers.get('Content-Type', 'text/event-stream')
            )
        elif action == 'map_image':
            resp = requests.get(GAME_SERVER_URL + 'map-image', stream=True)
            return Response(
                stream_with_context(resp.iter_content(chunk_size=1024)),
                status=resp.status_code,
                content_type=resp.headers.get('Content-Type', 'image/png')
            )
        else:
            # 处理普通请求（需要根据 action 构建不同的 URL 和 payload）
            if action == 'register':
                target_url = GAME_SERVER_URL + 'register'
                payload = {
                    'nation': data.get('nation'),
                    'username': data.get('username') or data.get('playerId')
                }
                resp = requests.post(target_url, json=payload, timeout=10)
            elif action == 'login':
                target_url = GAME_SERVER_URL + 'login'
                payload = {
                    'username': data.get('username') or data.get('playerId')
                }
                resp = requests.post(target_url, json=payload, timeout=10)
            elif action == 'nations':
                target_url = GAME_SERVER_URL + 'nations'
                resp = requests.get(target_url, timeout=10)
            elif action == 'state':
                if not username:
                    return jsonify({'error': '缺少用户名'}), 400
                target_url = GAME_SERVER_URL + f'state?username={username}'
                resp = requests.get(target_url, timeout=10)
            elif action == 'action':
                if not username:
                    return jsonify({'error': '缺少用户名'}), 400
                params = data.get('params', {})
                target_url = GAME_SERVER_URL + 'action'
                payload = {
                    'username': username,
                    'actionType': params.get('type'),
                    'param': params.get('param', ''),
                    'wage': params.get('wage', 0),
                    'currency': params.get('currency', 'CoinCopper'),
                    'wageCopper': params.get('wageCopper', 0),
                    'wageAluminum': params.get('wageAluminum', 0),
                    'wageSilver': params.get('wageSilver', 0)
                }
                resp = requests.post(target_url, json=payload, timeout=10)
            elif action == 'delete':
                if not username:
                    return jsonify({'error': '缺少用户名'}), 400
                params = data.get('params', {})
                target_url = GAME_SERVER_URL + 'delete'
                payload = {
                    'username': username,
                    'params': {'index': params.get('index')}
                }
                resp = requests.post(target_url, json=payload, timeout=10)
            elif action == 'message':
                target_url = GAME_SERVER_URL + 'message'
                resp = requests.get(target_url, timeout=10)
            else:
                return jsonify({'error': '未知操作'}), 400

            # 对 JSON 响应进行字段映射（供前端使用）
            content_type = resp.headers.get('Content-Type', '')
            if 'application/json' in content_type:
                response_data = resp.json()
                # 注册返回时，将 username 映射为 playerId
                if action == 'register' and 'username' in response_data and 'error' not in response_data:
                    response_data['playerId'] = response_data['username']
                # 国家列表返回时，同时添加 playerId 和 currentUsername 字段
                elif action == 'nations' and isinstance(response_data, list):
                    for nation in response_data:
                        if 'playerUsername' in nation:
                            nation['playerId'] = nation['playerUsername']
                            nation['currentUsername'] = nation['playerUsername']  # 为电脑客户端添加
                modified_response = json.dumps(response_data)
                return Response(modified_response, status=resp.status_code, content_type='application/json')
            else:
                return Response(resp.content, status=resp.status_code, content_type=content_type)

    except requests.exceptions.Timeout:
        return jsonify({'error': '后端服务器超时'}), 504
    except requests.exceptions.ConnectionError:
        return jsonify({'error': '无法连接到后端服务器，请确保其已启动'}), 502
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)