虚荣抉择 · 我的虚荣女友
by GlitchGamer562007 lines91.7 KB
<!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} | ❤️ 磊磊 ${state.le} | 👑 王子 ${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: 虚荣抉择 · 我的虚荣女友
Creator: GlitchGamer56
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: game-glitchgamer56" to link back to the original. Then publish at arcadelab.ai/publish.