🎮ArcadeLab

Remix of 虚荣抉择 · 我的虚荣女友

by ElectricFlare30
2007 lines91.7 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, viewport-fit=cover">
    <title>虚荣抉择 · 我的虚荣女友</title>
    <style>
        /* ===== 全局样式(与之前一致,已压缩) ===== */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }
        body {
            font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif;
            background: #141c26;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 12px;
            touch-action: manipulation;
        }
        #app {
            width: 100%;
            max-width: 420px;
            margin: 0 auto;
            background: linear-gradient(145deg, #1a2332 0%, #2d3a4a 100%);
            border-radius: 32px;
            padding: 16px 14px 12px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.7);
            min-height: 92vh;
            display: flex;
            flex-direction: column;
            position: relative;
            overflow: hidden;
        }
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 4px 4px 12px 4px;
            flex-shrink: 0;
            border-bottom: 1px solid rgba(255, 255, 255, 0.06);
        }
        .header h1 {
            font-size: 18px;
            font-weight: 600;
            color: #e8dccc;
            letter-spacing: 1px;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }
        .header h1 span {
            font-weight: 300;
            color: #b8a99a;
            font-size: 14px;
        }
        .header-actions {
            display: flex;
            gap: 6px;
        }
        .header-actions button {
            background: rgba(255, 255, 255, 0.06);
            border: none;
            color: #c8bdb0;
            font-size: 18px;
            width: 36px;
            height: 36px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: all 0.2s;
            touch-action: manipulation;
        }
        .header-actions button:active {
            transform: scale(0.88);
            background: rgba(255, 255, 255, 0.15);
        }
        .header-actions button.active {
            color: #f0d5b0;
            background: rgba(240, 213, 176, 0.15);
        }
        .main-content {
            flex: 1;
            overflow-y: auto;
            padding: 8px 0 6px;
            margin: 6px 0 4px;
            scroll-behavior: smooth;
            -webkit-overflow-scrolling: touch;
        }
        .main-content::-webkit-scrollbar {
            width: 3px;
        }
        .main-content::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, 0.15);
            border-radius: 10px;
        }
        .story-card {
            background: #f5f0e8;
            border-radius: 24px;
            padding: 20px 18px 18px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
            transition: opacity 0.4s ease, transform 0.4s ease;
            min-height: 200px;
            display: flex;
            flex-direction: column;
        }
        .story-card.fade-out {
            opacity: 0;
            transform: translateY(12px);
        }
        .story-card.fade-in {
            opacity: 1;
            transform: translateY(0);
        }
        .story-title {
            font-size: 16px;
            font-weight: 600;
            color: #3d2e1e;
            margin-bottom: 10px;
            letter-spacing: 0.5px;
            border-left: 3px solid #c8a882;
            padding-left: 12px;
        }
        .story-title .badge {
            font-size: 11px;
            font-weight: 400;
            color: #8a7a6a;
            margin-left: 8px;
            background: rgba(200, 168, 130, 0.2);
            padding: 0 10px;
            border-radius: 20px;
        }
        .story-text {
            font-size: 15.5px;
            line-height: 1.75;
            color: #2c241c;
            white-space: pre-wrap;
            word-break: break-word;
            min-height: 80px;
            font-weight: 400;
            letter-spacing: 0.3px;
        }
        .story-text .cursor-blink {
            display: inline-block;
            width: 2px;
            height: 1em;
            background: #8a7a6a;
            margin-left: 2px;
            animation: blink 0.9s step-end infinite;
            vertical-align: text-bottom;
        }
        @keyframes blink {
            0%,
            100% {
                opacity: 1;
            }
            50% {
                opacity: 0;
            }
        }
        .memory-trigger {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            margin-top: 12px;
            padding: 6px 14px 6px 10px;
            background: rgba(200, 168, 130, 0.18);
            border-radius: 30px;
            color: #6b4f3a;
            font-size: 13px;
            font-weight: 500;
            cursor: pointer;
            touch-action: manipulation;
            transition: all 0.2s;
            border: none;
            width: fit-content;
            align-self: flex-start;
        }
        .memory-trigger:active {
            transform: scale(0.94);
            background: rgba(200, 168, 130, 0.3);
        }
        .memory-trigger .icon {
            font-size: 18px;
        }
        .options-area {
            margin-top: 18px;
            display: flex;
            flex-direction: column;
            gap: 10px;
            padding-top: 14px;
            border-top: 1px solid rgba(60, 45, 30, 0.08);
        }
        .option-btn {
            background: #fffaf5;
            border: 1.5px solid #dacfc4;
            border-radius: 18px;
            padding: 14px 18px;
            font-size: 15px;
            font-weight: 500;
            color: #2c241c;
            text-align: left;
            cursor: pointer;
            touch-action: manipulation;
            transition: all 0.15s ease;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.02);
            line-height: 1.4;
            letter-spacing: 0.2px;
            position: relative;
            min-height: 52px;
            display: flex;
            align-items: center;
        }
        .option-btn:active {
            transform: scale(0.96);
            background: #f0e8de;
            border-color: #b8a08c;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
        }
        .option-btn .opt-label {
            display: inline-block;
            background: #dacfc4;
            color: #3d2e1e;
            font-size: 12px;
            font-weight: 600;
            padding: 0 10px;
            border-radius: 30px;
            margin-right: 12px;
            flex-shrink: 0;
            line-height: 22px;
            letter-spacing: 0.5px;
        }
        .option-btn .opt-text {
            flex: 1;
        }
        .option-btn .opt-hint {
            font-size: 12px;
            color: #8a7a6a;
            font-weight: 400;
            margin-left: 6px;
        }
        .status-bar {
            flex-shrink: 0;
            display: flex;
            justify-content: space-around;
            align-items: center;
            background: rgba(0, 0, 0, 0.25);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 10px 8px;
            margin-top: 8px;
            border: 1px solid rgba(255, 255, 255, 0.05);
        }
        .status-item {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 1px;
            flex: 1;
        }
        .status-item .label {
            font-size: 10px;
            color: #a09888;
            letter-spacing: 0.5px;
            font-weight: 400;
            text-transform: uppercase;
        }
        .status-item .value {
            font-size: 17px;
            font-weight: 700;
            color: #f0e8dc;
            line-height: 1.2;
            transition: color 0.3s;
        }
        .status-item .value.va-high {
            color: #e8b88a;
        }
        .status-item .value.va-mid {
            color: #d4c8b8;
        }
        .status-item .value.va-low {
            color: #8ab8d4;
        }
        .status-item .value.le-high {
            color: #e8b88a;
        }
        .status-item .value.le-mid {
            color: #d4c8b8;
        }
        .status-item .value.le-low {
            color: #8ab8d4;
        }
        .status-item .value.wi-high {
            color: #e8b88a;
        }
        .status-item .value.wi-mid {
            color: #d4c8b8;
        }
        .status-item .value.wi-low {
            color: #8ab8d4;
        }
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.7);
            backdrop-filter: blur(8px);
            -webkit-backdrop-filter: blur(8px);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            padding: 20px;
            animation: fadeInModal 0.3s ease;
        }
        .modal-overlay.open {
            display: flex;
        }
        @keyframes fadeInModal {
            from {
                opacity: 0;
                transform: scale(0.96);
            }
            to {
                opacity: 1;
                transform: scale(1);
            }
        }
        .modal-box {
            background: #f5f0e8;
            border-radius: 32px;
            max-width: 400px;
            width: 100%;
            max-height: 80vh;
            padding: 24px 20px 20px;
            overflow-y: auto;
            box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
        }
        .modal-box h2 {
            font-size: 20px;
            color: #3d2e1e;
            margin-bottom: 16px;
            text-align: center;
            font-weight: 600;
            letter-spacing: 1px;
        }
        .modal-close {
            display: block;
            margin: 16px auto 0;
            background: #dacfc4;
            border: none;
            padding: 12px 32px;
            border-radius: 40px;
            font-size: 16px;
            font-weight: 600;
            color: #2c241c;
            cursor: pointer;
            touch-action: manipulation;
            transition: all 0.2s;
        }
        .modal-close:active {
            transform: scale(0.94);
        }
        .gallery-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .gallery-item {
            background: #fffaf5;
            border-radius: 16px;
            padding: 14px 16px;
            border: 1.5px solid #dacfc4;
            transition: all 0.2s;
        }
        .gallery-item.locked {
            opacity: 0.4;
            filter: grayscale(0.6);
        }
        .gallery-item .g-title {
            font-weight: 600;
            color: #3d2e1e;
            font-size: 15px;
        }
        .gallery-item .g-preview {
            font-size: 13px;
            color: #6b5f4f;
            margin-top: 4px;
            line-height: 1.5;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }
        .gallery-item .g-status {
            font-size: 12px;
            color: #8a7a6a;
            margin-top: 4px;
        }
        .gallery-item .g-status .unlocked {
            color: #6a8a6a;
        }
        .gallery-item .g-status .locked {
            color: #b0a090;
        }
        .memory-popup {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.6);
            backdrop-filter: blur(6px);
            -webkit-backdrop-filter: blur(6px);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 999;
            padding: 24px;
        }
        .memory-popup.open {
            display: flex;
        }
        .memory-popup .mem-box {
            background: #f5f0e8;
            border-radius: 28px;
            max-width: 380px;
            width: 100%;
            padding: 24px 20px 20px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
            animation: fadeInModal 0.3s ease;
        }
        .memory-popup .mem-box h3 {
            font-size: 17px;
            color: #3d2e1e;
            margin-bottom: 10px;
            border-left: 3px solid #c8a882;
            padding-left: 12px;
        }
        .memory-popup .mem-box p {
            font-size: 15px;
            line-height: 1.7;
            color: #2c241c;
            white-space: pre-wrap;
        }
        .memory-popup .mem-box .mem-close {
            margin-top: 16px;
            padding: 10px 28px;
            background: #dacfc4;
            border: none;
            border-radius: 40px;
            font-size: 15px;
            font-weight: 500;
            color: #2c241c;
            cursor: pointer;
            touch-action: manipulation;
            transition: all 0.2s;
            display: block;
            margin-left: auto;
        }
        .memory-popup .mem-box .mem-close:active {
            transform: scale(0.94);
        }
        .ending-actions {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 18px;
            justify-content: center;
        }
        .ending-actions button {
            background: #fffaf5;
            border: 1.5px solid #dacfc4;
            border-radius: 40px;
            padding: 10px 22px;
            font-size: 14px;
            font-weight: 500;
            color: #2c241c;
            cursor: pointer;
            touch-action: manipulation;
            transition: all 0.15s;
            flex: 1;
            min-width: 100px;
        }
        .ending-actions button:active {
            transform: scale(0.94);
            background: #f0e8de;
        }
        .ending-actions button.primary {
            background: #c8a882;
            border-color: #b8987a;
            color: #2c241c;
            font-weight: 600;
        }
        .ending-actions button.primary:active {
            background: #b8987a;
        }
        .hidden {
            display: none !important;
        }
        .text-center {
            text-align: center;
        }
        .mt-8 {
            margin-top: 8px;
        }
        .mt-12 {
            margin-top: 12px;
        }
        .modal-box::-webkit-scrollbar,
        .main-content::-webkit-scrollbar {
            width: 4px;
        }
        .modal-box::-webkit-scrollbar-thumb,
        .main-content::-webkit-scrollbar-thumb {
            background: rgba(60, 45, 30, 0.2);
            border-radius: 10px;
        }
        @media (max-width: 380px) {
            #app {
                padding: 10px 8px 8px;
                border-radius: 24px;
            }
            .story-card {
                padding: 16px 14px 14px;
            }
            .story-text {
                font-size: 14.5px;
                line-height: 1.7;
            }
            .option-btn {
                padding: 12px 14px;
                font-size: 14px;
                min-height: 46px;
            }
            .status-item .value {
                font-size: 15px;
            }
            .header h1 {
                font-size: 16px;
            }
        }
        @media (max-height: 700px) {
            .story-card {
                min-height: 140px;
            }
            .story-text {
                font-size: 14px;
                min-height: 50px;
            }
            .options-area {
                gap: 8px;
                margin-top: 12px;
                padding-top: 10px;
            }
            .option-btn {
                padding: 10px 14px;
                min-height: 42px;
                font-size: 14px;
            }
            .status-bar {
                padding: 6px 8px;
            }
            .status-item .value {
                font-size: 15px;
            }
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="header">
            <h1>虚荣抉择 <span>· 我的虚荣女友</span></h1>
            <div class="header-actions">
                <button id="btnSound" aria-label="音效开关">🔊</button>
                <button id="btnGallery" aria-label="结局图鉴">📖</button>
                <button id="btnRestart" aria-label="重新开始">🔄</button>
            </div>
        </div>
        <div class="main-content" id="mainContent">
            <div class="story-card fade-in" id="storyCard">
                <div class="story-title" id="storyTitle">序章</div>
                <div class="story-text" id="storyText"></div>
                <button class="memory-trigger hidden" id="memoryTrigger">
                    <span class="icon">📜</span> 回忆片段
                </button>
                <div class="options-area" id="optionsArea"></div>
            </div>
        </div>
        <div class="status-bar">
            <div class="status-item">
                <span class="label">💎 虚荣值</span>
                <span class="value" id="valVa">45</span>
            </div>
            <div class="status-item">
                <span class="label">❤️ 磊磊好感</span>
                <span class="value" id="valLe">60</span>
            </div>
            <div class="status-item">
                <span class="label">👑 王子执念</span>
                <span class="value" id="valWi">30</span>
            </div>
        </div>
    </div>

    <div class="modal-overlay" id="galleryModal">
        <div class="modal-box">
            <h2>📖 结局图鉴</h2>
            <div class="gallery-grid" id="galleryGrid"></div>
            <button class="modal-close" id="galleryClose">关闭</button>
        </div>
    </div>

    <div class="memory-popup" id="memoryPopup">
        <div class="mem-box">
            <h3 id="memTitle">📜 回忆</h3>
            <p id="memText"></p>
            <button class="mem-close" id="memClose">收起</button>
        </div>
    </div>

    <script>
        // ================================================================
        //  结局定义 (9个结局)
        // ================================================================
        const ENDINGS = {
            A: {
                id: 'A',
                title: '⭐ 烟火余生 · 真结局',
                preview: '你选择和林磊站在一起,普通人间烟火,足以温暖余生。',
                fullText: `你拉黑王子所有联系方式,坦诚内心,选择和林磊站在一起。\n诱惑并没有彻底消失,往后逛街橱窗依旧会看见璀璨奢侈品,心底虚荣念头偶尔还会冒出来。但你学会区分"渴望"和"必须拥有"。\n你们一起省吃俭用,努力打拼,多年之后买下一套不大却属于自己的小三居。两人约定设立家庭"欣赏基金",只买自己真正负担得起、真心热爱的东西,拒绝高仿,拒绝透支面子。\n三年后的商场,偶然和王子擦肩而过。他依旧活在那个金光闪闪的上层世界。你们只是互不打扰的陌生人。\n没有直升机,没有私人岛屿,没有海瑞温斯顿钻石。\n但锅里咕嘟冒泡的红烧肉,彼此紧握的双手,普通人间烟火,足以温暖余生。`,
                icon: '⭐',
                hidden: false
            },
            B: {
                id: 'B',
                title: '💔 金丝雀牢笼 · 悲剧结局',
                preview: '你住进梦寐以求的江景大平层,却永远活在别人的审视之下。',
                fullText: `你住进了梦寐以求的江景大平层,珠宝华服唾手可得。\n可你永远活在别人的审视之下。王子对你日渐平淡,你清楚自己更像一件被收藏的漂亮藏品。\n你再也听不到林磊带着嫌弃的唠叨,再也吃不到那碗热乎红烧肉。午夜独处在空旷豪宅,物质填满房间,填不满心底巨大空洞。\n这场以报恩开场的童话,最终变成一座金碧辉煌的牢笼。`,
                icon: '💔',
                hidden: false
            },
            C: {
                id: 'C',
                title: '🌫️ 孑然摇摆 · 普通结局',
                preview: '你始终在两条道路之间反复摇摆,最终豪门的捷径没有抓住,平凡的真心也被消耗殆尽。',
                fullText: `你始终在两条道路之间反复摇摆。既放不下王子带来的体面光鲜,又割舍不掉林磊六年的感情羁绊。\n人性的耐心是有限的。\n林磊耗尽所有期待,彻底退出你的人生。王子也厌倦你的犹豫不决,收回大部分馈赠,慢慢淡出你的生活。\n到最后,豪门的捷径没有抓住,平凡的真心也被消耗殆尽。\n你留在原地,孤身一人,继续在这座城市独自前行。`,
                icon: '🌫️',
                hidden: false
            },
            D: {
                id: 'D',
                title: '✨ 独自成长 · 开放结局',
                preview: '你坚定拒绝王子,选择暂时不和林磊复合,先找回自我。',
                fullText: `你坚定拒绝王子全部物质与示好。\n但伤痛还未消散,你选择暂时不和林磊复合。\n你决定先找回自我,不再依靠奢侈品伪装自尊。认真打磨自己的工作能力,慢慢梳理内心欲望。\n你和林磊没有彻底断联,保持距离,留给彼此时间沉淀。未来会走向何方,一切都是未知数。\n不必急着一定要选择谁,先成为完整的自己。`,
                icon: '✨',
                hidden: false
            },
            E: {
                id: 'E',
                title: '🔍 清醒抽身 · 搞事业 (隐藏)',
                preview: '情爱暂时靠边站,你把全部精力投入职场,用实力换取尊重。',
                fullText: `情爱暂时全部靠边站。\n你拒绝王子所有物质,也暂时放下和林磊的感情纠葛。\n你把全部精力投入自己的职场。不再执着包装外在名牌,转而提升自己的能力,用实力换取尊重。\n你明白了:真正的体面,从来不是包包衣服上的logo,而是自己手握的底气。\n一个人的日子,同样可以闪闪发光。`,
                icon: '🔍',
                hidden: true
            },
            F: {
                id: 'F',
                title: '😵 短暂幻梦 · 隐藏结局',
                preview: '你短暂踏入豪门世界,幡然醒悟时两边都已失去。',
                fullText: `你短暂踏入那个纸醉金迷的豪门世界,在物质漩涡之中幡然醒悟,想要回头。\n可伤害已经造成。林磊的心早已彻底冷却,过往的裂痕无法修复,再也回不到从前的旧时光。\n王子也看清你的态度,收回大部分优待。\n一场盛大幻梦清醒之后,两边都已经失去。\n你只能带着这段刻骨铭心的经历,独自往前走。`,
                icon: '😵',
                hidden: true
            },
            G: {
                id: 'G',
                title: '🕷️ 黑寡妇 · 夺权 (隐藏)',
                preview: '你成功夺取王子的财富和地位,成为真正的赢家,却永远失去了爱的能力。',
                fullText: `你步步为营,让王子彻底沦陷,最终签署了股权转让协议。你成了王氏集团的实际掌控者,财富、地位、权力都握在手中。\n你站在摩天大楼顶层,俯瞰整座城市,嘴角浮起冷笑。王子被你掏空一切后颓然离开,你没有任何愧疚。\n你赢得了世界,但午夜梦回,你发现自己再也无法相信任何人,也再也不会心动了。\n这是一场冷冰冰的胜利,你成了真正的黑寡妇。`,
                icon: '🕷️',
                hidden: true
            },
            H: {
                id: 'H',
                title: '❤️ 王子真心 · 清醒结局',
                preview: '你在算计中假戏真做,最终放下了戒备,和王子真心相爱。',
                fullText: `你原本只想利用王子,却在日复一日的相处中,被他眼底的真诚融化。你开始怀疑自己的野心,怀疑那些物质是否真的比一个温暖的怀抱更重要。\n你试探他,他却说:"我早就知道你的目的,但我还是爱你。"\n你崩溃大哭,第一次卸下所有伪装。他紧紧抱住你,说:"我们一起面对。"\n你放弃夺权计划,把股份还给他。他反而把一部分资产转到你的名下,笑着说:"这是你应得的,因为你教会我什么是真爱。"\n你们举行了一场简单的婚礼,没有媒体,没有名流,只有彼此。你终于明白,最珍贵的不是财富,而是愿意为你放下一切的人。`,
                icon: '❤️',
                hidden: false
            },
            I: {
                id: 'I',
                title: '💔 黑寡妇 · 觉醒',
                preview: '你在最后一刻放弃阴谋,离开王子,带着一身疲惫重新开始。',
                fullText: `你手握转让协议,看着王子信任的眼神,突然崩溃。你撕碎协议,留下一句"我不配",转身离去。\n你失去了一切财富和地位,但找回了内心的平静。你开始学习独立生活,不再依附任何人,也不再算计任何爱。\n你搬进一间小公寓,找了份普通工作,每天挤地铁、买菜、做饭。偶尔路过奢侈品橱窗,你只是笑笑,不再心动。\n某天,你在街角看到王子牵着另一个女孩的手,他依然笑得温柔。你转身离开,心里没有嫉妒,只有释然。\n你终于明白,有些路走错了,及时回头就是新生。`,
                icon: '💔',
                hidden: false
            }
        };

        // ================================================================
        //  回忆数据 (不变)
        // ================================================================
        const MEMORIES = {
            'prologue': {
                title: '🌙 东南亚那个夜晚',
                text: '潮湿闷热的异国夜晚,巷口响起打斗声。劫匪亮出弹簧刀,危急关头,你大喊"Police!",把高仿香奈儿链条包奋力砸向歹徒。歹徒受惊逃窜,你救下被抢劫的男人——王子。\n\n后来,王子动用直升机邀请你前往私人岛屿答谢。烛光晚宴,他推来丝绒礼盒,璀璨的海蓝宝钻石项链静静躺在盒中。你在项链的诱惑,和手机锁屏林磊发来的消息"红烧肉放冰箱,项链帮你问到折扣"之间挣扎。'
            },
            'island': {
                title: '🏝️ 私人岛屿的烛光',
                text: '私人岛屿的烛光晚宴上,王子推来丝绒礼盒,璀璨的海蓝宝钻石项链静静躺在盒中。他说:"这只是件我觉得很衬你的小饰品。"\n\n你想起林磊发来的消息:"项链帮你问到折扣,比代购便宜一千二。"\n最终,你选择了拒绝。'
            },
            'apartment': {
                title: '🏙️ 江景公寓的诱惑',
                text: '深夜,一通陌生售楼处电话打了进来:"卉子小姐您好,王先生委托,为您预定柏悦府临江精品江景公寓,全套手续已经备好,您随时可以办理入住。"\n\n一套普通人奋斗一辈子都难以企及的江景大平层,就这么被当做"朋友的关怀"送到你面前。'
            },
            'linlei': {
                title: '🍖 林磊的红烧肉',
                text: '林磊在B市彻底断了联系,冰箱冷冻层还留着他临走前冻起来的一小碗红烧肉。\n某个深夜,情绪崩溃,你蹲在打开的冰箱门口,啃食那块冰冷冻硬的红烧肉,眼泪不停往下掉。\n门锁"咔哒"一声响动——林磊,连夜从B市项目赶回来了。'
            },
            'struggle': {
                title: '⚖️ 摇摆的泥潭',
                text: '你夹在两个男人之间,林磊的失望和王子的鲜花一样重。\n一面是六年相濡以沫的温情,一面是金光闪闪的捷径。\n你站在十字路口,迷茫又疲惫,连自己都不知道心到底偏向哪一边。'
            },
            'career': {
                title: '💼 职场孤光',
                text: '你屏蔽所有感情干扰,连续加班拿下年度大项目。庆功宴上觥筹交错,你成了最闪亮的新星。\n可散场后一个人站在街头,连个分享喜悦的人都没有。\n再坚强的女人,也想有个温暖的怀抱。'
            },
            'life': {
                title: '🏠 柴米油盐',
                text: '房租、水电、菜价……琐碎的现实磨平了浪漫。林磊依然会做红烧肉,但你们开始为未来发愁。\n路过LV橱窗,你驻足良久,心里那点虚荣的火焰,在烟火气中忽明忽灭。'
            },
            'fake': {
                title: '🎭 阴影下的妥协',
                text: '你表面顺从林磊,私下却留着王子的名片。\n王子直接来到你家楼下逼问,你惊慌失措。\n纸终究包不住火,选择的时候到了。'
            },
            'cage': {
                title: '🔒 镶金边的牢笼',
                text: '你住进了豪宅,却成了金丝雀。王子的女友圈排挤你,你连买个包都要看他的脸色。\n穿着高定礼服站在镜子前,你觉得镜中人陌生得可怕。'
            },
            'dinner': {
                title: '🍷 豪门晚宴',
                text: '王子带你出席家族晚宴,你被他的母亲上下打量。那些名媛窃窃私语,你像一件被估价的艺术品。\n你咬牙微笑,心里却在盘算如何利用这次机会。'
            },
            'jealousy': {
                title: '💔 前女友的阴影',
                text: '你发现王子手机里还存着前女友的照片,那是一个真正的名媛,气质、家世都碾压你。\n嫉妒像毒蛇缠绕你的心,你发誓要夺走王子所有的爱。'
            },
            'office': {
                title: '🏢 办公室的流言',
                text: '茶水间里,同事们在窃窃私语:"听说卉子攀上了王氏的公子哥,难怪最近升职这么快。"\n你端着咖啡站在门口,进退两难。'
            },
            'night': {
                title: '🌃 失眠的夜',
                text: '深夜,你辗转反侧,脑中不断回放林磊失望的眼神和王子的笑容。窗外霓虹闪烁,你却觉得无比孤独。'
            },
            'gift': {
                title: '🎁 昂贵的礼物',
                text: '王子送来一条限量版钻石手链,你拆开时心跳加速。可下一秒,你想起林磊为你煮的那碗廉价泡面,心里涌起一阵酸楚。'
            }
        };

        // ================================================================
        //  完整节点定义 (扩展至30+节点,每条路径至少12个选择)
        // ================================================================
        const NODES = {};

        // ---- 序章 ----
        NODES.prologue = {
            id: 'prologue',
            title: '序章 · 回忆碎片',
            text: `【回忆·东南亚那个夜晚】\n潮湿闷热的异国夜晚,巷口响起打斗声。劫匪亮出弹簧刀,危急关头,你大喊"Police!",把高仿香奈儿链条包奋力砸向歹徒。歹徒受惊逃窜,你救下被抢劫的男人——王子。\n\n后来,王子动用直升机邀请你前往私人岛屿答谢。\n烛光晚宴,他推来丝绒礼盒,璀璨的海蓝宝钻石项链静静躺在盒中。你在项链的诱惑,和手机锁屏林磊发来的消息"红烧肉放冰箱,项链帮你问到折扣"之间挣扎。\n你最终拒绝项链,但王子并没有就此放弃。\n\n回国之后,风暴才刚刚开始。\n回到城中村狭小隔板出租屋,墙壁薄薄的,隔壁邻居吵架声隐约传来。你的高仿Celine包边角已经磕破磨损。林磊看见王子派迈巴赫送你回家,二人爆发激烈争吵。林磊满心疲惫,留下半个月冷静期,动身前往B市出差,临走前告诉你:"这半个月不要联系,想清楚自己到底想要什么。"`,
            memoryKey: 'prologue',
            options: [{
                label: 'A',
                text: '回到现实,开启你的抉择',
                next: 'node1',
                values: { va: 0, le: 0, wi: 0 },
                hint: '开始故事'
            }]
        };

        // ---- 节点1:平静下的暗流 ----
        NODES.node1 = {
            id: 'node1',
            title: '🌊 平静下的暗流',
            text: `距离林磊负气离开已经过去四天。\n王子的攻势从未停止。公司前台天天收到送来的顶级下午茶、贵妇护肤品礼盒;你加班晚归,黑色轿车会等在公司楼下;行业酒会上,他轻飘飘一句话,就让公司领导对你格外客气,全公司流言四起,同事看你的眼神复杂又微妙。\n你把大部分礼物退回或者分给同事,可王子总能找到新的方式渗透你的生活。\n\n深夜,一通陌生售楼处电话打了进来。\n"卉子小姐您好,王先生委托,为您预定柏悦府临江精品江景公寓,全套手续已经备好,您随时可以办理入住。"\n一套普通人奋斗一辈子都难以企及的江景大平层,就这么被当做"朋友的关怀"送到你面前。\n手机屏幕亮起,紧跟着王子短信:"只是觉得你住的地方不安全,就当临时住处,别有负担。"`,
            memoryKey: 'apartment',
            options: [{
                label: 'A',
                text: '接受这套江景公寓',
                next: 'branchB',
                values: { va: 25, le: -40, wi: 30 },
                hint: '虚荣+25 磊磊-40 王子+30'
            }, {
                label: 'B',
                text: '口头敷衍,"谢谢您,我考虑一段时间"',
                next: 'node1_1',
                values: { va: 10, le: -20, wi: 15 },
                hint: '虚荣+10 磊磊-20 王子+15'
            }, {
                label: 'C',
                text: '坚决拒绝公寓,严词表明立场,拉黑王子全部联系方式',
                next: 'node1_2',
                values: { va: -20, le: 30, wi: -25 },
                hint: '虚荣-20 磊磊+30 王子-25'
            }]
        };

        // ---- 新增节点1_1:敷衍后的日常 ----
        NODES.node1_1 = {
            id: 'node1_1',
            title: '☕ 敷衍后的日常',
            text: `你婉拒了公寓,但王子并没有停止。每天下午茶、鲜花、限量版首饰依然源源不断。公司里关于你的流言愈演愈烈,甚至传到了总监耳朵里。\n你试图保持低调,但每当拆开那些精致的包装,你都能感到自己心跳加速——那是你从未拥有过的东西。\n林磊在B市毫无音讯,你给他发的消息石沉大海。深夜,你翻看旧照片,眼泪无声滑落。`,
            memoryKey: 'office',
            options: [{
                label: 'A',
                text: '主动给林磊打电话,却被他挂断',
                next: 'node1_1a',
                values: { va: -5, le: -10, wi: 5 },
                hint: '磊磊-10 王子+5'
            }, {
                label: 'B',
                text: '接受王子的邀请,出席一场私人晚宴',
                next: 'node1_1b',
                values: { va: 15, le: -5, wi: 10 },
                hint: '虚荣+15 王子+10'
            }, {
                label: 'C',
                text: '独自喝闷酒,然后发了一条含沙射影的朋友圈',
                next: 'node2',
                values: { va: 5, le: -5, wi: 0 },
                hint: '虚荣+5 磊磊-5'
            }]
        };

        // ---- node1_1a:被挂断电话 ----
        NODES.node1_1a = {
            id: 'node1_1a',
            title: '📱 被挂断的电话',
            text: `你鼓起勇气拨通了林磊的电话,响了两声就被挂断。你愣在原地,心像被钝器重击。\n你开始怀疑自己是不是真的失去了他。而此时,王子的短信恰好弹出:"明天有个画展,想请你一起,散散心。"`,
            memoryKey: 'night',
            options: [{
                label: 'A',
                text: '答应王子的邀约,用新欢麻醉自己',
                next: 'node1_1b',
                values: { va: 10, le: -10, wi: 10 },
                hint: '虚荣+10 王子+10'
            }, {
                label: 'B',
                text: '拒绝邀约,独自在家彻夜未眠',
                next: 'node2',
                values: { va: -5, le: 0, wi: -5 },
                hint: '虚荣-5 王子-5'
            }]
        };

        // ---- node1_1b:私人晚宴 ----
        NODES.node1_1b = {
            id: 'node1_1b',
            title: '🍷 豪门晚宴',
            text: `你穿上了王子派人送来的高级定制礼服,踏入了从未想象过的奢华世界。水晶吊灯、香槟塔、谈吐不凡的宾客……你像闯入了一个全新的宇宙。\n王子全程陪伴,介绍你时总是说"这位是我非常重视的朋友"。你感到一种前所未有的虚荣满足,但同时也隐隐听到周围名媛的窃窃私语。`,
            memoryKey: 'dinner',
            options: [{
                label: 'A',
                text: '享受这一切,不再想林磊',
                next: 'branchB',
                values: { va: 20, le: -15, wi: 15 },
                hint: '虚荣+20 王子+15'
            }, {
                label: 'B',
                text: '中途离场,跑到洗手间给林磊发了一条长消息',
                next: 'node2',
                values: { va: -10, le: 10, wi: -10 },
                hint: '虚荣-10 磊磊+10'
            }]
        };

        // ---- 新增节点1_2:坚决拒绝后的反应 ----
        NODES.node1_2 = {
            id: 'node1_2',
            title: '🗡️ 拒绝之后的余波',
            text: `你拉黑了王子,删除了所有联系方式。第二天,公司里突然传出你要被调去边缘部门的消息,很明显是有人在背后施压。\n同事们看你的眼神变得暧昧,有人同情,有人幸灾乐祸。你咬着牙,不知道自己的坚持还能撑多久。`,
            memoryKey: 'office',
            options: [{
                label: 'A',
                text: '向林磊倾诉,求他立刻回来',
                next: 'node1_2a',
                values: { va: -10, le: 15, wi: -5 },
                hint: '虚荣-10 磊磊+15'
            }, {
                label: 'B',
                text: '找王子质问,要求他不要干涉你的工作',
                next: 'node1_2b',
                values: { va: 5, le: -10, wi: 10 },
                hint: '虚荣+5 王子+10'
            }, {
                label: 'C',
                text: '隐忍,装作无事发生,加倍努力工作',
                next: 'node_career',
                values: { va: 5, le: 0, wi: -10 },
                hint: '虚荣+5 王子-10'
            }]
        };

        // ---- node1_2a:向林磊求助 ----
        NODES.node1_2a = {
            id: 'node1_2a',
            title: '📞 深夜求救',
            text: `你哭着给林磊留言,把工作危机和盘托出。凌晨三点,林磊回复了:"我明天请假回来,你撑住。"\n你握着手机,泪如雨下。但心里也清楚,这份感情已经千疮百孔。`,
            memoryKey: 'linlei',
            options: [{
                label: 'A',
                text: '等他回来,好好谈谈',
                next: 'node3',
                values: { va: -5, le: 10, wi: -5 },
                hint: '磊磊+10'
            }, {
                label: 'B',
                text: '觉得自己配不上他,回绝他的好意',
                next: 'node4_alone',
                values: { va: 5, le: -10, wi: 0 },
                hint: '虚荣+5 磊磊-10'
            }]
        };

        // ---- node1_2b:质问王子 ----
        NODES.node1_2b = {
            id: 'node1_2b',
            title: '⚡ 对峙王子',
            text: `你怒气冲冲地找到王子,质问他为什么要干扰你的工作。王子淡淡地笑了:"我只是想让你明白,这个世界靠实力说话。而实力,我恰好有。"\n你愣住了,随即感到一股寒意。他的温柔背后,是锋利的控制欲。`,
            memoryKey: 'apartment',
            options: [{
                label: 'A',
                text: '屈服,接受他的安排',
                next: 'branchB',
                values: { va: 20, le: -20, wi: 20 },
                hint: '虚荣+20 王子+20'
            }, {
                label: 'B',
                text: '绝地反击,靠自己争取机会',
                next: 'node_career',
                values: { va: -5, le: 10, wi: -10 },
                hint: '磊磊+10 王子-10'
            }]
        };

        // ---- 节点2:摇摆的煎熬 (入口) ----
        NODES.node2 = {
            id: 'node2',
            title: '🌀 摇摆的煎熬',
            text: `你没有直接收下公寓,也没有彻底拒绝王子。\n你委婉回复需要考虑。王子没有逼迫,但攻势并未停下。音乐会门票、艺术沙龙邀请源源不断送到你手上。\n你偶尔会出席王子安排的社交场合,见识衣香鬓影的上层圈子,那些珠宝华服不断撩拨你心底的虚荣心。但每当深夜回到城中村出租屋,看着空荡荡房间,又会想起林磊做的红烧肉。\n林磊在B市,彻底断了联系,没有一条消息。冰箱冷冻层,还留着他临走前冻起来的一小碗红烧肉。\n某个深夜,情绪崩溃,你蹲在打开的冰箱门口,啃食那块冰冷冻硬的红烧肉,眼泪不停往下掉。门锁"咔哒"一声响动——林磊,连夜从B市项目赶回来了。风尘仆仆,眼底浓重黑眼圈,手里拎着超市泡面塑料袋。\n他看见蹲在冰箱前,满嘴油渍、泪流满面的你。\n林磊声音沙哑:"你又在折腾什么。姓王的,还在纠缠你?"`,
            memoryKey: 'linlei',
            options: [{
                label: 'A',
                text: '向林磊诉苦,坦白王子送公寓,但不想彻底斩断和王子往来',
                next: 'node2_1',
                values: { va: 10, le: -25, wi: 10 },
                hint: '虚荣+10 磊磊-25 王子+10'
            }, {
                label: 'B',
                text: '借着此刻情绪下定决心,跟林磊坦白全部内心,愿意彻底和王子切割',
                next: 'node2_1a',
                values: { va: -15, le: 20, wi: -20 },
                hint: '虚荣-15 磊磊+20 王子-20'
            }, {
                label: 'C',
                text: '谁都不想依靠,情爱暂且放下,只想专注自己事业',
                next: 'node_career',
                values: { va: -25, le: -10, wi: -30 },
                hint: '虚荣-25 磊磊-10 王子-30'
            }]
        };

        // ---- 节点2_1:进退两难 ----
        NODES.node2_1 = {
            id: 'node2_1',
            title: '⚖️ 进退两难的泥潭',
            text: `林磊的失望像钝刀子割肉,他不再争吵,只是沉默地看着你。王子的短信依然准时,甚至开始关心你的胃病。\n你夹在两个男人之间,林磊的失望和王子的鲜花一样重。深夜,你独自面对空荡荡的房间,心里乱成一团麻。\n你明白,再这样拖延下去,只会把两个人都伤透。`,
            memoryKey: 'struggle',
            options: [{
                label: 'A',
                text: '追回林磊,彻底割舍王子',
                next: 'node2_1a',
                values: { va: -10, le: 15, wi: -15 },
                hint: '虚荣-10 磊磊+15 王子-15'
            }, {
                label: 'B',
                text: '接受王子的世界,放手林磊',
                next: 'branchB',
                values: { va: 15, le: -20, wi: 15 },
                hint: '虚荣+15 磊磊-20 王子+15'
            }, {
                label: 'C',
                text: '原地不动,听天由命',
                next: 'node4_passive',
                values: { va: 5, le: -5, wi: 0 },
                hint: '虚荣+5 磊磊-5'
            }]
        };

        // ---- 新增节点2_1a:坚定切割后的缓冲 ----
        NODES.node2_1a = {
            id: 'node2_1a',
            title: '✂️ 切割的代价',
            text: `你当着林磊的面删除了王子的所有联系方式,并发了一条决绝的短信。林磊没有说话,只是紧紧抱住了你。\n但接下来的几天,你开始疯狂想念那些奢侈品的精美包装,甚至偷偷搜索王子的近况。你发现自己根本戒不掉那种诱惑。`,
            memoryKey: 'gift',
            options: [{
                label: 'A',
                text: '强迫自己回归平凡,和林磊一起规划未来',
                next: 'node3_life',
                values: { va: -15, le: 20, wi: -20 },
                hint: '虚荣-15 磊磊+20 王子-20'
            }, {
                label: 'B',
                text: '偷偷联系王子,说"只是做个朋友"',
                next: 'node3_fake',
                values: { va: 20, le: -25, wi: 20 },
                hint: '虚荣+20 磊磊-25 王子+20'
            }]
        };

        // ---- 节点4_passive:被动等待 ----
        NODES.node4_passive = {
            id: 'node4_passive',
            title: '⏳ 被动的代价',
            text: `你选择什么都不做,任由两个男人在你生命里来来去去。\n林磊终于耗尽最后一点耐心,他收拾行李,正式搬离了城中村。你看着空了一半的衣柜,心里空落落的。\n王子则更加主动,他开始安排你的生活,甚至替你辞了工作,让你搬到他的私人别墅。\n你像一片浮萍,被命运的浪潮推着走。直到有一天,你发现自己完全失去了选择的权利。`,
            options: [{
                label: 'A',
                text: '惊觉自己成了傀儡,奋力挣脱',
                next: 'node3',
                values: { va: -10, le: 10, wi: -20 },
                hint: '虚荣-10 磊磊+10 王子-20'
            }, {
                label: 'B',
                text: '既然反抗不了,那就享受吧',
                next: 'branchB_life',
                values: { va: 20, le: -10, wi: 15 },
                hint: '虚荣+20 王子+15'
            }, {
                label: 'C',
                text: '偷偷联系林磊,想挽回',
                next: 'node3_fake',
                values: { va: -5, le: 15, wi: -10 },
                hint: '磊磊+15 王子-10'
            }]
        };

        // ---- 节点_career:职场孤光 (已有) ----
        NODES.node_career = {
            id: 'node_career',
            title: '💼 职场孤光',
            text: `你屏蔽所有感情干扰,连续加班拿下年度大项目。庆功宴上觥筹交错,你成了最闪亮的新星。\n可散场后一个人站在街头,连个分享喜悦的人都没有。\n再坚强的女人,也想有个温暖的怀抱。你望着手机里林磊的头像,指尖悬停。`,
            memoryKey: 'career',
            options: [{
                label: 'A',
                text: '享受孤独,事业就是我的底气',
                next: 'endingE',
                values: { va: -10, le: 0, wi: -10 },
                hint: '虚荣-10 王子-10'
            }, {
                label: 'B',
                text: '再坚强的女人也想有个家',
                next: 'node3',
                values: { va: -5, le: 10, wi: -5 },
                hint: '磊磊+10'
            }]
        };

        // ---- 节点3:深夜的意外归来 ----
        NODES.node3 = {
            id: 'node3',
            title: '🌙 深夜的意外归来',
            text: `深夜出租屋,灯光昏黄。\n林磊风尘仆仆站在门口,刚刚连夜从B市赶回。他看见狼狈哭泣的你,一把夺下你手里那碗冻红烧肉。\n他没有一味发火,拿热毛巾擦干净你的脸,给你套上厚厚的袜子。\n"卉子,我再问你一次。抛开房子、珠宝、别人羡慕的眼光。你内心真正想要什么?"\n过去六年一幕幕浮现在脑海:大学门口五块钱一碗的麻辣烫;刚工作挤潮湿地下室;他明明吐槽你买假货,却悄悄帮你研究辨别高仿;记得你看中的那条珍珠项链,拼命打听内部折扣。\n王子给你全世界最昂贵的礼物,可那些馈赠背后,是居高临下的施舍,把你当成一件精致藏品。\n现在,轮到你说出心底真实想法。`,
            memoryKey: 'linlei',
            options: [{
                label: 'A',
                text: '坦诚所有纠结:我选择你,选择烟火平凡的生活',
                next: 'node3_life',
                values: { va: -15, le: 35, wi: -30 },
                hint: '虚荣-15 磊磊+35 王子-30'
            }, {
                label: 'B',
                text: '内心疲惫,拒绝王子,但暂时不敢复合,需要独处时间',
                next: 'node4_alone',
                values: { va: -10, le: -15, wi: -20 },
                hint: '虚荣-10 磊磊-15 王子-20'
            }, {
                label: 'C',
                text: '嘴上向林磊妥协,心里依旧放不下豪门世界的诱惑',
                next: 'node3_fake',
                values: { va: 15, le: -30, wi: 20 },
                hint: '虚荣+15 磊磊-30 王子+20'
            }]
        };

        // ---- 新增节点3_a:复合初期的磨合 ----
        NODES.node3_a = {
            id: 'node3_a',
            title: '🌱 复合初期的磨合',
            text: `你和林磊决定重新开始,但裂痕依然存在。他变得有些敏感,经常检查你的手机。你感到窒息,却又不敢发火。\n你开始怀念王子给你的自由——那种物质上的予取予求。\n一天,你在商场偶遇王子,他身边站着另一个女孩,笑靥如花。你心里莫名绞痛。`,
            memoryKey: 'jealousy',
            options: [{
                label: 'A',
                text: '主动上前打招呼,故作大方',
                next: 'node3_fake',
                values: { va: 10, le: -10, wi: 10 },
                hint: '虚荣+10 王子+10'
            }, {
                label: 'B',
                text: '默默走开,回家后对林磊发脾气',
                next: 'node3_life_a',
                values: { va: -5, le: -10, wi: 0 },
                hint: '磊磊-10'
            }, {
                label: 'C',
                text: '冷静反思,意识到自己依然被虚荣心控制',
                next: 'node4_alone',
                values: { va: -5, le: 5, wi: -5 },
                hint: '磊磊+5'
            }]
        };

        // ---- 节点3_life:柴米油盐的考验 ----
        NODES.node3_life = {
            id: 'node3_life',
            title: '🏠 柴米油盐的考验',
            text: `你们终于决定在一起,开始真正的同居生活。\n他加班到很晚,你为柴米油盐焦虑。橱窗里的奢侈品依然诱人,而你们银行卡里的余额却捉襟见肘。\n林磊依然会做红烧肉,但你们开始为未来发愁。路过LV橱窗,你驻足良久。\n烟火气的日子,并不总是浪漫,更多是琐碎的摩擦。`,
            memoryKey: 'life',
            options: [{
                label: 'A',
                text: '和他一起奋斗,知足常乐',
                next: 'node3_life_a',
                values: { va: -10, le: 15, wi: -10 },
                hint: '虚荣-10 磊磊+15 王子-10'
            }, {
                label: 'B',
                text: '这种日子太苦,想逃回舒适区',
                next: 'node3_fake',
                values: { va: 10, le: -10, wi: 5 },
                hint: '虚荣+10 磊磊-10'
            }]
        };

        // ---- 新增节点3_life_a:经济压力 ----
        NODES.node3_life_a = {
            id: 'node3_life_a',
            title: '💸 经济压力下的争执',
            text: `你们因为一笔意外开支大吵一架。林磊说你乱花钱,你嫌他抠门。冷战持续了两天。\n你开始偷偷翻看王子送你的旧照片,那些奢华时光像毒品一样引诱着你。\n林磊发现了你的小动作,眼神黯淡。`,
            memoryKey: 'life',
            options: [{
                label: 'A',
                text: '主动求和,承认错误',
                next: 'node5_future',
                values: { va: -5, le: 10, wi: -5 },
                hint: '磊磊+10'
            }, {
                label: 'B',
                text: '把照片删除,但心里依然不甘',
                next: 'node5_future_a',
                values: { va: 5, le: -5, wi: 5 },
                hint: '虚荣+5 王子+5'
            }]
        };

        // ---- 节点5_future:未来规划 ----
        NODES.node5_future = {
            id: 'node5_future',
            title: '📈 未来的蓝图',
            text: `你们开始认真规划未来:存钱买房、计划旅行、甚至讨论结婚。林磊努力工作,你也升了职。\n但某天,王子突然出现在你公司楼下,他看起来憔悴了许多,说想最后和你谈谈。\n他说他愿意放弃一切,只要你愿意回到他身边。\n你看着林磊发来的"今晚想吃什么",心里掀起巨浪。`,
            options: [{
                label: 'A',
                text: '毫不犹豫地拒绝王子,回到林磊身边',
                next: 'node5_future_b',
                values: { va: -20, le: 20, wi: -30 },
                hint: '虚荣-20 磊磊+20 王子-30'
            }, {
                label: 'B',
                text: '心里有些动摇,答应和王子喝杯咖啡',
                next: 'endingC',
                values: { va: 15, le: -20, wi: 20 },
                hint: '虚荣+15 磊磊-20 王子+20'
            }]
        };

        // ---- 新增节点5_future_a:心存不甘 ----
        NODES.node5_future_a = {
            id: 'node5_future_a',
            title: '💔 暗流涌动',
            text: `你表面上和林磊和好,但心里那团火从未熄灭。你开始偷偷关注王子的社交账号,甚至注册了小号去点赞。\n林磊察觉到你的心不在焉,开始频繁查岗。你们的关系变得紧张而脆弱。`,
            memoryKey: 'fake',
            options: [{
                label: 'A',
                text: '破罐子破摔,直接去找王子',
                next: 'branchB',
                values: { va: 25, le: -30, wi: 25 },
                hint: '虚荣+25 王子+25'
            }, {
                label: 'B',
                text: '悬崖勒马,对林磊坦白一切',
                next: 'node5_future_b',
                values: { va: -15, le: 15, wi: -15 },
                hint: '磊磊+15 王子-15'
            }]
        };

        // ---- 新增节点5_future_b:最终考验 ----
        NODES.node5_future_b = {
            id: 'node5_future_b',
            title: '🔥 最后的考验',
            text: `你告诉林磊王子来找过你,并坦承自己曾经动摇过。林磊沉默了很久,最后说:"我尊重你的选择,但我不会再等第二次。"\n你感到前所未有的恐惧——你真的要失去他了。同时,王子还在楼下等着你的答复。`,
            options: [{
                label: 'A',
                text: '跑下楼,对王子说"对不起,我选林磊"',
                next: 'endingA',
                values: { va: -20, le: 30, wi: -30 },
                hint: '虚荣-20 磊磊+30 王子-30'
            }, {
                label: 'B',
                text: '不追王子,也不挽回林磊,独自离开',
                next: 'endingD',
                values: { va: -10, le: -10, wi: -10 },
                hint: '各减10'
            }]
        };

        // ---- 节点3_fake:阴影下的妥协 ----
        NODES.node3_fake = {
            id: 'node3_fake',
            title: '🎭 阴影下的妥协',
            text: `你表面顺从林磊,私下却留着王子的名片。你偷偷回复了王子的消息,约在咖啡馆见面。\n王子发现你的摇摆,直接来到你家楼下逼问。林磊无意间看到你的手机,眼神从震惊变成死寂。\n纸终究包不住火,你被当场戳穿,站在两个男人之间,无处可逃。`,
            memoryKey: 'fake',
            options: [{
                label: 'A',
                text: '惊慌失措,彻底投向王子',
                next: 'branchB',
                values: { va: 20, le: -30, wi: 25 },
                hint: '虚荣+20 磊磊-30 王子+25'
            }, {
                label: 'B',
                text: '删除名片,跪求林磊原谅',
                next: 'node5_future_b',
                values: { va: -20, le: 25, wi: -20 },
                hint: '虚荣-20 磊磊+25 王子-20'
            }, {
                label: 'C',
                text: '干脆两个都不要,自己过',
                next: 'endingE',
                values: { va: -5, le: -10, wi: -15 },
                hint: '虚荣-5 磊磊-10 王子-15'
            }]
        };

        // ---- 分支B:金丝雀牢笼 (入口) ----
        NODES.branchB = {
            id: 'branchB',
            title: '🏛️ 金丝雀牢笼',
            text: `你收下柏悦府江景公寓钥匙。\n你搬离拥挤城中村,落地窗俯瞰整条城市江景。源源不断高定服饰、珠宝送到家门口。王子带你出入私人酒会、游艇出海,你终于过上曾经梦寐以求的光鲜日子。\n你朋友圈全是精致度假、奢侈品照片,无数人羡慕你一步登天。\n代价接踵而至。\n林磊得知你收下豪宅,彻底心碎,微信电话全部拉黑,再也不会回来给你做一碗红烧肉。\n豪门圈子的规则冰冷锋利。你背着那些真金白银的名牌,可骨子里的出身与过往始终格格不入。名媛圈子私下对你议论纷纷,觉得你只是一场"救命之恩"换来的玩物。\n王子对你的新鲜感慢慢消退。他依旧对你物质大方,但尊重越来越少。很多决定,不需要询问你的意见就直接安排。你拥有一切物质,却没有话语权。`,
            memoryKey: 'apartment',
            options: [{
                label: 'A',
                text: '安心接受金丝雀的命运,习惯依附王子的生活',
                next: 'branchB_a',
                values: { va: 20, le: 0, wi: 15 },
                hint: '虚荣+20 王子+15'
            }, {
                label: 'B',
                text: '拥有一切之后幡然醒悟,厌恶被物化的人生,想要逃离',
                next: 'node6_escape',
                values: { va: -15, le: 10, wi: -20 },
                hint: '虚荣-15 磊磊+10 王子-20'
            }, {
                label: 'C',
                text: '我要反过来利用王子的感情,夺取一切',
                next: 'node6_black',
                values: { va: 25, le: -10, wi: 30 },
                hint: '虚荣+25 王子+30'
            }]
        };

        // ---- 新增 branchB_a:奢华背后的孤独 ----
        NODES.branchB_a = {
            id: 'branchB_a',
            title: '💎 奢华背后的孤独',
            text: `你住进了豪宅,却越来越空虚。王子忙于生意,经常留你一个人。你开始用购物填补寂寞,但每一个新包都只能带来短暂的快乐。\n你尝试融入名媛圈,却总是被当作"暴发户"嘲笑。你甚至开始怀念城中村的嘈杂——至少那里有人气。`,
            memoryKey: 'cage',
            options: [{
                label: 'A',
                text: '继续沉溺,用更多奢侈品麻痹自己',
                next: 'branchB_b',
                values: { va: 15, le: -5, wi: 10 },
                hint: '虚荣+15 王子+10'
            }, {
                label: 'B',
                text: '开始暗中筹划,想摆脱这种生活',
                next: 'node6_escape',
                values: { va: -10, le: 5, wi: -10 },
                hint: '磊磊+5 王子-10'
            }]
        };

        // ---- 新增 branchB_b:前女友出现 ----
        NODES.branchB_b = {
            id: 'branchB_b',
            title: '💢 前女友的阴影',
            text: `你发现王子还和前女友保持联系,那是一个真正的名媛,气质、家世都碾压你。你质问王子,他却说:"她只是我的商业伙伴。"\n你感到被羞辱,却又不敢发作,因为你的一切都依赖他。`,
            memoryKey: 'jealousy',
            options: [{
                label: 'A',
                text: '忍气吞声,假装大度',
                next: 'branchB_life',
                values: { va: 5, le: 0, wi: 5 },
                hint: '王子+5'
            }, {
                label: 'B',
                text: '发疯般大闹,王子开始疏远你',
                next: 'node6_escape',
                values: { va: -10, le: -5, wi: -10 },
                hint: '各减'
            }, {
                label: 'C',
                text: '化嫉妒为动力,决心夺走他的一切',
                next: 'node6_black',
                values: { va: 20, le: -10, wi: 20 },
                hint: '虚荣+20 王子+20'
            }]
        };

        // ---- branchB_life:镶金边的牢笼 ----
        NODES.branchB_life = {
            id: 'branchB_life',
            title: '🔒 镶金边的牢笼',
            text: `你住进了豪宅,却成了真正的金丝雀。王子的朋友揶揄你是"东南亚捡来的灰姑娘",名媛圈排挤你,你连买个包都要看他的脸色。\n你穿着高定礼服站在镜子前,觉得镜中人陌生得可怕。\n午夜梦回,你想起城中村那碗热乎的红烧肉,想起林磊嫌弃又宠溺的眼神。可一切都回不去了。`,
            memoryKey: 'cage',
            options: [{
                label: 'A',
                text: '用钱填满空虚,不回头',
                next: 'endingB',
                values: { va: 15, le: -5, wi: 10 },
                hint: '虚荣+15 王子+10'
            }, {
                label: 'B',
                text: '后悔莫及,逃离这个笼子',
                next: 'endingF',
                values: { va: -20, le: 5, wi: -20 },
                hint: '虚荣-20 王子-20'
            }, {
                label: 'C',
                text: '假意顺从,暗中谋划夺权',
                next: 'node6_black',
                values: { va: 10, le: 0, wi: 15 },
                hint: '虚荣+10 王子+15'
            }]
        };

        // ---- 节点6_escape:逃离金笼 ----
        NODES.node6_escape = {
            id: 'node6_escape',
            title: '🏃 逃离金笼',
            text: `你开始偷偷联系林磊,想挽回那段感情。但林磊已经心死,他只回了一句:"各走各路吧。"\n你崩溃大哭,发现自己两头落空。王子得知你在联络前男友,愤怒地收回了大部分馈赠。\n你站在空荡荡的公寓里,第一次感到真正的贫穷——不是物质上的,而是精神上的。`,
            options: [{
                label: 'A',
                text: '放下自尊,求王子原谅',
                next: 'endingB',
                values: { va: 10, le: -10, wi: 15 },
                hint: '虚荣+10 王子+15'
            }, {
                label: 'B',
                text: '彻底离开,靠自己重新开始',
                next: 'endingD',
                values: { va: -20, le: 5, wi: -25 },
                hint: '虚荣-20 王子-25'
            }]
        };

        // ---- 节点6_black:黑寡妇之路 ----
        NODES.node6_black = {
            id: 'node6_black',
            title: '🕸️ 黑寡妇之路',
            text: `你决定不再做金丝雀,你要做捕猎者。\n你开始精心研究王子的喜好、软肋,甚至他的商业布局。你利用自己的美貌和智慧,一步步取得他的信任。\n你陪他出席各种高端场合,表现得体大方,赢得了他家族长辈的赞赏。\n王子越来越依赖你,甚至开始把一些核心业务交给你打理。\n你表面温柔,内心冷酷,每一步都在计算。`,
            options: [{
                label: 'A',
                text: '继续演戏,等时机成熟吞掉他的家产',
                next: 'node6_black_a',
                values: { va: 30, le: -10, wi: 30 },
                hint: '虚荣+30 王子+30'
            }, {
                label: 'B',
                text: '在这个过程中,你竟然假戏真做爱上了他',
                next: 'node7_love',
                values: { va: -10, le: 0, wi: 20 },
                hint: '王子+20'
            }]
        };

        // ---- 新增 node6_black_a:窃取机密 ----
        NODES.node6_black_a = {
            id: 'node6_black_a',
            title: '📂 窃取机密',
            text: `你趁王子醉酒,偷偷拷贝了他电脑里的核心商业文件。你心跳如雷,但告诉自己这是为了"翻身"。\n第二天,王子毫无察觉,还温柔地给你带了早餐。你看着他的脸,有一瞬间的恍惚。`,
            options: [{
                label: 'A',
                text: '继续,绝不能被感情左右',
                next: 'node6_black_b',
                values: { va: 20, le: 0, wi: 20 },
                hint: '虚荣+20 王子+20'
            }, {
                label: 'B',
                text: '犹豫了,把文件放回去',
                next: 'node7_love',
                values: { va: -15, le: 5, wi: -10 },
                hint: '虚荣-15 磊磊+5'
            }]
        };

        // ---- 新增 node6_black_b:布局 ----
        NODES.node6_black_b = {
            id: 'node6_black_b',
            title: '🎯 布局',
            text: `你开始利用窃取的信息,在王子背后布局。你暗中联系他的竞争对手,为自己铺路。\n王子依然蒙在鼓里,他计划带你去见他的父母,说想和你订婚。\n你表面惊喜,心里却在盘算,订婚意味着更大的信任,也意味着更大的筹码。`,
            options: [{
                label: 'A',
                text: '答应订婚,进一步获得他的资产控制权',
                next: 'node7_heiress',
                values: { va: 25, le: -10, wi: 25 },
                hint: '虚荣+25 王子+25'
            }, {
                label: 'B',
                text: '在订婚仪式上,突然良心发现,逃离现场',
                next: 'endingI',
                values: { va: -20, le: 10, wi: -20 },
                hint: '觉醒结局'
            }]
        };

        // ---- 节点7_heiress:女继承人 ----
        NODES.node7_heiress = {
            id: 'node7_heiress',
            title: '💎 女继承人的野心',
            text: `你成功让王子把部分股权转到你名下,你成了公司董事。\n王子对你毫无防备,甚至开始计划求婚。\n你看着他的眼神,心里只有胜利的喜悦。\n你约他烛光晚餐,准备在浪漫氛围中让他签下最后一份转让协议。`,
            options: [{
                label: 'A',
                text: '收下一切,然后甩掉他,独自享受财富',
                next: 'endingG',
                values: { va: 40, le: -20, wi: 40 },
                hint: '虚荣+40 王子+40'
            }, {
                label: 'B',
                text: '最后一刻心软,放弃了阴谋',
                next: 'endingI',
                values: { va: -20, le: 20, wi: -20 },
                hint: '虚荣-20 磊磊+20 王子-20'
            }]
        };

        // ---- 节点7_love:假戏真做 ----
        NODES.node7_love = {
            id: 'node7_love',
            title: '❤️ 假戏真做',
            text: `你发现自己真的爱上了王子。他为你做的每一件事,都让你心动。你开始患得患失,担心他会不会只是玩玩。\n你试探性地问他:"你愿意为了我放弃整个商业帝国吗?"\n他认真地看着你:"只要你愿意,我可以。"\n你哭了,第一次不是因为算计,而是因为感动。`,
            options: [{
                label: 'A',
                text: '相信他的真心,和他一起过平凡但相爱的日子',
                next: 'endingH',
                values: { va: -30, le: 10, wi: 30 },
                hint: '虚荣-30 王子+30'
            }, {
                label: 'B',
                text: '依然怀疑他的动机,继续试探',
                next: 'endingF',
                values: { va: -10, le: -5, wi: 10 },
                hint: '王子+10'
            }]
        };

        // ---- 节点4_alone:独处时光 ----
        NODES.node4_alone = {
            id: 'node4_alone',
            title: '🧘 独处的时光',
            text: `你谢绝了所有人的关心,搬到了公司附近一间小公寓,打算冷静一段时间。\n你开始重新审视自己的欲望:那些名牌、珠宝,究竟是你真的需要,还是只是为了填补内心的空洞?\n你报名了陶艺课,试图用双手创造一些真实的东西。林磊偶尔发来问候,你都礼貌回复。\n王子依然每天送花,但你没有拆开。\n某天,你路过那家熟悉的LV橱窗,忽然发现,那些闪烁的logo再也激不起你心中的波澜。`,
            options: [{
                label: 'A',
                text: '彻底放下物欲,回归本真',
                next: 'endingD',
                values: { va: -25, le: 5, wi: -15 },
                hint: '虚荣-25 磊磊+5 王子-15'
            }, {
                label: 'B',
                text: '你只是暂时逃避,内心依然渴望奢华',
                next: 'node3_fake',
                values: { va: 15, le: -10, wi: 20 },
                hint: '虚荣+15 王子+20'
            }]
        };

        // ---- 结局节点 (全部保留) ----
        NODES.endingA = {
            id: 'endingA',
            title: '⭐ 烟火余生 · 真结局',
            text: ENDINGS.A.fullText,
            isEnding: true,
            endingId: 'A',
            options: []
        };
        NODES.endingB = {
            id: 'endingB',
            title: '💔 金丝雀牢笼 · 悲剧结局',
            text: ENDINGS.B.fullText,
            isEnding: true,
            endingId: 'B',
            options: []
        };
        NODES.endingC = {
            id: 'endingC',
            title: '🌫️ 孑然摇摆 · 普通结局',
            text: ENDINGS.C.fullText,
            isEnding: true,
            endingId: 'C',
            options: []
        };
        NODES.endingD = {
            id: 'endingD',
            title: '✨ 独自成长 · 开放结局',
            text: ENDINGS.D.fullText,
            isEnding: true,
            endingId: 'D',
            options: []
        };
        NODES.endingE = {
            id: 'endingE',
            title: '🔍 清醒抽身 · 搞事业 (隐藏)',
            text: ENDINGS.E.fullText,
            isEnding: true,
            endingId: 'E',
            options: []
        };
        NODES.endingF = {
            id: 'endingF',
            title: '😵 短暂幻梦 · 隐藏结局',
            text: ENDINGS.F.fullText,
            isEnding: true,
            endingId: 'F',
            options: []
        };
        NODES.endingG = {
            id: 'endingG',
            title: '🕷️ 黑寡妇 · 夺权 (隐藏)',
            text: ENDINGS.G.fullText,
            isEnding: true,
            endingId: 'G',
            options: []
        };
        NODES.endingH = {
            id: 'endingH',
            title: '❤️ 王子真心 · 清醒结局',
            text: ENDINGS.H.fullText,
            isEnding: true,
            endingId: 'H',
            options: []
        };
        NODES.endingI = {
            id: 'endingI',
            title: '💔 黑寡妇 · 觉醒',
            text: ENDINGS.I.fullText,
            isEnding: true,
            endingId: 'I',
            options: []
        };

        // ================================================================
        //  游戏引擎 (与之前相同,省略详细注释)
        // ================================================================

        const state = {
            va: 45,
            le: 60,
            wi: 30,
            currentNode: 'prologue',
            history: [],
            unlockedEndings: [],
            soundOn: true,
            isTyping: false,
            typingTimer: null,
            memoryShowing: false,
            nodeQueue: [],
        };

        const storyTextEl = document.getElementById('storyText');
        const storyTitleEl = document.getElementById('storyTitle');
        const optionsArea = document.getElementById('optionsArea');
        const memoryTrigger = document.getElementById('memoryTrigger');
        const memoryPopup = document.getElementById('memoryPopup');
        const memTitle = document.getElementById('memTitle');
        const memText = document.getElementById('memText');
        const memClose = document.getElementById('memClose');
        const galleryModal = document.getElementById('galleryModal');
        const galleryGrid = document.getElementById('galleryGrid');
        const galleryClose = document.getElementById('galleryClose');
        const btnSound = document.getElementById('btnSound');
        const btnGallery = document.getElementById('btnGallery');
        const btnRestart = document.getElementById('btnRestart');
        const valVa = document.getElementById('valVa');
        const valLe = document.getElementById('valLe');
        const valWi = document.getElementById('valWi');
        const storyCard = document.getElementById('storyCard');
        const mainContent = document.getElementById('mainContent');

        let audioCtx = null;

        function initAudio() {
            if (!audioCtx) {
                try {
                    audioCtx = new(window.AudioContext || window.webkitAudioContext)();
                } catch (e) { audioCtx = null; }
            }
            if (audioCtx && audioCtx.state === 'suspended') {
                audioCtx.resume().catch(() => {});
            }
            return audioCtx;
        }

        function playTone(freq, duration, type = 'sine', volume = 0.12) {
            if (!state.soundOn) return;
            const ctx = initAudio();
            if (!ctx) return;
            try {
                const osc = ctx.createOscillator();
                const gain = ctx.createGain();
                osc.type = type;
                osc.frequency.value = freq;
                gain.gain.setValueAtTime(volume, ctx.currentTime);
                gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + duration);
                osc.connect(gain);
                gain.connect(ctx.destination);
                osc.start(ctx.currentTime);
                osc.stop(ctx.currentTime + duration);
            } catch (_) {}
        }

        function playPageTurn() {
            playTone(280, 0.18, 'sine', 0.06);
            setTimeout(() => playTone(320, 0.12, 'sine', 0.04), 80);
        }

        function playClick() {
            playTone(600, 0.08, 'sine', 0.10);
            setTimeout(() => playTone(800, 0.06, 'sine', 0.07), 60);
        }

        function playEndingMusic(endingId) {
            if (!state.soundOn) return;
            const map = {
                'A': { freq: 440, dur: 0.6 },
                'B': { freq: 220, dur: 0.8 },
                'C': { freq: 330, dur: 0.5 },
                'D': { freq: 523, dur: 0.5 },
                'E': { freq: 587, dur: 0.5 },
                'F': { freq: 392, dur: 0.7 },
                'G': { freq: 200, dur: 0.9 },
                'H': { freq: 480, dur: 0.7 },
                'I': { freq: 310, dur: 0.8 }
            };
            const cfg = map[endingId] || { freq: 440, dur: 0.5 };
            playTone(cfg.freq, cfg.dur, 'sine', 0.08);
            setTimeout(() => playTone(cfg.freq * 1.25, cfg.dur * 0.6, 'sine', 0.05), 200);
        }

        function typeText(text, callback) {
            if (state.isTyping) {
                clearTimeout(state.typingTimer);
                state.isTyping = false;
                storyTextEl.textContent = text;
                storyTextEl.classList.remove('cursor-blink');
                if (callback) callback();
                return;
            }
            state.isTyping = true;
            storyTextEl.textContent = '';
            let index = 0;
            const chars = text.split('');
            storyTextEl.classList.remove('cursor-blink');

            function typeChar() {
                if (index < chars.length) {
                    storyTextEl.textContent += chars[index];
                    index++;
                    const delay = 20 + Math.random() * 25;
                    state.typingTimer = setTimeout(typeChar, delay);
                } else {
                    state.isTyping = false;
                    storyTextEl.classList.remove('cursor-blink');
                    if (callback) callback();
                }
            }
            typeChar();
        }

        function skipType() {
            if (state.isTyping) {
                clearTimeout(state.typingTimer);
                state.isTyping = false;
                storyTextEl.classList.remove('cursor-blink');
                const node = NODES[state.currentNode];
                if (node) {
                    storyTextEl.textContent = node.text;
                }
            }
        }

        function updateStatus() {
            valVa.textContent = state.va;
            valLe.textContent = state.le;
            valWi.textContent = state.wi;
            const setClass = (el, val) => {
                el.className = 'value';
                if (val >= 70) el.classList.add('va-high');
                else if (val >= 40) el.classList.add('va-mid');
                else el.classList.add('va-low');
            };
            setClass(valVa, state.va);
            setClass(valLe, state.le);
            setClass(valWi, state.wi);
        }

        function checkHiddenEndings() {
            if (state.va <= 25 && state.le < 45 && state.wi < 40) {
                if (!state.unlockedEndings.includes('E')) state.unlockedEndings.push('E');
            }
            if (state.va >= 50 && state.le <= 30 && state.wi >= 30) {
                if (!state.unlockedEndings.includes('F')) state.unlockedEndings.push('F');
            }
            if (state.va >= 80 && state.wi >= 70 && state.le <= 20) {
                if (!state.unlockedEndings.includes('G')) state.unlockedEndings.push('G');
            }
        }

        function goToNode(nodeId, playSound = true) {
            const node = NODES[nodeId];
            if (!node) return;
            if (state.isTyping) {
                clearTimeout(state.typingTimer);
                state.isTyping = false;
            }
            state.currentNode = nodeId;
            storyCard.classList.remove('fade-in');
            storyCard.classList.add('fade-out');

            setTimeout(() => {
                storyTitleEl.textContent = node.title || ' ';
                optionsArea.innerHTML = '';

                if (node.isEnding) {
                    const eid = node.endingId;
                    if (eid && !state.unlockedEndings.includes(eid)) state.unlockedEndings.push(eid);
                    checkHiddenEndings();
                    if (playSound && state.soundOn) playEndingMusic(eid);
                    storyTextEl.textContent = node.text;
                    renderEndingActions(eid);
                    memoryTrigger.classList.add('hidden');
                } else {
                    memoryTrigger.classList.add('hidden');
                    if (node.memoryKey && MEMORIES[node.memoryKey]) {
                        memoryTrigger.classList.remove('hidden');
                        memoryTrigger.dataset.memKey = node.memoryKey;
                    }
                    typeText(node.text, () => {
                        renderOptions(node);
                    });
                    if (playSound && state.soundOn && nodeId !== 'prologue') playPageTurn();
                    if (nodeId === 'prologue' && playSound) playPageTurn();
                }
                updateStatus();
                storyCard.classList.remove('fade-out');
                storyCard.classList.add('fade-in');
                mainContent.scrollTop = 0;
            }, 350);
        }

        function renderOptions(node) {
            optionsArea.innerHTML = '';
            if (!node.options || node.options.length === 0) return;
            node.options.forEach((opt, idx) => {
                const btn = document.createElement('button');
                btn.className = 'option-btn';
                const labelSpan = document.createElement('span');
                labelSpan.className = 'opt-label';
                labelSpan.textContent = String.fromCharCode(65 + idx);
                const textSpan = document.createElement('span');
                textSpan.className = 'opt-text';
                textSpan.textContent = opt.text;
                const hintSpan = document.createElement('span');
                hintSpan.className = 'opt-hint';
                hintSpan.textContent = opt.hint || '';
                btn.appendChild(labelSpan);
                btn.appendChild(textSpan);
                if (opt.hint) btn.appendChild(hintSpan);

                btn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    if (state.isTyping) {
                        skipType();
                        setTimeout(() => { renderOptions(node); }, 50);
                        return;
                    }
                    if (state.soundOn) playClick();
                    if (opt.values) {
                        state.va = Math.max(0, Math.min(100, state.va + (opt.values.va || 0)));
                        state.le = Math.max(0, Math.min(100, state.le + (opt.values.le || 0)));
                        state.wi = Math.max(0, Math.min(100, state.wi + (opt.values.wi || 0)));
                        updateStatus();
                    }
                    if (opt.next) {
                        goToNode(opt.next, true);
                    }
                });
                optionsArea.appendChild(btn);
            });
        }

        function renderEndingActions(endingId) {
            optionsArea.innerHTML = '';
            const container = document.createElement('div');
            container.className = 'ending-actions';

            const btnRestartLocal = document.createElement('button');
            btnRestartLocal.className = 'primary';
            btnRestartLocal.textContent = '🔄 重新游玩';
            btnRestartLocal.addEventListener('click', () => { if (state.soundOn) playClick();
                resetGame(); });

            const btnGalleryLocal = document.createElement('button');
            btnGalleryLocal.textContent = '📖 结局图鉴';
            btnGalleryLocal.addEventListener('click', () => { if (state.soundOn) playClick();
                openGallery(); });

            container.appendChild(btnRestartLocal);
            container.appendChild(btnGalleryLocal);
            optionsArea.appendChild(container);

            const infoDiv = document.createElement('div');
            infoDiv.style.cssText =
                'text-align:center;margin-top:12px;font-size:14px;color:#6b5f4f;background:rgba(200,168,130,0.12);padding:10px;border-radius:16px;';
            infoDiv.innerHTML =
                `💎 虚荣 ${state.va} &nbsp;|&nbsp; ❤️ 磊磊 ${state.le} &nbsp;|&nbsp; 👑 王子 ${state.wi}`;
            optionsArea.appendChild(infoDiv);
            checkHiddenEndings();
        }

        function resetGame() {
            if (state.soundOn) playClick();
            state.va = 45;
            state.le = 60;
            state.wi = 30;
            state.unlockedEndings = [];
            state.history = [];
            if (state.isTyping) { clearTimeout(state.typingTimer);
                state.isTyping = false; }
            updateStatus();
            goToNode('prologue', true);
        }

        function openGallery() {
            if (state.soundOn) playClick();
            renderGallery();
            galleryModal.classList.add('open');
        }

        function closeGallery() { galleryModal.classList.remove('open'); }

        function renderGallery() {
            galleryGrid.innerHTML = '';
            const allEndings = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
            allEndings.forEach((key) => {
                const end = ENDINGS[key];
                const unlocked = state.unlockedEndings.includes(key);
                const div = document.createElement('div');
                div.className = 'gallery-item' + (unlocked ? '' : ' locked');
                const titleDiv = document.createElement('div');
                titleDiv.className = 'g-title';
                titleDiv.textContent = (unlocked ? '✅ ' : '🔒 ') + end.icon + ' ' + end.title;
                const previewDiv = document.createElement('div');
                previewDiv.className = 'g-preview';
                previewDiv.textContent = unlocked ? end.preview : '??? 尚未解锁';
                const statusDiv = document.createElement('div');
                statusDiv.className = 'g-status';
                statusDiv.innerHTML = unlocked ?
                    `<span class="unlocked">✦ 已解锁</span>` :
                    `<span class="locked">✦ 未解锁</span>`;
                div.appendChild(titleDiv);
                div.appendChild(previewDiv);
                div.appendChild(statusDiv);
                if (unlocked) {
                    div.style.cursor = 'pointer';
                    div.addEventListener('click', () => {
                        if (state.soundOn) playClick();
                        alert(end.fullText);
                    });
                }
                galleryGrid.appendChild(div);
            });
        }

        function openMemory(key) {
            const mem = MEMORIES[key];
            if (!mem) return;
            if (state.soundOn) playPageTurn();
            memTitle.textContent = mem.title;
            memText.textContent = mem.text;
            memoryPopup.classList.add('open');
            state.memoryShowing = true;
        }

        function closeMemory() {
            memoryPopup.classList.remove('open');
            state.memoryShowing = false;
        }

        function toggleSound() {
            state.soundOn = !state.soundOn;
            btnSound.textContent = state.soundOn ? '🔊' : '🔇';
            btnSound.classList.toggle('active', !state.soundOn);
            if (state.soundOn) playClick();
        }

        // ---- 事件绑定 ----
        memoryTrigger.addEventListener('click', () => {
            const key = memoryTrigger.dataset.memKey;
            if (key) openMemory(key);
        });
        memClose.addEventListener('click', closeMemory);
        memoryPopup.addEventListener('click', (e) => { if (e.target === memoryPopup) closeMemory(); });
        galleryClose.addEventListener('click', closeGallery);
        galleryModal.addEventListener('click', (e) => { if (e.target === galleryModal) closeGallery(); });
        btnSound.addEventListener('click', toggleSound);
        btnGallery.addEventListener('click', openGallery);
        btnRestart.addEventListener('click', resetGame);
        storyTextEl.addEventListener('click', () => {
            if (state.isTyping) {
                skipType();
                const node = NODES[state.currentNode];
                if (node && !node.isEnding) renderOptions(node);
            }
        });
        document.addEventListener('keydown', (e) => {
            if (e.key === 'r' || e.key === 'R') resetGame();
            if (e.key === 'g' || e.key === 'G') openGallery();
            if (e.key === 'm' || e.key === 'M') toggleSound();
            if (e.key === 'Escape') { closeGallery();
                closeMemory(); }
        });

        // ---- 修补渲染逻辑 ----
        const origRenderOptions = renderOptions;
        renderOptions = function(node) {
            if (state.isTyping) return;
            origRenderOptions(node);
        };
        const origGoToNode = goToNode;
        goToNode = function(nodeId, playSound = true) {
            if (state.isTyping) { clearTimeout(state.typingTimer);
                state.isTyping = false; }
            origGoToNode(nodeId, playSound);
        };
        const origTypeText = typeText;
        typeText = function(text, callback) {
            if (state.isTyping) {
                clearTimeout(state.typingTimer);
                state.isTyping = false;
                storyTextEl.textContent = text;
                storyTextEl.classList.remove('cursor-blink');
                const node = NODES[state.currentNode];
                if (node && !node.isEnding) renderOptions(node);
                if (callback) callback();
                return;
            }
            origTypeText(text, () => {
                const node = NODES[state.currentNode];
                if (node && !node.isEnding) renderOptions(node);
                if (callback) callback();
            });
        };

        // ---- 初始化 ----
        function init() {
            state.va = 45;
            state.le = 60;
            state.wi = 30;
            state.unlockedEndings = [];
            updateStatus();
            goToNode('prologue', false);
            btnSound.textContent = '🔊';
            btnSound.classList.remove('active');
            document.addEventListener('touchstart', () => { initAudio(); }, { once: true });
            document.addEventListener('click', () => { initAudio(); }, { once: true });
        }
        init();
        console.log('🌟 虚荣抉择 · 超长完整版 (30+节点,每条路径≥12个选择) 已启动');
    </script>
</body>
</html>

Game Source: Remix of 虚荣抉择 · 我的虚荣女友

Creator: ElectricFlare30

Libraries: none

Complexity: complex (2007 lines, 91.7 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: remix-of-electricflare30" to link back to the original. Then publish at arcadelab.ai/publish.