<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>向火独行</title>
<style>
@import url(../css/tongyong/root.css);
@import url(../css/tongyong/quanju.css);
@import url("../css/tongyong/body.css");
.container {
max-width: 800px;
margin: 0 auto;
border: 1px solid var(--accent-color);
padding: 20px;
box-shadow: 0 0 15px rgba(139, 0, 0, 0.3);
background-color: rgba(10, 10, 10, 0.9);
position: relative;
min-height: 80vh;
}
header {
text-align: center;
margin-bottom: 20px;
border-bottom: 1px solid var(--accent-color);
padding-bottom: 15px;
}
h1 {
color: var(--highlight-color);
font-size: 2.2rem;
letter-spacing: 2px;
text-shadow: 0 0 5px rgba(212, 175, 55, 0.5);
}
.game-area {
display: flex;
flex-direction: column;
gap: 20px;
}
.stats-panel {
display: flex;
justify-content: space-between;
background-color: rgba(20, 20, 20, 0.8);
padding: 10px;
border: 1px solid var(--accent-color);
font-size: 0.9rem;
}
.story-text {
background-color: rgba(15, 15, 15, 0.8);
padding: 20px;
border: 1px solid var(--accent-color);
min-height: 250px;
max-height: 400px;
overflow-y: auto;
line-height: 1.8;
white-space: pre-line;
}
.choices-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.choice-btn {
background-color: rgba(30, 30, 30, 0.8);
color: var(--text-color);
border: 1px solid var(--accent-color);
padding: 12px;
text-align: left;
cursor: pointer;
transition: all 0.3s;
}
.choice-btn:hover {
background-color: rgba(50, 50, 50, 0.8);
border-color: var(--highlight-color);
color: var(--highlight-color);
}
.controls {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
button {
background-color: var(--accent-color);
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #a00;
}
.save-load-container {
display: flex;
gap: 10px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 100;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: var(--dark-color);
border: 2px solid var(--accent-color);
padding: 20px;
width: 80%;
max-width: 500px;
}
.save-slots {
display: flex;
flex-direction: column;
gap: 10px;
margin: 15px 0;
}
.save-slot {
background-color: rgba(30, 30, 30, 0.8);
padding: 10px;
border: 1px solid var(--accent-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.save-slot:hover {
border-color: var(--highlight-color);
}
.save-info {
flex-grow: 1;
cursor: pointer;
}
.delete-btn {
background-color: var(--danger-color);
padding: 5px 10px;
font-size: 0.8rem;
}
.delete-btn:hover {
background-color: #e53e3e;
}
.fade-in {
animation: fadeIn 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.typing-effect {
overflow: hidden;
border-right: 2px solid var(--highlight-color);
white-space: nowrap;
margin: 0 auto;
animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite;
}
@keyframes typing {
from { width: 0 }
to { width: 100% }
}
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: var(--highlight-color) }
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>向火独行</h1>
<p>《克苏鲁的呼唤》介绍性单人冒险模组</p>
</header>
<div class="game-area">
<!--状态栏-->
<div class="stats-panel">
<div>理智: <span id="sanity-value">80</span>/100</div>
<div>生命: <span id="health-value">10</span>/10</div>
<div>时间: <span id="time-value">第1天 - 早晨</span></div>
<div>地点: <span id="location-value">车站</span></div>
</div>
<!--故事-->
<div class="story-text" id="story-text"></div>
<!--选项-->
<div class="choices-container" id="choices-container"></div>
<div class="controls">
<div class="save-load-container">
<button id="save-btn">保存游戏</button>
<button id="load-btn">读取游戏</button>
</div>
<button id="reset-btn">重新开始</button>
<button id="back-to-menu-btn">返回主菜单</button>
</div>
</div>
</div>
<!-- 存档/读档模态框 -->
<div class="modal" id="save-modal">
<div class="modal-content">
<h3>保存游戏</h3>
<p>选择一个存档位保存当前游戏进度:</p>
<!--存档槽-->
<div class="save-slots" id="save-slots"></div>
<div style="text-align: right;">
<button id="close-save-modal">关闭</button>
</div>
</div>
</div>
<div class="modal" id="load-modal">
<div class="modal-content">
<h3>读取游戏</h3>
<p>选择一个存档位加载游戏进度:</p>
<div class="save-slots" id="load-slots">
<!-- 存档槽位将通过JavaScript动态生成 -->
</div>
<div style="text-align: right;">
<button id="close-load-modal">关闭</button>
</div>
</div>
</div>
<script>
// 游戏状态
const gameState = {
currentScene: "1",
sanity: 80,
health: 10,
time: { day: 1, period: "中午" },
location: "车站",
inventory: [],
flags: {}
};
// 游戏场景数据
const scenes = {
"1": {
text: "太阳高悬天空,无情地释放着热量。当你来到汽车站时,感觉自己要熟了。你放下沉重的行李箱,终于得到片刻休息。在你的家乡,夏天总是漫长。但这个夏天格外令你空虚。\n你望向街对面,街对面是肮脏的肉店,挂着褪色遮阳篷的杂货店,还有破旧的烟草店。人们路过你时,用不信任的表情盯着你看,打量着你的衣着和行李。但住在这里是你父母的主意,和你无关。当你还是个小孩时也曾经南下。那时你还很开心,普罗维登斯有白墙小屋,还有绿树掩映的教堂。也许去阿卡姆开始新工作可以带给你需要的改变。\n但你在这世界上认识的所有人都住在这里。阿卡姆对于你来说是一个完全陌生的地方。你最后问了自己一遍,自己的选择到底是不是正确的。\n答案已经很清楚了。你所谓的朋友没有一个人来为你送行,你现在孤身一人。无论阿卡姆有什么挑战等待着你,那都将是新的生活、光彩夺目的生活。\n一辆灰色的小长途车开了过来,嘎啷嘎啷地响着停下。你戴回帽子,提起你的行李箱。",
choices: [
{ text: "上车", nextScene: "263"},
]
},
"263":{
text:"两个面有愠色的年轻男人走下了长途车。一个人从头到脚打量了你一遍,才转头离去。司机也下了车,瞟了你一眼,穿过马路进了烟草店。等他回来的时候,他正用泛黄的手指搓着烟卷。他卷完了最后一下,一边伸手寻找火柴盒,一边仔细地观察你。他身材瘦削,约莫五十多岁,身上穿的衬衫有公共汽车公司的徽标,沾着不少污渍。但他的眼神仍然锐利地从深陷的眼眶里透出来。\n“去哪儿?”你把你去奥西皮 (Ossipee) 的票拿给他看。从那里你可以前往罗切斯特、朴次茅斯,再沿海岸线到达纽伯里波特,最后抵达阿卡姆。你也许买得起至少一部分旅程的火车票,否则这将是很多段长途汽车旅程的第一站。“嗯……哼~”司机擦着火柴,点燃了他的香烟。他吸了一口,烟头开始闪光。然后他又吐了一口烟,用手指了指车尾。\n“行李架在上边。”"
choices:[
{text:"继续",nextScene:"8"},
],
},
"8":{
text:
}
"mansion_approach": {
text: "你按照地址找到了那座位于镇郊的大宅。它看起来年久失修,但依然能看出昔日的辉煌。铁门锈迹斑斑,庭院里杂草丛生。当你走近时,注意到二楼的一个窗户似乎有微弱的灯光一闪而过。",
choices: [
{ text: "敲门", nextScene: "mansion_knock" },
{ text: "尝试从后门进入", nextScene: "mansion_backdoor", condition: () => gameState.flags.hasLockpick || Math.random() > 0.7 },
{ text: "先绕房子观察一圈", nextScene: "mansion_observe" }
]
},
"town_inquiry": {
text: "你走进镇上唯一还在营业的酒馆。里面只有寥寥几位客人,他们看到你进来后都停止了交谈,用警惕的目光打量着你。酒保擦拭着杯子,头也不抬地问:'外地人?需要什么?'",
choices: [
{ text: "询问关于哈德利家族的事", nextScene: "ask_hadley" },
{ text: "打听镇上最近有什么怪事", nextScene: "ask_strange" },
{ text: "只是点一杯酒", nextScene: "order_drink" }
]
},
"mansion_enter": {
text: "你步入昏暗的大厅,空气中弥漫着灰尘和霉味。墙上挂着几幅肖像画,画中人物的眼睛似乎都在注视着你。管家带你来到书房,壁炉里的火苗微弱地跳动着。",
choices: [
{ text: "询问遗产的具体内容", nextScene: "ask_inheritance" },
{ text: "要求看看宅邸的其他部分", nextScene: "mansion_tour" },
{ text: "表示旅途劳累需要休息", nextScene: "mansion_rest" }
]
},
"ask_hadley": {
text: "'哈德利家族?'酒保的表情变得严肃,'他们很久以前就离开了这个镇子。有人说他们惹上了不该惹的东西。我建议你不要打听太多。'周围的客人听到你们的对话,纷纷低下头,避免与你有眼神接触。",
choices: [
{ text: "坚持询问更多细节", nextScene: "insist_hadley", effect: () => gameState.sanity -= 10 },
{ text: "改变话题,询问镇上住宿", nextScene: "ask_accommodation" },
{ text: "离开酒馆", nextScene: "1" }
]
},
"hotel": {
text: "你在镇上唯一的旅馆住下。房间简陋但干净。夜晚,你被一阵奇怪的声响惊醒,仿佛有人在走廊上低语。当你仔细聆听时,声音又消失了。",
choices: [
{ text: "起身查看", nextScene: "hotel_investigate", effect: () => gameState.sanity -= 5 },
{ text: "继续睡觉", nextScene: "hotel_sleep" },
{ text: "收拾行李准备离开", nextScene: "1" }
]
}
};
// DOM元素
const storyTextElement = document.getElementById('story-text');
const choicesContainer = document.getElementById('choices-container');
const sanityValueElement = document.getElementById('sanity-value');
const healthValueElement = document.getElementById('health-value');
const timeValueElement = document.getElementById('time-value');
const locationValueElement = document.getElementById('location-value');
const saveBtn = document.getElementById('save-btn');
const loadBtn = document.getElementById('load-btn');
const resetBtn = document.getElementById('reset-btn');
const saveModal = document.getElementById('save-modal');
const loadModal = document.getElementById('load-modal');
const saveSlotsElement = document.getElementById('save-slots');
const loadSlotsElement = document.getElementById('load-slots');
const closeSaveModal = document.getElementById('close-save-modal');
const closeLoadModal = document.getElementById('close-load-modal');
const backToMenuBtn = document.getElementById('back-to-menu-btn');
// 初始化游戏
function initGame() {
updateGameState();
renderScene(gameState.currentScene);
}
// 渲染场景
function renderScene(sceneId) {
const scene = scenes[sceneId];
if (!scene) {
storyTextElement.textContent = "前面的故事以后再来吧,没做啊喂!";
choicesContainer.innerHTML = "";
return;
}
// 更新故事文本
storyTextElement.textContent = scene.text;
storyTextElement.classList.add('fade-in');
// 清除旧的选择按钮
choicesContainer.innerHTML = "";
// 创建新的选择按钮
scene.choices.forEach(choice => {
// 检查条件(如果有)
if (choice.condition && !choice.condition()) {
return;
}
const button = document.createElement('button');
button.className = 'choice-btn';
button.textContent = choice.text;
button.addEventListener('click', () => {
// 执行效果(如果有)
if (choice.effect) {
choice.effect();
}
// 更新当前场景
gameState.currentScene = choice.nextScene;
// 更新游戏状态显示
updateGameState();
// 渲染新场景
renderScene(choice.nextScene);
});
choicesContainer.appendChild(button);
});
// 移除淡入动画类,以便下次可以重新添加
setTimeout(() => {
storyTextElement.classList.remove('fade-in');
}, 500);
}
// 更新游戏状态显示
function updateGameState() {
sanityValueElement.textContent = gameState.sanity;
healthValueElement.textContent = gameState.health;
timeValueElement.textContent = `第${gameState.time.day}天 - ${gameState.time.period}`;
locationValueElement.textContent = gameState.location;
}
// 存档功能
function saveGame(slot) {
const saveData = {
gameState: JSON.parse(JSON.stringify(gameState)), // 深拷贝
timestamp: new Date().toLocaleString()
};
localStorage.setItem(`coc_firealone_save_${slot}`, JSON.stringify(saveData));
alert(`游戏已保存到存档位 ${slot}`);
updateSaveSlotsDisplay();
}
// 读档功能
function loadGame(slot) {
const saveData = localStorage.getItem(`coc_firealone_save_${slot}`);
if (saveData) {
const parsedData = JSON.parse(saveData);
Object.assign(gameState, parsedData.gameState);
updateGameState();
renderScene(gameState.currentScene);
alert(`已从存档位 ${slot} 加载游戏`);
loadModal.style.display = 'none';
} else {
alert(`存档位 ${slot} 为空`);
}
}
// 删除存档
function deleteSave(slot, isSaveModal = false) {
if (confirm(`确定要删除存档位 ${slot} 的存档吗?`)) {
localStorage.removeItem(`coc_firealone_save_${slot}`);
updateSaveSlotsDisplay();
if (!isSaveModal) {
updateLoadSlotsDisplay();
}
}
}
// 更新存档槽位显示
function updateSaveSlotsDisplay() {
saveSlotsElement.innerHTML = "";
for (let i = 1; i <= 5; i++) {
const slot = document.createElement('div');
slot.className = 'save-slot';
const saveData = localStorage.getItem(`coc_firealone_save_${i}`);
let slotText = `存档位 ${i}`;
const infoDiv = document.createElement('div');
infoDiv.className = 'save-info';
if (saveData) {
const parsedData = JSON.parse(saveData);
slotText += ` - ${parsedData.timestamp}`;
infoDiv.textContent = slotText;
infoDiv.addEventListener('click', () => saveGame(i));
} else {
slotText += ` - 空`;
infoDiv.textContent = slotText;
infoDiv.addEventListener('click', () => saveGame(i));
}
slot.appendChild(infoDiv);
// 如果有存档,添加删除按钮
if (saveData) {
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.textContent = '删除';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation(); // 防止触发存档事件
deleteSave(i, true);
});
slot.appendChild(deleteBtn);
}
saveSlotsElement.appendChild(slot);
}
}
// 更新读取槽位显示
function updateLoadSlotsDisplay() {
loadSlotsElement.innerHTML = "";
for (let i = 1; i <= 5; i++) {
const slot = document.createElement('div');
slot.className = 'save-slot';
const saveData = localStorage.getItem(`coc_firealone_save_${i}`);
let slotText = `存档位 ${i}`;
const infoDiv = document.createElement('div');
infoDiv.className = 'save-info';
if (saveData) {
const parsedData = JSON.parse(saveData);
slotText += ` - ${parsedData.timestamp}`;
infoDiv.textContent = slotText;
infoDiv.addEventListener('click', () => loadGame(i));
} else {
slotText += ` - 空`;
infoDiv.textContent = slotText;
infoDiv.style.cursor = 'default';
}
slot.appendChild(infoDiv);
// 如果有存档,添加删除按钮
if (saveData) {
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.textContent = '删除';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation(); // 防止触发加载事件
deleteSave(i, false);
});
slot.appendChild(deleteBtn);
}
loadSlotsElement.appendChild(slot);
}
}
// 显示存档模态框
function showSaveModal() {
updateSaveSlotsDisplay();
saveModal.style.display = 'flex';
}
// 显示读档模态框
function showLoadModal() {
updateLoadSlotsDisplay();
loadModal.style.display = 'flex';
}
//返回主界面
function backToMainMenu() {
if (confirm("确定要返回主菜单吗?当前游戏进度将丢失。")) {
window.location.href = 'kaishi.html';
}
}
// 各种事件监听器
saveBtn.addEventListener('click', showSaveModal);
loadBtn.addEventListener('click', showLoadModal);
backToMenuBtn.addEventListener('click', backToMainMenu);
resetBtn.addEventListener('click', () => {
if (confirm("确定要重新开始游戏吗?当前进度将丢失。")) {
// 重置游戏状态
Object.assign(gameState, {
currentScene: "1",
sanity: 80,
health: 10,
time: { day: 1, period: "早晨" },
location: "车站",
inventory: [],
flags: {}
});
updateGameState();
renderScene(gameState.currentScene);
}
});
closeSaveModal.addEventListener('click', () => {
saveModal.style.display = 'none';
});
closeLoadModal.addEventListener('click', () => {
loadModal.style.display = 'none';
});
// 点击模态框外部关闭
window.addEventListener('click', (event) => {
if (event.target === saveModal) {
saveModal.style.display = 'none';
}
if (event.target === loadModal) {
loadModal.style.display = 'none';
}
});
// 初始化游戏
initGame();
</script>
</body>
</html>
修复上面代码中无法正常显示文本的问题