🎮ArcadeLab

重生之我解锁了德州登神系统

by RocketPanda33
1647 lines62.9 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>重生之我解锁了德州登神系统</title>
    <style>
        /* ===== 全局样式 ===== */
        * { box-sizing: border-box; user-select: none; }
        body {
            font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
            background: #1a1a2e;
            color: #eee;
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 12px;
        }
        .game-container {
            max-width: 580px;
            width: 100%;
            background: linear-gradient(145deg, #16213e, #0f3460);
            border-radius: 36px;
            padding: 16px 16px 30px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.6);
            border: 1px solid #2a3a6a;
        }
        .game-title {
            text-align: center;
            font-size: 20px;
            font-weight: 700;
            color: #f7d44a;
            text-shadow: 0 0 20px #f7d44a44;
            margin-bottom: 6px;
            letter-spacing: 1px;
        }
        .tab-nav {
            display: flex;
            gap: 6px;
            margin-bottom: 14px;
            border-bottom: 1px solid #2a3a6a;
            padding-bottom: 8px;
        }
        .tab-nav .tab-btn {
            flex: 1;
            background: transparent;
            border: none;
            color: #99aabb;
            padding: 8px 0;
            font-size: 15px;
            font-weight: 600;
            cursor: pointer;
            transition: 0.2s;
            border-radius: 30px;
            background: #1a2a4a;
        }
        .tab-nav .tab-btn.active {
            background: #2a5a8a;
            color: white;
            box-shadow: 0 0 12px #2a5a8a88;
        }
        .tab-nav .tab-btn:hover { background: #2a4a6a; }
        .page {
            display: none;
        }
        .page.active {
            display: block;
        }

        /* ===== 共用组件 ===== */
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 0 6px 12px;
            border-bottom: 1px solid #3a4a7a;
            flex-wrap: wrap;
            gap: 8px;
        }
        .gold-display {
            font-size: 24px;
            font-weight: bold;
            color: #f7d44a;
            text-shadow: 0 0 10px #f7d44a55;
        }
        .controls-header {
            display: flex;
            gap: 10px;
            align-items: center;
        }
        .btn {
            background: #2a3a6a;
            border: none;
            color: white;
            padding: 8px 16px;
            border-radius: 30px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: 0.2s;
            box-shadow: 0 4px 0 #0f1a3a;
            touch-action: manipulation;
        }
        .btn:active { transform: translateY(3px); box-shadow: 0 1px 0 #0f1a3a; }
        .btn-primary { background: #e94560; box-shadow: 0 4px 0 #8a1a2a; }
        .btn-success { background: #2ecc71; box-shadow: 0 4px 0 #1a8a4a; }
        .btn-gold { background: #f39c12; box-shadow: 0 4px 0 #a76f0a; color: #1a1a2e; }
        .btn:disabled { opacity: 0.5; transform: translateY(3px); box-shadow: 0 1px 0 #0f1a3a; pointer-events: none; }

        .hand-area {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 6px 12px;
            background: #0a1a2a;
            border-radius: 16px;
            margin-bottom: 10px;
            font-size: 14px;
            color: #aab;
        }
        .hand-area .hand-label { font-weight: 600; color: #f7d44a; }
        .hand-cards { display: flex; gap: 8px; }
        .hand-cards .hand-cell {
            width: 48px; height: 64px; background: #1e2f4a; border-radius: 8px;
            display: flex; flex-direction: column; align-items: center; justify-content: center;
            font-weight: 700; font-size: 18px; color: #fff;
            box-shadow: inset 0 -3px 0 #0a1a2a; text-shadow: 0 1px 2px black;
        }
        .hand-cards .hand-cell .rank { font-size: 20px; line-height: 1; }
        .hand-cards .hand-cell .suit { font-size: 16px; line-height: 1; }
        .hand-cards .hand-cell .suit-red { color: #e74c3c; }
        .hand-cards .hand-cell .suit-black { color: #bdc3c7; }
        .hand-cards .hand-cell .suit-joker { color: #f1c40f; }

        .grid-wrapper {
            margin: 8px 0; background: #0a1a2a; border-radius: 20px; padding: 10px;
            box-shadow: inset 0 0 20px #00000066; display: flex; justify-content: center;
        }
        .grid {
            display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px;
            width: 100%; max-width: 360px; aspect-ratio: 1 / 1;
        }
        .cell {
            background: #1e2f4a; border-radius: 10px; display: flex; flex-direction: column;
            align-items: center; justify-content: center; font-size: 18px; font-weight: 700;
            color: #fff; box-shadow: inset 0 -3px 0 #0a1a2a; aspect-ratio: 1 / 1;
            padding: 2px; text-shadow: 0 1px 2px black; transition: background 0.1s;
        }
        .cell .rank { font-size: 22px; line-height: 1; }
        .cell .suit { font-size: 18px; line-height: 1; }
        .suit-red { color: #e74c3c; }
        .suit-black { color: #bdc3c7; }
        .suit-joker { color: #f1c40f; }

        .score-detail {
            margin: 8px 0; background: #0a1a2a; border-radius: 16px; padding: 8px 10px;
            max-height: 150px; overflow-y: auto; border: 1px solid #2a3a6a; font-size: 12px;
        }
        .score-detail .line-item {
            display: flex; justify-content: space-between; padding: 2px 4px;
            border-bottom: 1px solid #1a2a4a;
        }
        .score-detail .line-item:last-child { border-bottom: none; }
        .score-detail .line-name { color: #aab; }
        .score-detail .line-score { color: #f7d44a; font-weight: 600; }

        .info-bar {
            display: flex; justify-content: space-between; align-items: center;
            padding: 8px 4px; font-size: 14px; flex-wrap: wrap; gap: 6px;
        }
        .score-display { font-size: 20px; font-weight: bold; color: #2ecc71; }
        .action-area {
            display: flex; gap: 12px; justify-content: center; margin: 12px 0 8px;
            flex-wrap: wrap;
        }
        .talent-section {
            margin-top: 20px; background: #0a1a2a; border-radius: 20px; padding: 12px 10px;
            border: 1px solid #2a3a6a;
        }
        .talent-tabs {
            display: flex; gap: 6px; margin-bottom: 10px; flex-wrap: wrap;
        }
        .tab-btn-sub {
            flex: 1; min-width: 60px; background: #1a2a4a; border: none; color: #aab;
            padding: 6px 0; border-radius: 30px; font-size: 12px; font-weight: 600;
            cursor: pointer; transition: 0.2s;
        }
        .tab-btn-sub.active { background: #2a5a8a; color: white; box-shadow: 0 0 10px #2a5a8a88; }
        .talent-panel { display: none; flex-direction: column; gap: 6px; }
        .talent-panel.active { display: flex; }
        .talent-item {
            display: flex; justify-content: space-between; align-items: center;
            background: #121e32; padding: 6px 12px; border-radius: 30px;
            border-left: 4px solid #f39c12; font-size: 13px;
        }
        .talent-item .info { display: flex; flex-direction: column; flex:1; }
        .talent-item .name { font-weight: 600; font-size: 14px; }
        .talent-item .desc { font-size: 10px; color: #99aabb; }
        .talent-item .level { font-size: 12px; color: #f7d44a; margin-right: 8px; }
        .talent-item .cost { font-size: 12px; color: #e67e22; margin-right: 6px; }
        .talent-item .status { font-size: 11px; color: #e74c3c; margin-right: 6px; }
        .talent-item .upgrade-btn { padding: 2px 12px; font-size: 12px; }
        .footer-note { text-align: center; font-size: 11px; color: #667; margin-top: 12px; }
        .hidden { display: none !important; }

        /* ===== 锦标赛样式 ===== */
        .tournament-stats {
            display: flex; justify-content: space-around; background: #0a1a2a;
            border-radius: 16px; padding: 8px; margin-bottom: 12px;
        }
        .tournament-stats .stat { text-align: center; }
        .tournament-stats .stat .label { font-size: 12px; color: #99aabb; }
        .tournament-stats .stat .value { font-size: 18px; font-weight: bold; color: #f7d44a; }

        .tournament-list {
            display: flex; flex-direction: column; gap: 8px; max-height: 400px;
            overflow-y: auto; padding-right: 4px;
        }
        .tournament-item {
            background: #121e32; border-radius: 16px; padding: 10px 14px;
            display: flex; justify-content: space-between; align-items: center;
            border-left: 4px solid #f39c12;
        }
        .tournament-item .info .name { font-weight: 600; font-size: 15px; }
        .tournament-item .info .detail { font-size: 12px; color: #99aabb; }
        .tournament-item .info .reward { font-size: 12px; color: #f7d44a; }
        .tournament-item .btn { padding: 4px 16px; font-size: 13px; }

        .match-detail {
            background: #0a1a2a; border-radius: 16px; padding: 12px;
            margin-bottom: 12px;
        }
        .match-detail .match-info { display: flex; flex-wrap: wrap; gap: 12px; font-size: 14px; }
        .match-detail .match-info .item { background: #1a2a4a; padding: 4px 12px; border-radius: 30px; }
        .match-detail .match-log {
            background: #0a0a1a; border-radius: 12px; padding: 8px; max-height: 200px;
            overflow-y: auto; font-size: 12px; font-family: monospace; margin: 8px 0;
            border: 1px solid #2a3a6a;
        }
        .match-detail .match-log .log-line { color: #aab; padding: 2px 0; border-bottom: 1px solid #1a2a4a; }
        .match-actions {
            display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;
            margin-top: 8px;
        }
        .match-actions .btn { min-width: 80px; }

        @media (max-width: 480px) {
            .cell .rank { font-size: 18px; }
            .cell .suit { font-size: 14px; }
            .hand-cards .hand-cell { width: 40px; height: 54px; font-size: 16px; }
            .gold-display { font-size: 20px; }
            .btn { padding: 6px 12px; font-size: 12px; }
            .score-detail { max-height: 120px; font-size: 11px; }
            .game-title { font-size: 16px; }
            .tournament-item { flex-wrap: wrap; }
            .tournament-item .info { flex: 1; }
        }
    </style>
</head>
<body>
<div class="game-container" id="app">
    <div class="game-title">♠ 重生之我解锁了德州登神系统 ♥</div>

    <div class="tab-nav">
        <button class="tab-btn active" data-page="adventure">冒险</button>
        <button class="tab-btn" data-page="cash">现金桌</button>
        <button class="tab-btn" data-page="tournament">锦标赛</button>
    </div>

    <!-- ===== 冒险页面 ===== -->
    <div class="page active" id="page-adventure">
        <div class="header">
            <div class="gold-display">$ <span id="goldDisplay">0</span></div>
            <div class="controls-header">
                <button class="btn hidden" id="autoToggle">⏸️ 自动</button>
                <button class="btn" id="resetBtn" style="background:#c0392b;box-shadow:0 4px 0 #782a1a;">↺ 重置</button>
            </div>
        </div>

        <div class="hand-area">
            <span class="hand-label">🃏 手牌</span>
            <div class="hand-cards" id="handCards">
                <div class="hand-cell" id="hand0"><span style="color:#666;">?</span></div>
                <div class="hand-cell" id="hand1"><span style="color:#666;">?</span></div>
            </div>
            <span style="margin-left:auto;font-size:12px;color:#99aabb;" id="skillCounter">未解锁</span>
        </div>

        <div class="grid-wrapper">
            <div class="grid" id="gridContainer"></div>
        </div>

        <div class="score-detail" id="scoreDetail">
            <div style="color:#667;text-align:center;">拉杆后显示得分详情</div>
        </div>

        <div class="info-bar">
            <span>次数: <strong id="spinCount">0</strong></span>
            <span class="score-display">+<span id="lastScore">0</span> $</span>
            <span>每秒: <strong id="perSecond">0</strong> $</span>
        </div>

        <div class="action-area">
            <button class="btn btn-primary" id="spinBtn">🎰 拉杆</button>
        </div>

        <div class="talent-section">
            <div class="talent-tabs">
                <button class="tab-btn-sub active" data-tab="tab0">增强</button>
                <button class="tab-btn-sub" data-tab="tab1">技能</button>
                <button class="tab-btn-sub" data-tab="tab2">理论</button>
            </div>
            <div class="talent-panel active" id="tab0"></div>
            <div class="talent-panel" id="tab1"></div>
            <div class="talent-panel" id="tab2"></div>
        </div>
        <div class="footer-note">数据自动保存 · 离线挂机收益</div>
    </div>

    <!-- ===== 现金桌 ===== -->
    <div class="page" id="page-cash">
        <div style="text-align:center;padding:60px 20px;color:#aab;">
            <h2 style="color:#f7d44a;">💰 现金桌</h2>
            <p style="font-size:16px;margin-top:20px;">即将开放,敬请期待...</p>
        </div>
    </div>

    <!-- ===== 锦标赛 ===== -->
    <div class="page" id="page-tournament">
        <div id="tournamentContent"></div>
    </div>
</div>

<script>
// ============================================================
//  🎮 完整游戏逻辑(冒险 + 锦标赛)
// ============================================================

// ---------- 常量 ----------
const SUIT_SYMBOLS = { 'spade': '♠', 'heart': '♥', 'diamond': '♦', 'club': '♣' };
const ALL_RANKS = ['2','3','4','5','6','7','8','9','10','J','Q','K','A'];
const ALL_SUITS = ['spade','heart','diamond','club'];
const HAND_TYPE_NAMES = {
    'FIVE_OF_A_KIND_FLUSH': '同花五条',
    'FOUR_OF_A_KIND_FLUSH': '同花四条',
    'ROYAL_FLUSH': '皇家同花顺',
    'STRAIGHT_FLUSH': '同花顺',
    'FOUR_OF_A_KIND': '四条',
    'FULL_HOUSE': '葫芦',
    'FLUSH': '同花',
    'STRAIGHT': '顺子',
    'THREE_OF_A_KIND': '三条',
    'TWO_PAIR': '两对',
    'ONE_PAIR': '一对',
    'HIGH_CARD': '高牌',
};

const CONFIG = {
    HAND_MULTIPLIERS: {
        FIVE_OF_A_KIND_FLUSH: 1000000,
        FOUR_OF_A_KIND_FLUSH: 100000,
        ROYAL_FLUSH: 50000,
        STRAIGHT_FLUSH: 10000,
        FOUR_OF_A_KIND: 1000,
        FULL_HOUSE: 200,
        FLUSH: 30,
        STRAIGHT: 20,
        THREE_OF_A_KIND: 10,
        TWO_PAIR: 3,
        ONE_PAIR: 1,
        HIGH_CARD: 0,
    },
    SKILL_BASE_TRIGGER: 30,
    SKILL_REDUCTION_PER_LEVEL: 4,
    AUTO_INTERVAL: 9500,
    OFFLINE_DECAY: 0.8,
    ANIMATION_DURATION: 9000,
    COLUMN_STOP_TIMES: [2.5, 4.5, 6.0, 7.5, 8.8],
};

// ---------- 移牌链 ----------
const SUITS = ['diamond', 'club', 'heart', 'spade'];
const SUIT_NAMES = { diamond:'方块', club:'梅花', heart:'红桃', spade:'黑桃' };
const RANKS = ['2','3','4','5','6'];
const REMOVE_SEQUENCE = [];
for (let rank of RANKS) {
    for (let suit of SUITS) {
        REMOVE_SEQUENCE.push({
            id: `remove_${rank}_${suit}`,
            name: `移除 ${SUIT_NAMES[suit]}${rank}`,
            rank: rank,
            suit: suit,
            cost: 100 + REMOVE_SEQUENCE.length * 100,
        });
    }
}

// ---------- 锦标赛数据 ----------
const TOURNAMENTS = [
    { id: 'daily', name: '俱乐部日赛', buyin: 210, minPlayers: 60, maxPlayers: 120, skillPoints: 1 },
    { id: 'weekly', name: '俱乐部周赛', buyin: 630, minPlayers: 200, maxPlayers: 350, skillPoints: 2 },
    { id: 'monthly', name: '俱乐部月赛', buyin: 1440, minPlayers: 400, maxPlayers: 600, skillPoints: 4 },
    { id: 'national_side', name: '国内巡回赛边赛', buyin: 4400, minPlayers: 300, maxPlayers: 1000, skillPoints: 10 },
    { id: 'national_main', name: '国内巡回赛主赛', buyin: 10000, minPlayers: 1800, maxPlayers: 3500, skillPoints: 20 },
    { id: 'wsop_side', name: 'WSOP边赛', buyin: 20000, minPlayers: 1000, maxPlayers: 4500, skillPoints: 200 },
    { id: 'wsop_main', name: 'WSOP主赛', buyin: 100000, minPlayers: 8000, maxPlayers: 20000, skillPoints: 1000 },
    { id: 'wsop_million', name: 'WSOP百万豪客赛', buyin: 10000000, minPlayers: 18, maxPlayers: 27, skillPoints: 0 },
];

// ---------- 全局状态 ----------
let state = {
    gold: 100,
    spinCount: 0,
    lastScore: 0,
    talents: {
        removeProgress: 0,
        smallJoker: 0,
        bigJoker: 0,
        boostAce: 0,
        lineUnlock: 0,
        autoSpin: 0,
        skillUnlock: 0,
        theory: 0,
    },
    handsSinceLastSkill: 0,
    autoEnabled: false,
    scoreHistory: [],
    lastSaveTime: Date.now(),
    handCards: [],
    tournament: {
        practicalExp: 1,
        skillPoints: 0,
        currentMatch: null,
    }
};

// ---------- 辅助函数 ----------
function rankToValue(rank) {
    if (rank === 'A') return 14;
    if (rank === 'K') return 13;
    if (rank === 'Q') return 12;
    if (rank === 'J') return 11;
    if (rank === '10') return 10;
    if (rank === '9') return 9;
    if (rank === '8') return 8;
    if (rank === '7') return 7;
    if (rank === '6') return 6;
    if (rank === '5') return 5;
    if (rank === '4') return 4;
    if (rank === '3') return 3;
    if (rank === '2') return 2;
    return 0;
}

function createDeck() {
    const deck = [];
    for (let rank of ALL_RANKS) {
        for (let suit of ALL_SUITS) {
            deck.push({ rank, suit, isJoker: false });
        }
    }
    return deck;
}

function shuffle(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
}

// ---------- 德州扑克牌型判断(支持大小王) ----------
function getHandType5(cards) {
    // 处理王:将王替换为所有可能的牌,取最高分
    const nonJokers = cards.filter(c => !c.isJoker);
    const jokers = cards.filter(c => c.isJoker);
    if (jokers.length === 0) {
        return evaluateNormalHand(cards);
    }
    let best = null;
    const possibleRanks = ALL_RANKS;
    const possibleSuits = ALL_SUITS;
    function generateReplacements(index, current) {
        if (index === jokers.length) {
            const result = evaluateNormalHand(current);
            if (!best || result.score > best.score) best = result;
            return;
        }
        for (let rank of possibleRanks) {
            for (let suit of possibleSuits) {
                const dup = current.some(c => c.rank === rank && c.suit === suit && !c.isJoker);
                if (dup) continue;
                const newCard = { rank, suit, isJoker: false };
                generateReplacements(index + 1, [...current, newCard]);
            }
        }
    }
    generateReplacements(0, nonJokers);
    return best || { score: 0 };
}

function evaluateNormalHand(cards) {
    const values = cards.map(c => rankToValue(c.rank)).sort((a,b)=>a-b);
    const suits = cards.map(c => c.suit);
    const isFlush = suits.every(s => s === suits[0]);
    const isStraight = (() => {
        if (values[0] === 2 && values[1] === 3 && values[2] === 4 && values[3] === 5 && values[4] === 14) return true;
        for (let i=1; i<5; i++) if (values[i] !== values[i-1]+1) return false;
        return true;
    })();
    const rankCount = {};
    for (let v of values) rankCount[v] = (rankCount[v]||0)+1;
    const counts = Object.values(rankCount);
    const hasFour = counts.includes(4);
    const hasThree = counts.includes(3);
    const pairs = counts.filter(c => c===2).length;

    if (isFlush && isStraight && values[0]===10 && values[4]===14) return { score: 900 };
    if (isFlush && isStraight) return { score: 800 };
    if (hasFour) return { score: 700 };
    if (hasThree && pairs===1) return { score: 600 };
    if (isFlush) return { score: 500 };
    if (isStraight) return { score: 400 };
    if (hasThree) return { score: 300 };
    if (pairs===2) return { score: 200 };
    if (pairs===1) return { score: 100 };
    return { score: 0 };
}

function getHandType7(cards) {
    let best = null;
    const indices = cards.map((_, i) => i);
    for (let i = 0; i < indices.length; i++) {
        for (let j = i+1; j < indices.length; j++) {
            const five = cards.filter((_, idx) => idx !== i && idx !== j);
            const hand = getHandType5(five);
            if (!best || hand.score > best.score) best = hand;
        }
    }
    return best || { score: 0 };
}

// ---------- 牌技值 ----------
function getTheoryValue() {
    const lv = state.talents.theory || 0;
    return lv * 10;
}
function getSkillPower() {
    return getTheoryValue() * (state.tournament.practicalExp || 1);
}

// ---------- 冒险核心逻辑 ----------
const $ = id => document.getElementById(id);
const goldDisplay = $('goldDisplay');
const gridContainer = $('gridContainer');
const spinCountEl = $('spinCount');
const lastScoreEl = $('lastScore');
const perSecondEl = $('perSecond');
const spinBtn = $('spinBtn');
const autoToggle = $('autoToggle');
const resetBtn = $('resetBtn');
const skillCounter = $('skillCounter');
const scoreDetail = $('scoreDetail');
const hand0 = $('hand0');
const hand1 = $('hand1');

function getCurrentDeck() {
    let deck = createDeck();
    const removedCount = state.talents.removeProgress || 0;
    for (let i = 0; i < removedCount; i++) {
        const t = REMOVE_SEQUENCE[i];
        deck = deck.filter(c => !(c.rank === t.rank && c.suit === t.suit && !c.isJoker));
    }
    if (state.talents.smallJoker >= 1) {
        deck.push({ rank: '小王', suit: 'joker', isJoker: true, jokerType: 'small' });
    }
    if (state.talents.bigJoker >= 1) {
        deck.push({ rank: '大王', suit: 'joker', isJoker: true, jokerType: 'big' });
    }
    return deck;
}

function drawHandAndBoard() {
    const deck = getCurrentDeck();
    if (deck.length < 27) {
        const extra = createDeck();
        while (deck.length < 27) {
            const card = extra[Math.floor(Math.random() * extra.length)];
            if (!deck.some(c => c.rank === card.rank && c.suit === card.suit && c.isJoker === card.isJoker)) {
                deck.push({ ...card });
            }
        }
    }
    const shuffled = shuffle([...deck]);
    const hand = shuffled.slice(0, 2);
    const board = shuffled.slice(2, 27);
    return { hand, board };
}

function renderHand(hand) {
    const renderOne = (element, card) => {
        element.innerHTML = '';
        if (!card) {
            element.innerHTML = '<span style="color:#666;">?</span>';
            return;
        }
        if (card.isJoker) {
            const rankSpan = document.createElement('span');
            rankSpan.className = 'rank suit-joker';
            rankSpan.textContent = '🃏';
            const suitSpan = document.createElement('span');
            suitSpan.className = 'suit suit-joker';
            suitSpan.textContent = card.rank === '小王' ? '小' : '大';
            element.appendChild(rankSpan);
            element.appendChild(suitSpan);
        } else {
            const rankSpan = document.createElement('span');
            rankSpan.className = 'rank';
            rankSpan.textContent = card.rank;
            const suitSpan = document.createElement('span');
            suitSpan.className = 'suit ' + (card.suit === 'spade' || card.suit === 'club' ? 'suit-black' : 'suit-red');
            suitSpan.textContent = SUIT_SYMBOLS[card.suit] || '';
            element.appendChild(rankSpan);
            element.appendChild(suitSpan);
        }
    };
    renderOne(hand0, hand[0]);
    renderOne(hand1, hand[1]);
}

function renderGrid(cards) {
    gridContainer.innerHTML = '';
    for (let r = 0; r < 5; r++) {
        for (let c = 0; c < 5; c++) {
            const card = cards[r*5 + c];
            const cell = document.createElement('div');
            cell.className = 'cell';
            if (card.isJoker) {
                const rankSpan = document.createElement('span');
                rankSpan.className = 'rank suit-joker';
                rankSpan.textContent = '🃏';
                const suitSpan = document.createElement('span');
                suitSpan.className = 'suit suit-joker';
                suitSpan.textContent = card.rank === '小王' ? '小' : '大';
                cell.appendChild(rankSpan);
                cell.appendChild(suitSpan);
            } else {
                const rankSpan = document.createElement('span');
                rankSpan.className = 'rank';
                rankSpan.textContent = card.rank;
                const suitSpan = document.createElement('span');
                suitSpan.className = 'suit ' + (card.suit === 'spade' || card.suit === 'club' ? 'suit-black' : 'suit-red');
                suitSpan.textContent = SUIT_SYMBOLS[card.suit] || '';
                cell.appendChild(rankSpan);
                cell.appendChild(suitSpan);
            }
            gridContainer.appendChild(cell);
        }
    }
}

function getAllLines(cells) {
    const lines = [];
    for (let r = 0; r < 5; r++) lines.push(cells.slice(r*5, r*5+5));
    for (let c = 0; c < 5; c++) {
        const col = [];
        for (let r = 0; r < 5; r++) col.push(cells[r*5 + c]);
        lines.push(col);
    }
    const diag1 = [], diag2 = [];
    for (let i = 0; i < 5; i++) {
        diag1.push(cells[i*5 + i]);
        diag2.push(cells[i*5 + (4-i)]);
    }
    lines.push(diag1, diag2);
    return lines;
}

function getUnlockedLines(cells) {
    const all = getAllLines(cells);
    const lineUnlock = state.talents.lineUnlock || 0;
    const unlockedIndices = [0,1,2,3,4];
    const colsUnlocked = Math.min(lineUnlock, 5);
    for (let i = 0; i < colsUnlocked; i++) unlockedIndices.push(5 + i);
    if (lineUnlock >= 6) unlockedIndices.push(10, 11);
    return unlockedIndices.map(idx => all[idx]);
}

function calculateLineScore(lineCards, handCards) {
    const combined = lineCards.concat(handCards);
    const hand = getHandType7(combined);
    let score = hand.score;
    const aceCount = combined.filter(c => c.rank === 'A' && !c.isJoker).length;
    const boostLevel = state.talents.boostAce || 0;
    if (boostLevel > 0 && aceCount > 0) {
        const bonus = 0.10 * boostLevel * aceCount;
        score = Math.floor(score * (1 + bonus));
    }
    return { type: (score > 0) ? Object.keys(CONFIG.HAND_MULTIPLIERS).find(k => CONFIG.HAND_MULTIPLIERS[k] === score) : 'HIGH_CARD', score };
}

function updateScoreDetail(lineResults) {
    scoreDetail.innerHTML = '';
    if (!lineResults || lineResults.length === 0) {
        scoreDetail.innerHTML = '<div style="color:#667;text-align:center;">拉杆后显示得分详情</div>';
        return;
    }
    const filtered = lineResults.filter(item => item.score > 0);
    if (filtered.length === 0) {
        scoreDetail.innerHTML = '<div style="color:#667;text-align:center;">本局无得分线</div>';
        return;
    }
    for (let item of filtered) {
        const div = document.createElement('div');
        div.className = 'line-item';
        const nameSpan = document.createElement('span');
        nameSpan.className = 'line-name';
        nameSpan.textContent = item.name;
        const scoreSpan = document.createElement('span');
        scoreSpan.className = 'line-score';
        const typeName = HAND_TYPE_NAMES[item.type] || item.type;
        scoreSpan.textContent = `${typeName} +${item.score}`;
        div.appendChild(nameSpan);
        div.appendChild(scoreSpan);
        scoreDetail.appendChild(div);
    }
}

let isAnimating = false;
let animFrameId = null;

function performSpin() {
    if (isAnimating) return;
    try {
        // 黑桃领域触发检测
        let useSkill = false;
        const skillLevel = state.talents.skillUnlock || 0;
        if (skillLevel > 0) {
            state.handsSinceLastSkill = (state.handsSinceLastSkill || 0) + 1;
            const threshold = CONFIG.SKILL_BASE_TRIGGER - skillLevel * CONFIG.SKILL_REDUCTION_PER_LEVEL;
            if (state.handsSinceLastSkill >= threshold) {
                useSkill = true;
                state.handsSinceLastSkill = 0;
            }
        }

        const { hand, board } = drawHandAndBoard();
        state.handCards = hand;
        renderHand(hand);

        let finalCards = board;
        if (useSkill) {
            finalCards = finalCards.map(c => ({ ...c, suit: 'spade' }));
        }

        const allLines = getAllLines(finalCards);
        const lineUnlock = state.talents.lineUnlock || 0;
        const unlockedIndices = [0,1,2,3,4];
        for (let i = 0; i < Math.min(lineUnlock, 5); i++) unlockedIndices.push(5+i);
        if (lineUnlock >= 6) unlockedIndices.push(10, 11);
        const unlockedLines = unlockedIndices.map(idx => allLines[idx]);
        const lineNames = ['第1行','第2行','第3行','第4行','第5行','第1列','第2列','第3列','第4列','第5列','主对角线','副对角线'];
        const lineResults = [];
        let totalScore = 0;
        for (let i = 0; i < unlockedLines.length; i++) {
            const idx = unlockedIndices[i];
            const result = calculateLineScore(unlockedLines[i], hand);
            lineResults.push({ name: lineNames[idx], type: result.type, score: result.score });
            totalScore += result.score;
        }

        // 动画开始
        isAnimating = true;
        spinBtn.disabled = true;
        resetBtn.disabled = true;

        const finalCardsByCol = [];
        for (let c = 0; c < 5; c++) {
            const colCards = [];
            for (let r = 0; r < 5; r++) {
                colCards.push(finalCards[r*5 + c]);
            }
            finalCardsByCol.push(colCards);
        }

        const fullDeck = getCurrentDeck();
        const cells = gridContainer.children;
        const duration = CONFIG.ANIMATION_DURATION;
        const stopTimes = CONFIG.COLUMN_STOP_TIMES.map(t => t * 1000);
        const startTime = performance.now();
        let columnStopped = [false, false, false, false, false];

        function renderColumnRandom(colIndex) {
            for (let r = 0; r < 5; r++) {
                const idx = r*5 + colIndex;
                const randomCard = fullDeck[Math.floor(Math.random() * fullDeck.length)];
                const cell = cells[idx];
                cell.innerHTML = '';
                if (randomCard.isJoker) {
                    const rankSpan = document.createElement('span');
                    rankSpan.className = 'rank suit-joker';
                    rankSpan.textContent = '🃏';
                    const suitSpan = document.createElement('span');
                    suitSpan.className = 'suit suit-joker';
                    suitSpan.textContent = randomCard.rank === '小王' ? '小' : '大';
                    cell.appendChild(rankSpan);
                    cell.appendChild(suitSpan);
                } else {
                    const rankSpan = document.createElement('span');
                    rankSpan.className = 'rank';
                    rankSpan.textContent = randomCard.rank;
                    const suitSpan = document.createElement('span');
                    suitSpan.className = 'suit ' + (randomCard.suit === 'spade' || randomCard.suit === 'club' ? 'suit-black' : 'suit-red');
                    suitSpan.textContent = SUIT_SYMBOLS[randomCard.suit] || '';
                    cell.appendChild(rankSpan);
                    cell.appendChild(suitSpan);
                }
            }
        }

        function renderColumnFinal(colIndex) {
            const colCards = finalCardsByCol[colIndex];
            for (let r = 0; r < 5; r++) {
                const idx = r*5 + colIndex;
                const card = colCards[r];
                const cell = cells[idx];
                cell.innerHTML = '';
                if (card.isJoker) {
                    const rankSpan = document.createElement('span');
                    rankSpan.className = 'rank suit-joker';
                    rankSpan.textContent = '🃏';
                    const suitSpan = document.createElement('span');
                    suitSpan.className = 'suit suit-joker';
                    suitSpan.textContent = card.rank === '小王' ? '小' : '大';
                    cell.appendChild(rankSpan);
                    cell.appendChild(suitSpan);
                } else {
                    const rankSpan = document.createElement('span');
                    rankSpan.className = 'rank';
                    rankSpan.textContent = card.rank;
                    const suitSpan = document.createElement('span');
                    suitSpan.className = 'suit ' + (card.suit === 'spade' || card.suit === 'club' ? 'suit-black' : 'suit-red');
                    suitSpan.textContent = SUIT_SYMBOLS[card.suit] || '';
                    cell.appendChild(rankSpan);
                    cell.appendChild(suitSpan);
                }
            }
        }

        function animate(timestamp) {
            const elapsed = timestamp - startTime;

            for (let c = 0; c < 5; c++) {
                if (!columnStopped[c] && elapsed >= stopTimes[c]) {
                    columnStopped[c] = true;
                    renderColumnFinal(c);
                }
            }

            for (let c = 0; c < 5; c++) {
                if (columnStopped[c]) continue;
                const timeToStop = stopTimes[c] - elapsed;
                let prob = 1;
                if (timeToStop < 1000 && timeToStop > 0) {
                    prob = timeToStop / 1000;
                } else if (timeToStop <= 0) {
                    prob = 0;
                }
                if (Math.random() < prob) {
                    renderColumnRandom(c);
                }
            }

            if (columnStopped.every(v => v === true)) {
                for (let c = 0; c < 5; c++) renderColumnFinal(c);
                state.gold += totalScore;
                state.lastScore = totalScore;
                state.spinCount += 1;
                state.scoreHistory.push(totalScore);
                if (state.scoreHistory.length > 20) state.scoreHistory.shift();

                updateScoreDetail(lineResults);
                updateUI();
                saveGame();

                isAnimating = false;
                spinBtn.disabled = false;
                resetBtn.disabled = false;
                return;
            }

            animFrameId = requestAnimationFrame(animate);
        }

        animFrameId = requestAnimationFrame(animate);
    } catch (e) {
        console.error('拉杆出错:', e);
        isAnimating = false;
        spinBtn.disabled = false;
        resetBtn.disabled = false;
        alert('拉杆出现错误,请查看控制台信息');
    }
}

// ---------- 更新UI ----------
function updateUI() {
    goldDisplay.textContent = state.gold;
    spinCountEl.textContent = state.spinCount;
    lastScoreEl.textContent = state.lastScore;
    const recent = state.scoreHistory.slice(-10);
    const avg = recent.length ? Math.round(recent.reduce((a,b)=>a+b,0) / recent.length) : 0;
    perSecondEl.textContent = avg;

    const autoUnlocked = state.talents.autoSpin >= 1;
    if (autoUnlocked) {
        autoToggle.classList.remove('hidden');
        autoToggle.textContent = state.autoEnabled ? '⏸️ 自动' : '▶️ 自动';
    } else {
        autoToggle.classList.add('hidden');
        if (state.autoEnabled) {
            state.autoEnabled = false;
            stopAutoSpin();
        }
    }

    const skillLevel = state.talents.skillUnlock || 0;
    if (skillLevel === 0) {
        skillCounter.textContent = '未解锁';
    } else {
        const threshold = CONFIG.SKILL_BASE_TRIGGER - skillLevel * CONFIG.SKILL_REDUCTION_PER_LEVEL;
        const current = state.handsSinceLastSkill || 0;
        skillCounter.textContent = `触发: ${current}/${threshold}`;
    }

    updateTalentUI();
}

// ---------- 天赋系统 ----------
const THEORY_TALENT = {
    id: 'theory',
    label: '理论',
    getCost: () => {
        const lv = state.talents.theory || 0;
        if (lv >= 5) return Infinity;
        return [500, 1000, 2000, 4000, 8000][lv];
    },
    maxLevel: 5,
    getLevel: () => state.talents.theory || 0,
    getName: () => {
        const lv = state.talents.theory || 0;
        if (lv >= 5) return '理论·满级';
        return `理论 Lv${lv+1}`;
    },
    getDesc: () => {
        const lv = state.talents.theory || 0;
        if (lv >= 5) return '理论值 +50 (已满)';
        return `理论值 +${(lv+1)*10} (当前 ${lv*10})`;
    },
    prereq: () => true
};

const ENHANCE_TALENTS = [
    { id: 'removeProgress', label: '移牌', getCost: () => {
        const p = state.talents.removeProgress || 0;
        if (p >= REMOVE_SEQUENCE.length) return Infinity;
        return REMOVE_SEQUENCE[p].cost;
    }, maxLevel: REMOVE_SEQUENCE.length, getLevel: () => state.talents.removeProgress || 0,
      getName: () => {
          const p = state.talents.removeProgress || 0;
          if (p >= REMOVE_SEQUENCE.length) return '移除完成';
          const t = REMOVE_SEQUENCE[p];
          return `移除 ${SUIT_NAMES[t.suit]}${t.rank}`;
      },
      getDesc: () => {
          const p = state.talents.removeProgress || 0;
          if (p >= REMOVE_SEQUENCE.length) return '已移除所有2~6';
          const t = REMOVE_SEQUENCE[p];
          return `从牌库中移除 ${SUIT_NAMES[t.suit]}${t.rank}`;
      },
      prereq: () => true
    },
    { id: 'smallJoker', label: '小王', getCost: () => state.talents.smallJoker >= 1 ? Infinity : 1000,
      maxLevel: 1, getLevel: () => state.talents.smallJoker || 0,
      getName: () => '增加小王', getDesc: () => '牌库增加1张万能小王',
      prereq: () => true
    },
    { id: 'bigJoker', label: '大王', getCost: () => state.talents.bigJoker >= 1 ? Infinity : 10000,
      maxLevel: 1, getLevel: () => state.talents.bigJoker || 0,
      getName: () => '增加大王', getDesc: () => '牌库增加1张万能大王 (需小王≥1)',
      prereq: () => (state.talents.smallJoker || 0) >= 1
    },
    { id: 'boostAce', label: '倍率A', getCost: () => {
        const lv = state.talents.boostAce || 0;
        if (lv >= 5) return Infinity;
        return [300, 600, 1200, 2400, 4800][lv];
      }, maxLevel: 5, getLevel: () => state.talents.boostAce || 0,
      getName: () => '倍率A', getDesc: () => '每张A使总得分 +10%',
      prereq: () => true
    },
    { id: 'lineUnlock', label: '列/对角线', getCost: () => {
        const lv = state.talents.lineUnlock || 0;
        if (lv >= 6) return Infinity;
        return [100, 200, 300, 400, 500, 1000][lv];
      }, maxLevel: 6, getLevel: () => state.talents.lineUnlock || 0,
      getName: () => {
          const lv = state.talents.lineUnlock || 0;
          if (lv >= 6) return '全部解锁';
          return ['解锁第1列', '解锁第2列', '解锁第3列', '解锁第4列', '解锁第5列', '解锁两条对角线'][lv];
      },
      getDesc: () => {
          const lv = state.talents.lineUnlock || 0;
          if (lv >= 6) return '所有列和对角线已解锁';
          return ['第1列参与计分', '第2列参与计分', '第3列参与计分', '第4列参与计分', '第5列参与计分', '两条对角线参与计分'][lv];
      },
      prereq: () => true
    },
    { id: 'autoSpin', label: '自动拉杆', getCost: () => state.talents.autoSpin >= 1 ? Infinity : 100,
      maxLevel: 1, getLevel: () => state.talents.autoSpin || 0,
      getName: () => '自动拉杆', getDesc: () => '解锁自动旋转',
      prereq: () => true
    },
];

const SKILL_TALENTS = [
    { id: 'skillUnlock', label: '黑桃领域', getCost: () => {
        const lv = state.talents.skillUnlock || 0;
        if (lv >= 4) return Infinity;
        return [3000, 6000, 12000, 24000][lv];
      }, maxLevel: 4, getLevel: () => state.talents.skillUnlock || 0,
      getName: () => {
          const lv = state.talents.skillUnlock || 0;
          if (lv >= 4) return '黑桃领域·满级';
          return `黑桃领域 Lv${lv+1}`;
      },
      getDesc: () => {
          const lv = state.talents.skillUnlock || 0;
          if (lv >= 4) return '触发间隔 14手';
          const trigger = CONFIG.SKILL_BASE_TRIGGER - (lv+1) * CONFIG.SKILL_REDUCTION_PER_LEVEL;
          return `每${trigger}手牌自动触发 (当前Lv${lv+1})`;
      },
      prereq: () => true
    },
];

function createTalentItem(talent) {
    const div = document.createElement('div');
    div.className = 'talent-item';
    div.dataset.id = talent.id;
    const info = document.createElement('div');
    info.className = 'info';
    info.innerHTML = `<span class="name">${talent.getName()}</span><span class="desc">${talent.getDesc()}</span>`;
    const right = document.createElement('div');
    right.style.display = 'flex';
    right.style.alignItems = 'center';
    right.style.gap = '6px';
    const levelSpan = document.createElement('span');
    levelSpan.className = 'level';
    levelSpan.textContent = `Lv.${talent.getLevel()}`;
    const costSpan = document.createElement('span');
    costSpan.className = 'cost';
    const statusSpan = document.createElement('span');
    statusSpan.className = 'status';
    const btn = document.createElement('button');
    btn.className = 'btn btn-gold upgrade-btn';
    btn.textContent = '升级';
    btn.dataset.id = talent.id;
    right.appendChild(levelSpan);
    right.appendChild(costSpan);
    right.appendChild(statusSpan);
    right.appendChild(btn);
    div.appendChild(info);
    div.appendChild(right);
    return div;
}

function buildTalentPanels() {
    const tab0 = document.getElementById('tab0');
    tab0.innerHTML = '';
    for (let t of ENHANCE_TALENTS) tab0.appendChild(createTalentItem(t));

    const tab1 = document.getElementById('tab1');
    tab1.innerHTML = '';
    for (let t of SKILL_TALENTS) tab1.appendChild(createTalentItem(t));

    const tab2 = document.getElementById('tab2');
    tab2.innerHTML = '';
    tab2.appendChild(createTalentItem(THEORY_TALENT));

    document.querySelectorAll('.upgrade-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
            const id = e.target.dataset.id;
            upgradeTalent(id);
        });
    });
}

function updateTalentUI() {
    document.querySelectorAll('.talent-item').forEach(item => {
        const id = item.dataset.id;
        let talent = null;
        if (id === 'theory') talent = THEORY_TALENT;
        else {
            talent = ENHANCE_TALENTS.find(t => t.id === id) || SKILL_TALENTS.find(t => t.id === id);
        }
        if (!talent) return;
        const level = talent.getLevel();
        const maxLevel = talent.maxLevel;
        const cost = talent.getCost();
        const prereqOk = talent.prereq();

        const nameSpan = item.querySelector('.name');
        const descSpan = item.querySelector('.desc');
        const levelSpan = item.querySelector('.level');
        const costSpan = item.querySelector('.cost');
        const statusSpan = item.querySelector('.status');
        const btn = item.querySelector('.upgrade-btn');

        nameSpan.textContent = talent.getName();
        descSpan.textContent = talent.getDesc();
        levelSpan.textContent = `Lv.${level}`;

        if (level >= maxLevel || cost === Infinity) {
            costSpan.textContent = '已满';
            btn.disabled = true;
            btn.textContent = '满级';
            statusSpan.textContent = '';
        } else {
            costSpan.textContent = `$${cost}`;
            if (!prereqOk) {
                btn.disabled = true;
                btn.textContent = '前置不足';
                statusSpan.textContent = '🔒';
            } else if (state.gold < cost) {
                btn.disabled = true;
                btn.textContent = '金币不足';
                statusSpan.textContent = '💰';
            } else {
                btn.disabled = false;
                btn.textContent = '升级';
                statusSpan.textContent = '✅';
            }
        }
    });
}

function upgradeTalent(id) {
    let talent = null;
    if (id === 'theory') talent = THEORY_TALENT;
    else {
        talent = ENHANCE_TALENTS.find(t => t.id === id) || SKILL_TALENTS.find(t => t.id === id);
    }
    if (!talent) return;

    const level = talent.getLevel();
    const maxLevel = talent.maxLevel;
    if (level >= maxLevel) return;
    if (!talent.prereq()) {
        alert('前置条件未满足!');
        return;
    }
    const cost = talent.getCost();
    if (cost === Infinity) {
        alert('已满级');
        return;
    }
    if (state.gold < cost) {
        alert(`金币不足!需要 $${cost}`);
        return;
    }
    state.gold -= cost;
    // 升级
    if (id === 'removeProgress') {
        state.talents.removeProgress = (state.talents.removeProgress || 0) + 1;
    } else if (id === 'smallJoker') {
        state.talents.smallJoker = 1;
    } else if (id === 'bigJoker') {
        state.talents.bigJoker = 1;
    } else if (id === 'boostAce') {
        state.talents.boostAce = (state.talents.boostAce || 0) + 1;
    } else if (id === 'lineUnlock') {
        state.talents.lineUnlock = (state.talents.lineUnlock || 0) + 1;
    } else if (id === 'autoSpin') {
        state.talents.autoSpin = 1;
    } else if (id === 'skillUnlock') {
        state.talents.skillUnlock = (state.talents.skillUnlock || 0) + 1;
        state.handsSinceLastSkill = 0;
    } else if (id === 'theory') {
        state.talents.theory = (state.talents.theory || 0) + 1;
    }
    saveGame();
    updateUI();
}

// ---------- 自动拉杆 ----------
let autoTimer = null;
function startAutoSpin() {
    if (autoTimer) clearInterval(autoTimer);
    if (!state.autoEnabled || state.talents.autoSpin < 1) return;
    autoTimer = setInterval(() => {
        if (!isAnimating) {
            performSpin();
        }
    }, CONFIG.AUTO_INTERVAL);
}
function stopAutoSpin() {
    if (autoTimer) { clearInterval(autoTimer); autoTimer = null; }
}
function toggleAuto() {
    if (state.talents.autoSpin < 1) {
        alert('请先在天赋中解锁「自动拉杆」');
        return;
    }
    state.autoEnabled = !state.autoEnabled;
    if (state.autoEnabled) startAutoSpin();
    else stopAutoSpin();
    saveGame();
    updateUI();
}

// ---------- 锦标赛逻辑 ----------
function renderTournament() {
    const container = document.getElementById('tournamentContent');
    const theory = getTheoryValue();
    const exp = state.tournament.practicalExp || 1;
    const skillPower = theory * exp;
    const skillPoints = state.tournament.skillPoints || 0;

    if (state.tournament.currentMatch) {
        renderMatchDetail(container);
        return;
    }

    container.innerHTML = `
        <div class="tournament-stats">
            <div class="stat"><div class="label">牌技值</div><div class="value">${skillPower}</div></div>
            <div class="stat"><div class="label">理论</div><div class="value">${theory}</div></div>
            <div class="stat"><div class="label">实战经验</div><div class="value">${exp}</div></div>
            <div class="stat"><div class="label">技能点</div><div class="value">${skillPoints}</div></div>
        </div>
        <div style="margin-bottom:10px;font-size:14px;color:#aab;">选择比赛报名:</div>
        <div class="tournament-list" id="tournamentList">
            ${TOURNAMENTS.map(t => `
                <div class="tournament-item" data-id="${t.id}">
                    <div class="info">
                        <div class="name">${t.name}</div>
                        <div class="detail">报名费 $${t.buyin}  · 人数 ${t.minPlayers}-${t.maxPlayers}</div>
                        <div class="reward">🏆 冠军奖励 ${t.skillPoints} 技能点</div>
                    </div>
                    <button class="btn btn-primary join-btn" data-id="${t.id}">报名</button>
                </div>
            `).join('')}
        </div>
    `;

    container.querySelectorAll('.join-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            const id = this.dataset.id;
            joinTournament(id);
        });
    });
}

function joinTournament(id) {
    const tournament = TOURNAMENTS.find(t => t.id === id);
    if (!tournament) return;
    if (state.gold < tournament.buyin) {
        alert(`金币不足!需要 $${tournament.buyin}`);
        return;
    }
    state.gold -= tournament.buyin;
    const players = Math.floor(Math.random() * (tournament.maxPlayers - tournament.minPlayers + 1)) + tournament.minPlayers;
    const match = {
        id: tournament.id,
        name: tournament.name,
        buyin: tournament.buyin,
        totalPlayers: players,
        remainingPlayers: players,
        skillPointsReward: tournament.skillPoints,
        status: 'registered',
        blinds: 20,
        level: 1,
        myChips: players * 1000,
        avgChips: players * 1000,
        logs: [],
        finished: false,
        result: null,
        isManual: false,
        waitingForAction: false,
        actionResolve: null,
        intervalId: null,
        opponents: [],
    };
    state.tournament.currentMatch = match;
    renderTournament();
}

function renderMatchDetail(container) {
    const match = state.tournament.currentMatch;
    if (!match) return renderTournament();

    const logsHtml = match.logs.slice(-30).map(log => `<div class="log-line">${log}</div>`).join('');

    container.innerHTML = `
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
            <span style="font-size:18px;font-weight:bold;">${match.name}</span>
            <button class="btn" id="exitMatchBtn" style="background:#c0392b;box-shadow:0 4px 0 #782a1a;">退出</button>
        </div>
        <div class="match-detail">
            <div class="match-info">
                <span class="item">报名费 $${match.buyin}</span>
                <span class="item">参赛人数 ${match.totalPlayers}</span>
                <span class="item">剩余 ${match.remainingPlayers}</span>
                <span class="item">盲注级别 ${match.level}</span>
                <span class="item">盲注 ${match.blinds}/${match.blinds*2}</span>
                <span class="item">我的筹码 $${match.myChips}</span>
                <span class="item">平均筹码 $${match.avgChips}</span>
            </div>
            ${match.status === 'registered' ? `
                <div style="margin:12px 0;text-align:center;">
                    <button class="btn btn-primary" id="startMatchBtn">开始比赛</button>
                </div>
            ` : `
                <div class="match-log" id="matchLog">
                    ${logsHtml}
                </div>
                ${match.waitingForAction ? `
                    <div class="match-actions" id="actionButtons">
                        <button class="btn btn-success action-btn" data-action="fold">弃牌</button>
                        <button class="btn btn-primary action-btn" data-action="call">跟注</button>
                        <button class="btn btn-gold action-btn" data-action="raise">加注</button>
                    </div>
                ` : ''}
                ${match.status === 'finished' ? `
                    <div style="margin-top:10px;padding:10px;background:#1a2a4a;border-radius:12px;text-align:center;">
                        <div>🏆 比赛结束!</div>
                        <div>名次: ${match.result.rank}</div>
                        <div>奖金: $${match.result.prize}</div>
                        <div>获得技能点: ${match.result.skillPoints}</div>
                        <button class="btn btn-primary" id="claimPrizeBtn">领取奖励</button>
                    </div>
                ` : ''}
            `}
        </div>
    `;

    // 绑定事件
    const exitBtn = document.getElementById('exitMatchBtn');
    if (exitBtn) exitBtn.addEventListener('click', () => {
        if (match.status === 'running' || match.status === 'registered') {
            if (!confirm('退出将放弃当前比赛,确定吗?')) return;
        }
        if (match.intervalId) clearInterval(match.intervalId);
        state.tournament.currentMatch = null;
        renderTournament();
    });

    const startBtn = document.getElementById('startMatchBtn');
    if (startBtn) {
        startBtn.addEventListener('click', () => {
            const mode = confirm('选择比赛模式:\n点击“确定”为自动模式,点击“取消”为手动模式');
            match.isManual = !mode;
            match.status = 'running';
            startMatch(match);
            renderTournament();
        });
    }

    const claimBtn = document.getElementById('claimPrizeBtn');
    if (claimBtn) {
        claimBtn.addEventListener('click', () => {
            const prize = match.result.prize;
            const sp = match.result.skillPoints;
            state.gold += prize;
            state.tournament.skillPoints = (state.tournament.skillPoints || 0) + sp;
            state.tournament.practicalExp = (state.tournament.practicalExp || 1) + 1;
            if (match.intervalId) clearInterval(match.intervalId);
            state.tournament.currentMatch = null;
            saveGame();
            renderTournament();
        });
    }

    document.querySelectorAll('.action-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            const action = this.dataset.action;
            if (match.waitingForAction && match.actionResolve) {
                match.actionResolve(action);
                match.waitingForAction = false;
                match.actionResolve = null;
                setTimeout(() => simulateHand(match), 300);
            }
        });
    });
}

function startMatch(match) {
    match.opponents = [];
    for (let i = 0; i < match.totalPlayers - 1; i++) {
        match.opponents.push({ chips: match.totalPlayers * 1000, active: true });
    }
    match.myChips = match.totalPlayers * 1000;
    match.avgChips = match.totalPlayers * 1000;
    match.logs = [`比赛开始!共 ${match.totalPlayers} 名选手`];
    match.level = 1;
    match.blinds = 20;

    if (!match.isManual) {
        match.intervalId = setInterval(() => {
            if (match.status !== 'running') return;
            simulateHand(match);
        }, 1500);
    } else {
        simulateHand(match);
    }
}

function simulateHand(match) {
    if (match.status === 'finished') return;

    const activePlayers = match.opponents.filter(o => o.active).length + 1;
    if (activePlayers <= 1) {
        finishMatch(match, 1);
        return;
    }
    match.remainingPlayers = activePlayers;

    match.level = Math.floor(match.logs.length / 10) + 1;
    match.blinds = 20 + (match.level - 1) * 10;

    const deck = createDeck();
    shuffle(deck);
    match.hand = deck.slice(0, 2);
    const community = deck.slice(2, 7);
    match.board = community;

    const playerHand = getHandType7(match.hand.concat(community));
    const playerScore = playerHand.score;

    const theory = getTheoryValue();
    const exp = state.tournament.practicalExp || 1;
    const skillPower = theory * exp;
    let winRate = Math.min(playerScore / 1000, 0.8);
    winRate += skillPower / 10000;
    winRate = Math.min(winRate, 0.95);

    const isWin = Math.random() < winRate;
    const pot = match.blinds * 2 + Math.floor(Math.random() * 200);
    if (isWin) {
        match.myChips += pot;
        match.logs.push(`✅ 赢下底池 $${pot},当前筹码 $${match.myChips}`);
    } else {
        match.myChips -= pot;
        if (match.myChips < 0) match.myChips = 0;
        match.logs.push(`❌ 输掉底池 $${pot},当前筹码 $${match.myChips}`);
    }

    for (let opp of match.opponents) {
        if (!opp.active) continue;
        if (Math.random() < 0.05) {
            opp.active = false;
            match.logs.push(`💀 一名选手被淘汰`);
        }
    }

    if (match.myChips <= 0) {
        match.myChips = 0;
        finishMatch(match, activePlayers);
        return;
    }

    if (match.isManual) {
        match.waitingForAction = true;
        const actionPromise = new Promise((resolve) => {
            match.actionResolve = resolve;
        });
        renderTournament();
        return;
    }

    renderTournament();
}

function finishMatch(match, rank) {
    match.status = 'finished';
    if (match.intervalId) clearInterval(match.intervalId);
    const totalPrize = match.buyin * match.totalPlayers;
    const winners = Math.floor(match.totalPlayers * 0.15);
    let prize = 0;
    if (rank <= winners) {
        const factor = Math.max(0.02, 0.20 - (rank - 1) * 0.01);
        prize = Math.floor(totalPrize * factor);
        if (prize < match.buyin * 2) prize = match.buyin * 2;
    } else {
        prize = 0;
    }
    let skillPoints = 0;
    if (rank === 1) {
        skillPoints = match.skillPointsReward;
    } else if (rank <= 3) {
        skillPoints = Math.floor(match.skillPointsReward * 0.2);
    } else if (rank <= 10) {
        skillPoints = Math.floor(match.skillPointsReward * 0.05);
    }
    match.result = { rank, prize, skillPoints };
    match.logs.push(`🏁 比赛结束!名次 ${rank},奖金 $${prize},技能点 ${skillPoints}`);
    renderTournament();
}

// ---------- 存档 ----------
function saveGame() {
    const data = {
        gold: state.gold,
        spinCount: state.spinCount,
        lastScore: state.lastScore,
        talents: state.talents,
        handsSinceLastSkill: state.handsSinceLastSkill || 0,
        autoEnabled: state.autoEnabled,
        scoreHistory: state.scoreHistory,
        lastSaveTime: Date.now(),
        tournament: {
            practicalExp: state.tournament.practicalExp || 1,
            skillPoints: state.tournament.skillPoints || 0,
        }
    };
    localStorage.setItem('pokerSlotData', JSON.stringify(data));
}

function loadGame() {
    const raw = localStorage.getItem('pokerSlotData');
    if (!raw) return false;
    try {
        const data = JSON.parse(raw);
        state.gold = data.gold ?? 100;
        state.spinCount = data.spinCount ?? 0;
        state.lastScore = data.lastScore ?? 0;
        state.talents = data.talents || {};
        state.handsSinceLastSkill = data.handsSinceLastSkill || 0;
        state.autoEnabled = data.autoEnabled || false;
        state.scoreHistory = data.scoreHistory || [];
        state.tournament.practicalExp = data.tournament?.practicalExp || 1;
        state.tournament.skillPoints = data.tournament?.skillPoints || 0;
        const defaultTalents = {
            removeProgress: 0, smallJoker: 0, bigJoker: 0, boostAce: 0,
            lineUnlock: 0, autoSpin: 0, skillUnlock: 0, theory: 0,
        };
        for (let key in defaultTalents) {
            if (!(key in state.talents)) state.talents[key] = defaultTalents[key];
        }
        if (state.talents.autoSpin < 1) state.autoEnabled = false;
        return true;
    } catch(e) { return false; }
}

// ---------- 重置 ----------
function resetGame() {
    if (!confirm('重置所有数据(包括金币和天赋)?')) return;
    stopAutoSpin();
    if (animFrameId) cancelAnimationFrame(animFrameId);
    isAnimating = false;
    state.gold = 100;
    state.spinCount = 0;
    state.lastScore = 0;
    state.talents = {
        removeProgress: 0, smallJoker: 0, bigJoker: 0, boostAce: 0,
        lineUnlock: 0, autoSpin: 0, skillUnlock: 0, theory: 0,
    };
    state.handsSinceLastSkill = 0;
    state.autoEnabled = false;
    state.scoreHistory = [];
    state.lastSaveTime = Date.now();
    state.tournament.practicalExp = 1;
    state.tournament.skillPoints = 0;
    state.tournament.currentMatch = null;
    localStorage.removeItem('pokerSlotData');
    const initial = drawHandAndBoard();
    renderHand(initial.hand);
    renderGrid(initial.board);
    updateScoreDetail([]);
    updateUI();
    if (autoTimer) clearInterval(autoTimer);
    renderTournament();
    saveGame();
}

// ---------- 初始化 ----------
function initGame() {
    loadGame();

    // 构建天赋面板
    buildTalentPanels();

    // 初始牌局
    const initial = drawHandAndBoard();
    renderHand(initial.hand);
    renderGrid(initial.board);
    updateScoreDetail([]);
    updateUI();

    // 事件绑定
    spinBtn.addEventListener('click', performSpin);
    autoToggle.addEventListener('click', toggleAuto);
    resetBtn.addEventListener('click', resetGame);

    // 天赋子标签切换
    document.querySelectorAll('.talent-tabs .tab-btn-sub').forEach(tab => {
        tab.addEventListener('click', function() {
            document.querySelectorAll('.talent-tabs .tab-btn-sub').forEach(t => t.classList.remove('active'));
            this.classList.add('active');
            document.querySelectorAll('.talent-panel').forEach(p => p.classList.remove('active'));
            document.getElementById(this.dataset.tab).classList.add('active');
        });
    });

    // 自动开启(若存档中有)
    if (state.talents.autoSpin >= 1 && state.autoEnabled) {
        startAutoSpin();
    }

    // 渲染锦标赛
    renderTournament();

    // 主标签切换
    document.querySelectorAll('.tab-nav .tab-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            document.querySelectorAll('.tab-nav .tab-btn').forEach(b => b.classList.remove('active'));
            this.classList.add('active');
            document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
            const pageId = this.dataset.page;
            document.getElementById('page-' + pageId).classList.add('active');
            if (pageId === 'tournament') renderTournament();
            if (pageId === 'adventure') updateUI();
        });
    });

    // 自动保存
    setInterval(saveGame, 30000);
    window.addEventListener('beforeunload', saveGame);
}

// 启动
window.onload = initGame;
</script>
</body>
</html>

Game Source: 重生之我解锁了德州登神系统

Creator: RocketPanda33

Libraries: none

Complexity: complex (1647 lines, 62.9 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-rocketpanda33" to link back to the original. Then publish at arcadelab.ai/publish.