feat: 官网导航重构 + 主世界交互优化
- 官网(index.html/site-app.js): 特色/致玩家并入背景页(平铺非子菜单), 新增剧情tab, 新增登录后可见的"世界数据"tab - 主世界(world.html): 顶栏账号下拉常驻去重排, 坐标布局修正, 默认缩放下调, 微观滑动阴影修复, 时钟四色扇形+边框, 方向键/WASD平移+边界限位 - 官网百科页(website/intro.html): 背景页整合内容
This commit is contained in:
parent
c6ccc1d50b
commit
98a204b309
@ -1,43 +1,43 @@
|
||||
// 宇森官网 · 公开页数据打通(图鉴/规则/论坛/公告 从 Odoo 取数)
|
||||
// 依赖 assets/game-api.js
|
||||
|
||||
// ---------- 页面板块(特色 / 背景 / 致玩家) ----------
|
||||
// ---------- 页面板块:特色 / 背景 / 致玩家 合并进「背景」单页(平铺,非子菜单) ----------
|
||||
function featuresHTML(p){
|
||||
const items = (p.items || []).map(it =>
|
||||
`<div class="card"><div class="ic">${it.icon || ''}</div><h3>${it.title || ''}</h3><p>${it.desc || ''}</p></div>`
|
||||
).join('');
|
||||
return `<div class="sec-title">${p.title || '游戏特色'}<small>${p.subtitle || ''}</small></div><div class="grid">${items}</div>`;
|
||||
}
|
||||
|
||||
function backgroundHTML(p){
|
||||
const layers = (p.items || []).map((it, idx) =>
|
||||
`<div class="layer l${idx + 1}"><div class="lt">${it.title || ''}</div><div class="ld">${it.desc || ''}</div><span class="star">${it.icon || '✦'}</span></div>`
|
||||
).join('');
|
||||
return `<div class="layers">${layers}</div><div class="bg-text"><h2>${p.title || '世界起源'}</h2>${p.body_html || ''}</div>`;
|
||||
}
|
||||
|
||||
function letterHTML(p){
|
||||
return `<div class="letter-wrap"><h2>${p.title || ''}</h2><div class="lt-sub">${p.subtitle || ''}</div><div class="lt-body">${p.body_html || ''}</div><div class="lt-sign">—— 宇森开发组</div></div>`;
|
||||
}
|
||||
|
||||
async function loadPages(){
|
||||
try {
|
||||
const d = await API.pages();
|
||||
const pages = (d && d.items) || [];
|
||||
const bg = document.getElementById('background');
|
||||
let html = '';
|
||||
pages.forEach(p => {
|
||||
if (p.key === 'features') renderFeatures(p);
|
||||
else if (p.key === 'background') renderBackground(p);
|
||||
else if (p.key === 'letter') renderLetter(p);
|
||||
if (p.key === 'features') html += featuresHTML(p);
|
||||
else if (p.key === 'background') html += backgroundHTML(p);
|
||||
else if (p.key === 'letter') html += letterHTML(p);
|
||||
});
|
||||
if (bg) bg.innerHTML = html; // 特色 + 背景 + 致玩家 平铺进同一页,无子菜单切换
|
||||
|
||||
const story = document.getElementById('story');
|
||||
if (story) story.innerHTML = '<div class="sec-title">剧情<small>STORY</small></div><div style="text-align:center;padding:80px 20px;color:#556677;font-size:15px;"><div style="font-size:48px;margin-bottom:16px;">📖</div>剧情内容正在筹备中…<div style="font-size:13px;margin-top:8px;color:#445566;">敬请期待</div></div>';
|
||||
} catch (e) { /* 失败时保留 HTML 中的静态兜底内容 */ }
|
||||
}
|
||||
|
||||
function renderFeatures(p){
|
||||
const sec = document.getElementById('features');
|
||||
if (!sec) return;
|
||||
const items = (p.items || []).map(it =>
|
||||
`<div class="card"><div class="ic">${it.icon || ''}</div><h3>${it.title || ''}</h3><p>${it.desc || ''}</p></div>`
|
||||
).join('');
|
||||
sec.innerHTML = `<div class="sec-title">${p.title || '游戏特色'}<small>${p.subtitle || ''}</small></div><div class="grid">${items}</div>`;
|
||||
}
|
||||
|
||||
function renderBackground(p){
|
||||
const sec = document.getElementById('background');
|
||||
if (!sec) return;
|
||||
const layers = (p.items || []).map((it, idx) =>
|
||||
`<div class="layer l${idx + 1}"><div class="lt">${it.title || ''}</div><div class="ld">${it.desc || ''}</div><span class="star">${it.icon || '✦'}</span></div>`
|
||||
).join('');
|
||||
sec.innerHTML = `<div class="layers">${layers}</div><div class="bg-text"><h2>${p.title || '世界起源'}</h2>${p.body_html || ''}</div>`;
|
||||
}
|
||||
|
||||
function renderLetter(p){
|
||||
const sec = document.getElementById('letter');
|
||||
if (!sec) return;
|
||||
sec.innerHTML = `<div class="letter-wrap"><h2>${p.title || ''}</h2><div class="lt-sub">${p.subtitle || ''}</div><div class="lt-body">${p.body_html || ''}</div><div class="lt-sign">—— 宇森开发组</div></div>`;
|
||||
}
|
||||
|
||||
// ---------- 规则 ----------
|
||||
async function loadRules(){
|
||||
const el = document.querySelector('#rules .rules-scroll');
|
||||
|
||||
26
index.html
26
index.html
@ -52,6 +52,8 @@
|
||||
.user-box .auth-link:hover{color:#fff;}
|
||||
.user-box .auth-btn{font-family:inherit;font-size:13px;color:#0b1020;background:linear-gradient(120deg,var(--gold),#f0d49a);border:none;padding:6px 16px;border-radius:20px;cursor:pointer;font-weight:600;transition:.2s;}
|
||||
.user-box .auth-btn:hover{filter:brightness(1.1);box-shadow:0 0 16px rgba(231,184,92,.4);}
|
||||
.nav .auth-only{display:none;} /* 登录专属 tab,未登录时隐藏 */
|
||||
.nav .auth-only.visible{display:inline-block;}
|
||||
.cta:hover{filter:brightness(1.08);}
|
||||
|
||||
.content{flex:1;position:relative;overflow:hidden;}
|
||||
@ -201,13 +203,13 @@
|
||||
<div class="logo"><span class="cn">宇森</span></div>
|
||||
<nav class="nav">
|
||||
<button data-tab="intro" class="active">介绍</button>
|
||||
<button data-tab="features">特色</button>
|
||||
<button data-tab="background">背景</button>
|
||||
<button data-tab="rules">规则</button>
|
||||
<button data-tab="codex">图鉴</button>
|
||||
<button data-tab="forum">论坛</button>
|
||||
<button data-tab="announce">公告</button>
|
||||
<button data-tab="letter">致玩家</button>
|
||||
<button data-tab="story">剧情</button>
|
||||
<button data-tab="worlddata" class="auth-only">世界数据</button>
|
||||
</nav>
|
||||
<div class="spacer"></div>
|
||||
<div class="user-box" id="userBox"></div>
|
||||
@ -232,10 +234,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== 特色 ===== -->
|
||||
<section class="panel" id="features"></section>
|
||||
|
||||
<!-- ===== 背景 ===== -->
|
||||
<!-- ===== 背景(特色 + 世界背景 + 致玩家 合并平铺) ===== -->
|
||||
<section class="panel" id="background"></section>
|
||||
|
||||
<!-- ===== 规则 ===== -->
|
||||
@ -362,8 +361,17 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== 致玩家 ===== -->
|
||||
<section class="panel" id="letter"></section>
|
||||
<!-- ===== 剧情 ===== -->
|
||||
<section class="panel" id="story"></section>
|
||||
|
||||
<!-- ===== 世界数据(登录后可见) ===== -->
|
||||
<section class="panel" id="worlddata">
|
||||
<div class="sec-title">世界数据<small>WORLD DATA</small></div>
|
||||
<div id="worldDataContent" style="text-align:center;padding:60px 20px;color:#556677;font-size:15px;">
|
||||
<div style="font-size:40px;margin-bottom:14px;">🌍</div>
|
||||
加载中…
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -587,6 +595,7 @@
|
||||
function renderLoggedOut(){
|
||||
if(!userBox) return;
|
||||
userBox.innerHTML = '<a href="login.html?next=world.html" class="auth-link">登录</a><a href="login.html?next=world.html" class="auth-btn">注册</a>';
|
||||
document.querySelectorAll('.nav .auth-only').forEach(el => el.classList.remove('visible'));
|
||||
}
|
||||
|
||||
function renderLoggedIn(me){
|
||||
@ -594,6 +603,7 @@
|
||||
userBox.innerHTML = '<span class="ub-name"></span><button class="ub-logout" id="ubLogout">退出</button>';
|
||||
const nm = userBox.querySelector('.ub-name');
|
||||
if(nm) nm.textContent = me.name || me.login;
|
||||
document.querySelectorAll('.nav .auth-only').forEach(el => el.classList.add('visible'));
|
||||
const lb = document.getElementById('ubLogout');
|
||||
if(lb) lb.addEventListener('click', async ()=>{
|
||||
try { await API.logout(); } catch(e){}
|
||||
|
||||
@ -3,64 +3,103 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>游戏介绍 — 宇森</title>
|
||||
<title>宇森 — 游戏百科</title>
|
||||
<style>
|
||||
* { margin:0; padding:0; box-sizing:border-box; }
|
||||
html { scroll-behavior:smooth; }
|
||||
body {
|
||||
font-family:'Microsoft YaHei', 'PingFang SC', sans-serif;
|
||||
background:#06090f; color:#c8d6e5;
|
||||
line-height:1.7;
|
||||
font-family:'Microsoft YaHei','PingFang SC',sans-serif;
|
||||
background:#06090f; color:#c8d6e5; line-height:1.7;
|
||||
}
|
||||
#particles {
|
||||
position:fixed; top:0; left:0; width:100%; height:100%;
|
||||
z-index:0; pointer-events:none;
|
||||
}
|
||||
|
||||
/* ===== 顶栏导航(tab 切换) ===== */
|
||||
nav {
|
||||
position:fixed; top:0; left:0; right:0; z-index:1000;
|
||||
background:rgba(6,9,15,0.85); backdrop-filter:blur(12px);
|
||||
border-bottom:1px solid rgba(100,200,255,0.1);
|
||||
padding:0 40px; height:56px;
|
||||
display:flex; align-items:center; justify-content:space-between;
|
||||
background:rgba(6,9,15,.88); backdrop-filter:blur(14px);
|
||||
border-bottom:1px solid rgba(100,200,255,.1);
|
||||
padding:0 32px; height:56px;
|
||||
display:flex; align-items:center; gap:6px;
|
||||
}
|
||||
nav .logo { font-size:18px; font-weight:bold; text-decoration:none; letter-spacing:2px; background:linear-gradient(135deg, #4af, #a8e6cf); -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
|
||||
nav .links { display:flex; gap:28px; list-style:none; }
|
||||
nav .links a { color:#667788; text-decoration:none; font-size:14px; transition:color 0.3s; }
|
||||
nav .links a:hover, nav .links a.active { color:#4af; }
|
||||
nav .logo {
|
||||
font-size:18px; font-weight:bold; letter-spacing:2px;
|
||||
background:linear-gradient(135deg,#4af,#a8e6cf);
|
||||
-webkit-background-clip:text; -webkit-text-fill-color:transparent;
|
||||
text-decoration:none; margin-right:20px;
|
||||
}
|
||||
nav .tab {
|
||||
color:#667788; text-decoration:none; font-size:14px;
|
||||
padding:7px 16px; border-radius:8px; transition:all .2s;
|
||||
cursor:pointer; border:none; background:none; font-family:inherit;
|
||||
}
|
||||
nav .tab:hover { color:#aaccdd; background:rgba(100,200,255,.06); }
|
||||
nav .tab.active {
|
||||
color:#4af; background:rgba(74,175,255,.12);
|
||||
box-shadow:inset 0 0 0 1px rgba(74,175,255,.2);
|
||||
}
|
||||
#auth-area { margin-left:auto; margin-right:8px; }
|
||||
|
||||
/* ===== 主内容区 ===== */
|
||||
.page {
|
||||
position:relative; z-index:1; padding:90px 32px 60px; max-width:900px; margin:0 auto;
|
||||
position:relative; z-index:1; padding:76px 36px 60px;
|
||||
max-width:980px; margin:0 auto;
|
||||
}
|
||||
.tab-panel { display:none; }
|
||||
.tab-panel.active { display:block; animation:fadeIn .25s ease; }
|
||||
@keyframes fadeIn { from{opacity:0;transform:translateY(6px);} to{opacity:1;transform:none;} }
|
||||
|
||||
h1 {
|
||||
font-size:32px; font-weight:bold; margin-bottom:8px;
|
||||
background:linear-gradient(135deg, #4af, #a8e6cf);
|
||||
font-size:28px; font-weight:bold; margin-bottom:6px;
|
||||
background:linear-gradient(135deg,#4af,#a8e6cf);
|
||||
-webkit-background-clip:text; -webkit-text-fill-color:transparent;
|
||||
}
|
||||
.page-sub { color:#556677; font-size:15px; margin-bottom:48px; }
|
||||
.page-sub { color:#556677; font-size:14px; margin-bottom:32px; }
|
||||
|
||||
h2 {
|
||||
font-size:24px; color:#e0e0e0; margin:48px 0 16px; padding-left:12px;
|
||||
font-size:22px; color:#e0e0e0; margin:36px 0 14px; padding-left:12px;
|
||||
border-left:3px solid #4af;
|
||||
}
|
||||
p { color:#99aabb; font-size:15px; margin-bottom:16px; line-height:1.9; }
|
||||
|
||||
.features { display:grid; grid-template-columns:repeat(2, 1fr); gap:20px; margin-top:24px; }
|
||||
.feature-card {
|
||||
background:rgba(10,20,40,0.5); border:1px solid rgba(100,200,255,0.1);
|
||||
border-radius:12px; padding:28px 22px;
|
||||
transition:transform 0.3s, border-color 0.3s;
|
||||
h3 {
|
||||
font-size:17px; color:#d0dce8; margin:20px 0 10px;
|
||||
}
|
||||
.feature-card:hover { transform:translateY(-3px); border-color:rgba(100,200,255,0.3); }
|
||||
.feature-card .icon { font-size:32px; margin-bottom:12px; }
|
||||
.feature-card h3 { font-size:16px; color:#e0e0e0; margin-bottom:8px; }
|
||||
p { color:#99aabb; font-size:15px; margin-bottom:14px; line-height:1.9; }
|
||||
|
||||
/* ===== 背景页 · 内容章节(平铺,非子菜单) ===== */
|
||||
.bg-section { margin-bottom:40px; }
|
||||
.bg-section h2 { margin-top:0; }
|
||||
|
||||
.features { display:grid; grid-template-columns:repeat(2,1fr); gap:18px; margin-top:18px; }
|
||||
.feature-card {
|
||||
background:rgba(10,20,40,.5); border:1px solid rgba(100,200,255,.1);
|
||||
border-radius:12px; padding:24px 20px;
|
||||
transition:transform .25s,border-color .25s;
|
||||
}
|
||||
.feature-card:hover { transform:translateY(-3px); border-color:rgba(100,200,255,.28); }
|
||||
.feature-card .icon { font-size:30px; margin-bottom:10px; }
|
||||
.feature-card h3 { font-size:15px; color:#e0e0e0; margin-bottom:6px; }
|
||||
.feature-card p { font-size:13px; color:#8899aa; line-height:1.6; margin:0; }
|
||||
|
||||
@media (max-width:768px) {
|
||||
.features { grid-template-columns:1fr; }
|
||||
h1 { font-size:24px; } nav .links { display:none; }
|
||||
.page { padding:80px 20px 40px; }
|
||||
/* ===== 其他面板通用 ===== */
|
||||
.panel-placeholder {
|
||||
text-align:center; padding:80px 20px; color:#556677;
|
||||
font-size:15px;
|
||||
}
|
||||
.panel-placeholder .icon { font-size:48px; margin-bottom:16px; }
|
||||
|
||||
/* ===== 响应式 ===== */
|
||||
@media (max-width:768px) {
|
||||
nav { padding:0 16px; flex-wrap:wrap; height:auto; gap:4px; padding-top:8px;padding-bottom:8px; }
|
||||
nav .tab { font-size:13px; padding:6px 12px; }
|
||||
nav .logo { margin-right:12px; }
|
||||
.page { padding:96px 16px 40px; }
|
||||
.features { grid-template-columns:1fr; }
|
||||
}
|
||||
|
||||
/* ===== 加载态 ===== */
|
||||
.loading { text-align:center; color:#556677; padding:40px; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="auth.css">
|
||||
</head>
|
||||
@ -69,70 +108,176 @@ p { color:#99aabb; font-size:15px; margin-bottom:16px; line-height:1.9; }
|
||||
<canvas id="particles"></canvas>
|
||||
|
||||
<nav>
|
||||
<a href="index.html" class="logo">宇森</a>
|
||||
<a href="index.html" class="logo">✦ 宇森</a>
|
||||
<button class="tab active" data-tab="intro">介绍</button>
|
||||
<button class="tab" data-tab="bg">背景</button>
|
||||
<button class="tab" data-tab="rules">规则</button>
|
||||
<button class="tab" data-tab="codex">图鉴</button>
|
||||
<button class="tab" data-tab="forum">论坛</button>
|
||||
<button class="tab" data-tab="announce">公告</button>
|
||||
<button class="tab" data-tab="story">剧情</button>
|
||||
<div id="auth-area"></div>
|
||||
<ul class="links">
|
||||
<li><a href="intro.html" class="active">游戏介绍</a></li>
|
||||
<li><a href="platform.html">平台架构</a></li>
|
||||
<li><a href="world.html">世界结构</a></li>
|
||||
<li><a href="races.html">种族</a></li>
|
||||
<li><a href="demo.html">演示</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="page">
|
||||
<h1>游戏介绍</h1>
|
||||
<p class="page-sub">这个世界不是静止的布景,它有地质、气候、生态和文明演化逻辑</p>
|
||||
|
||||
<div style="background:rgba(10,20,40,0.4); border-radius:12px; padding:32px 28px; margin-bottom:32px;">
|
||||
<p style="font-size:15px; color:#b0c0d0;">
|
||||
你的角色不是操控者,而是<strong style="color:#4af;">「园丁」</strong>——播种、引导、在关键时刻施加影响,然后看着文明顺着自己的逻辑生长。
|
||||
</p>
|
||||
<p style="font-size:15px; color:#b0c0d0; margin-top:12px;">
|
||||
每个世界由五层垂直空间组成:从地心的熔岩脉搏,到地表上繁衍的城镇,再到星空中的未知信号。层与层之间不是割裂的维度,而是同一世界的不同切面——地下的矿脉供养地表的炉火,天空的季风影响作物的丰歉,星空的异常预示着文明的转折。
|
||||
</p>
|
||||
<p style="font-size:15px; color:#b0c0d0; margin-top:12px;">
|
||||
文明中的个体(NPC)有自己的性格、记忆和成长轨迹。他们会在火灾后学会预防,在交易中积累智慧,在危机中推举领袖。
|
||||
<br><strong style="color:#a8e6cf;">这是一个活的文明系统。</strong>
|
||||
</p>
|
||||
<!-- ========== 介绍 ========== -->
|
||||
<div class="tab-panel active" id="panel-intro">
|
||||
<h1>宇 森</h1>
|
||||
<p class="page-sub">寰 宇 · 森 罗 — 一个活的文明系统</p>
|
||||
|
||||
<div style="background:rgba(10,20,40,.4); border-radius:12px; padding:30px 26px;">
|
||||
<p style="font-size:15px;color:#b0c0d0;">
|
||||
你的角色不是操控者,而是<strong style="color:#4af;">「园丁」</strong>——播种、引导、在关键时刻施加影响,然后看着文明顺着自己的逻辑生长。
|
||||
</p>
|
||||
<p style="font-size:15px;color:#b0c0d0;margin-top:12px;">
|
||||
每个世界由五层垂直空间组成:从地心的熔岩脉搏,到地表上繁衍的城镇,再到星空中的未知信号。层与层之间不是割裂的维度,而是同一世界的不同切面——地下的矿脉供养地表的炉火,天空的季风影响作物的丰歉,星空的异常预示着文明的转折。
|
||||
</p>
|
||||
<p style="font-size:15px;color:#b0c0d0;margin-top:12px;">
|
||||
文明中的个体(NPC)有自己的性格、记忆和成长轨迹。他们会在火灾后学会预防,在交易中积累智慧,在危机中推举领袖。
|
||||
<br><strong style="color:#a8e6cf;">这是一个活的文明系统。</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>快速了解</h2>
|
||||
<div class="features">
|
||||
<div class="feature-card">
|
||||
<div class="icon">🧬</div>
|
||||
<h3>活的文明</h3>
|
||||
<p>NPC 有性格、记忆和自主决策能力,不是站桩的静态单位。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🏔️</div>
|
||||
<h3>五层垂直世界</h3>
|
||||
<p>地心 / 地下 / 大地 / 天空 / 星空,层间资源和事件互通。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🔭</div>
|
||||
<h3>镜头语言</h3>
|
||||
<p>点击格子 → 飞入微观场景,建筑动画、人物走动、资源飘字。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🌐</div>
|
||||
<h3>独立世界 + 异步互动</h3>
|
||||
<p>每人一个世界,通过贸易 / 战争 / 联盟产生连接。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>核心特色</h2>
|
||||
<div class="features">
|
||||
<div class="feature-card">
|
||||
<div class="icon">🧬</div>
|
||||
<h3>活的文明</h3>
|
||||
<p>NPC 有性格、记忆和自主决策能力,不是站桩的静态单位。日常靠规则引擎生存,周期性通过智能体反思进化。</p>
|
||||
<!-- ========== 背景(特色 + 世界背景 + 致玩家 整合) ========== -->
|
||||
<div class="tab-panel" id="panel-bg">
|
||||
<h1>背 景</h1>
|
||||
<p class="page-sub">世界观设定 · 开发理念 · 给玩家的话</p>
|
||||
|
||||
<!-- 子板块一:核心特色 -->
|
||||
<div class="bg-section">
|
||||
<h2>✦ 核心特色</h2>
|
||||
<p>宇森不是传统沙盒——每个系统都围绕「活的文明」这一核心理念设计。</p>
|
||||
<div class="features">
|
||||
<div class="feature-card">
|
||||
<div class="icon">🧬</div>
|
||||
<h3>活的文明</h3>
|
||||
<p>NPC 有性格、记忆和自主决策能力,不是站桩的静态单位。日常靠规则引擎生存,周期性通过智能体反思进化。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🏔️</div>
|
||||
<h3>五层垂直世界</h3>
|
||||
<p>地心 / 地下 / 大地 / 天空 / 星空,五层是同一世界的不同切面,层间有资源和事件互动。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🔭</div>
|
||||
<h3>镜头语言</h3>
|
||||
<p>点击格子 → 由远及近飞入 → 进入微观场景。建筑动画、人物走动、资源飘字,沉浸式体验。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🌐</div>
|
||||
<h3>独立世界 + 异步互动</h3>
|
||||
<p>每人一个世界,通过贸易 / 战争 / 联盟产生连接。独立的世界平面,异步策略博弈。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🗺️</div>
|
||||
<h3>渐进式地图</h3>
|
||||
<p>从 127 格起步,随文明发展扩展至千格、万格。正六边形密铺,无缝缩放。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">⚔️</div>
|
||||
<h3>深度系统</h3>
|
||||
<p>种族 / 性格 / 天赋 / 职业 / 建造 / 交易 / 战争 / 势力 / 装备 / 神器 / 传奇 — 十五大系统。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🏔️</div>
|
||||
<h3>五层垂直世界</h3>
|
||||
<p>地心 / 地下 / 本地 / 天空 / 星空,五层是同一世界的不同切面,层间有资源和事件互动。</p>
|
||||
|
||||
<!-- 子板块二:世界背景 -->
|
||||
<div class="bg-section">
|
||||
<h2>✦ 世界背景</h2>
|
||||
<p>你的角色不是操控者,而是<strong style="color:#4af;">「园丁」</strong>——播种、引导、在关键时刻施加影响,然后看着文明顺着自己的逻辑生长。</p>
|
||||
<p>每个世界由五层垂直空间组成:从地心的熔岩脉搏,到地表上繁衍的城镇,再到星空中的未知信号。层与层之间不是割裂的维度,而是同一世界的不同切面——地下的矿脉供养地表的炉火,天空的季风影响作物的丰歉,星空的异常预示着文明的转折。</p>
|
||||
<p>文明中的个体(NPC)有自己的性格、记忆和成长轨迹。他们会在火灾后学会预防,在交易中积累智慧,在危机中推举领袖。<br><strong style="color:#a8e6cf;">这是一个活的文明系统。</strong></p>
|
||||
<h3>时间与季节</h3>
|
||||
<p>世界拥有独立的昼夜循环和四季更替。时间不仅影响光照和氛围,还驱动 NPC 的作息、作物的生长、以及某些事件的发生概率。玩家可以通过时钟控件加速或暂停时间流逝。</p>
|
||||
<h3>五层结构</h3>
|
||||
<p><strong>星空</strong> — 未知的信号源,可能来自远古文明或外星存在。<br>
|
||||
<strong>天空</strong> — 季风与气候层,影响地表农业和航行。<br>
|
||||
<strong>大地</strong> — 文明的主舞台,城镇、农田、道路在此展开。<br>
|
||||
<strong>地下</strong> — 矿脉与遗迹,为地表提供资源。<br>
|
||||
<strong>地心</strong> — 熔岩与能量源头,极端环境下的特殊生态。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🔭</div>
|
||||
<h3>镜头语言</h3>
|
||||
<p>点击格子 → 由远及近飞入 → 进入微观场景。建筑动画、人物走动、资源飘字,沉浸式体验。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🌐</div>
|
||||
<h3>独立世界 + 异步互动</h3>
|
||||
<p>每人一个世界,通过贸易 / 战争 / 联盟产生连接。独立的世界平面,异步策略博弈。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">🗺️</div>
|
||||
<h3>渐进式地图</h3>
|
||||
<p>从 127 格起步,随文明发展扩展至千格、万格。正六边形密铺,无缝缩放。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="icon">⚔️</div>
|
||||
<h3>深度系统</h3>
|
||||
<p>种族 / 性格 / 天赋 / 职业 / 建造 / 交易 / 战争 / 势力 / 装备 / 神器 / 传奇 — 十五大系统。</p>
|
||||
|
||||
<!-- 子板块三:致玩家 -->
|
||||
<div class="bg-section">
|
||||
<h2>✦ 致玩家</h2>
|
||||
<p style="font-size:16px;color:#c8d6e5;line-height:2;">
|
||||
欢迎来到宇森。这里没有既定的胜利条件,也没有强制的主线任务。<br><br>
|
||||
你可以花一下午看 NPC 们如何从零建起一座小镇;也可以专注于地形塑造,把世界雕成你心中的样子;或者什么都不做,只是静静地看着昼夜交替、四季流转。<br><br>
|
||||
这个世界会记住你的每一次选择。你种下的因,会在某个意想不到的时刻结出果——也许是一棵参天大树,也许是一场席卷大陆的战争,也许只是一个居民在酒馆里提起你的名字。<br><br>
|
||||
<strong style="color:#a8e6cf;">去探索吧,园丁。这个世界在等你。</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 规则 ========== -->
|
||||
<div class="tab-panel" id="panel-rules">
|
||||
<h1>规 则</h1>
|
||||
<p class="page-sub">游戏机制与玩法说明</p>
|
||||
<div id="rules"><div class="loading">加载中...</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 图鉴 ========== -->
|
||||
<div class="tab-panel" id="panel-codex">
|
||||
<h1>图 鉴</h1>
|
||||
<p class="page-sub">世界中的万物记录</p>
|
||||
<div id="codex"><div class="loading">加载中...</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 论坛 ========== -->
|
||||
<div class="tab-panel" id="panel-forum">
|
||||
<h1>论 坛</h1>
|
||||
<p class="page-sub">玩家讨论与交流</p>
|
||||
<div id="forum"><div class="loading">加载中...</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 公告 ========== -->
|
||||
<div class="tab-panel" id="panel-announce">
|
||||
<h1>公 告</h1>
|
||||
<p class="page-sub">官方通知与更新日志</p>
|
||||
<div id="announce"><div class="loading">加载中...</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 剧情(新增) ========== -->
|
||||
<div class="tab-panel" id="panel-story">
|
||||
<h1>剧 情</h1>
|
||||
<p class="page-sub">世界观主线与支线叙事</p>
|
||||
<div class="panel-placeholder">
|
||||
<div class="icon">📖</div>
|
||||
<p>剧情内容正在筹备中…</p>
|
||||
<p style="font-size:13px;margin-top:8px;color:#445566;">敬请期待</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /.page -->
|
||||
|
||||
<script>
|
||||
/* ===== 粒子背景 ===== */
|
||||
const canvas = document.getElementById('particles');
|
||||
const ctx = canvas.getContext('2d');
|
||||
let particles = [];
|
||||
@ -140,15 +285,30 @@ function resize() { canvas.width = window.innerWidth; canvas.height = window.inn
|
||||
resize(); window.addEventListener('resize', resize);
|
||||
class Particle {
|
||||
constructor() { this.reset(); this.y = Math.random() * canvas.height; }
|
||||
reset() { this.x = Math.random() * canvas.width; this.y = -10; this.size = Math.random()*1.5+0.5; this.speed = Math.random()*0.3+0.1; this.opacity = Math.random()*0.5+0.1; }
|
||||
update() { this.y += this.speed; if (this.y > canvas.height+10) this.reset(); }
|
||||
draw() { ctx.fillStyle = `rgba(100,200,255,${this.opacity})`; ctx.beginPath(); ctx.arc(this.x,this.y,this.size,0,Math.PI*2); ctx.fill(); }
|
||||
reset() { this.x = Math.random()*canvas.width; this.y=-10; this.size=Math.random()*1.5+.5; this.speed=Math.random()*.3+.1; this.opacity=Math.random()*.5+.1; }
|
||||
update() { this.y+=this.speed; if(this.y>canvas.height+10)this.reset(); }
|
||||
draw() { ctx.fillStyle=`rgba(100,200,255,${this.opacity})`; ctx.beginPath(); ctx.arc(this.x,this.y,this.size,0,Math.PI*2); ctx.fill(); }
|
||||
}
|
||||
for (let i=0;i<80;i++) particles.push(new Particle());
|
||||
function animate() { ctx.clearRect(0,0,canvas.width,canvas.height); particles.forEach(p=>{p.update();p.draw();}); requestAnimationFrame(animate); }
|
||||
for(let i=0;i<80;i++) particles.push(new Particle());
|
||||
function animate(){ctx.clearRect(0,0,canvas.width,canvas.height);particles.forEach(p=>{p.update();p.draw();});requestAnimationFrame(animate);}
|
||||
animate();
|
||||
|
||||
/* ===== Tab 切换 ===== */
|
||||
const tabs = document.querySelectorAll('.tab');
|
||||
const panels = document.querySelectorAll('.tab-panel');
|
||||
function switchTab(name) {
|
||||
tabs.forEach(t => t.classList.toggle('active', t.dataset.tab === name));
|
||||
panels.forEach(p => p.classList.toggle('active', p.id === 'panel-'+name));
|
||||
// URL hash 不跳转,静默切换
|
||||
history.replaceState(null,'','#'+name);
|
||||
}
|
||||
tabs.forEach(t => t.addEventListener('click', () => switchTab(t.dataset.tab)));
|
||||
// 支持 hash 直接访问
|
||||
if(location.hash){const h=location.hash.slice(1);if(document.getElementById('panel-'+h))switchTab(h);}
|
||||
</script>
|
||||
<script src="auth.js"></script>
|
||||
<script src="assets/game-api.js"></script>
|
||||
<script src="assets/site-app.js"></script>
|
||||
<script>YuSenAuth.init();</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
50
world.html
50
world.html
@ -3323,33 +3323,43 @@ init();
|
||||
<button class="nc-se" data-cmd="se">↘</button>
|
||||
</div>
|
||||
<script>
|
||||
// 右下角导航控件:方向按钮 + 键盘方向键,统一平移相机
|
||||
const PAN_STEP = 120; // [PLACEHOLDER] 每按一步平移像素量
|
||||
// 右下角导航控件:方向按钮(支持长按) + 键盘方向键,统一平移相机
|
||||
const PAN_STEP = 20; // [PLACEHOLDER] 每步平移像素量(按钮单次 / 键盘单次)
|
||||
const PAN_HOLD_MS = 55; // [PLACEHOLDER] 按钮长按重复间隔(ms)
|
||||
const PAN_BOUND_K = 0.45; // [PLACEHOLDER] 平移上限 = 视口边长的比例(向内收缩,世界中心最多移到屏幕内约 45% 处即停)
|
||||
let panTimer = null;
|
||||
function stopPanHold() { if (panTimer) { clearInterval(panTimer); panTimer = null; } }
|
||||
function panCamera(dx, dy) {
|
||||
if (microViewOpen) return; // 微观视图打开时不平移主世界
|
||||
camera.x += dx;
|
||||
camera.y += dy;
|
||||
const bx = W * PAN_BOUND_K, by = H * PAN_BOUND_K;
|
||||
camera.x = Math.max(-bx, Math.min(bx, camera.x + dx));
|
||||
camera.y = Math.max(-by, Math.min(by, camera.y + dy));
|
||||
}
|
||||
document.getElementById('navControls').addEventListener('click', e => {
|
||||
const PAN_DIRS = {
|
||||
up:[0,-PAN_STEP], down:[0,PAN_STEP], left:[-PAN_STEP,0], right:[PAN_STEP,0],
|
||||
nw:[-PAN_STEP,-PAN_STEP], ne:[PAN_STEP,-PAN_STEP], sw:[-PAN_STEP,PAN_STEP], se:[PAN_STEP,PAN_STEP],
|
||||
};
|
||||
const navPanel = document.getElementById('navControls');
|
||||
navPanel.addEventListener('pointerdown', e => {
|
||||
const btn = e.target.closest('button');
|
||||
if (!btn) return;
|
||||
switch (btn.dataset.cmd) {
|
||||
case 'up': panCamera(0, -PAN_STEP); break;
|
||||
case 'down': panCamera(0, PAN_STEP); break;
|
||||
case 'left': panCamera(-PAN_STEP, 0); break;
|
||||
case 'right': panCamera( PAN_STEP, 0); break;
|
||||
case 'nw': panCamera(-PAN_STEP, -PAN_STEP); break;
|
||||
case 'ne': panCamera( PAN_STEP, -PAN_STEP); break;
|
||||
case 'sw': panCamera(-PAN_STEP, PAN_STEP); break;
|
||||
case 'se': panCamera( PAN_STEP, PAN_STEP); break;
|
||||
}
|
||||
e.preventDefault();
|
||||
const d = PAN_DIRS[btn.dataset.cmd];
|
||||
if (!d) return;
|
||||
stopPanHold();
|
||||
panCamera(d[0], d[1]); // 立即响应一次
|
||||
panTimer = setInterval(() => panCamera(d[0], d[1]), PAN_HOLD_MS); // 长按连续平移
|
||||
});
|
||||
// 键盘方向键 = 等效于方向按钮(按住时系统自动重复 keydown → 连续平移)
|
||||
window.addEventListener('pointerup', stopPanHold);
|
||||
window.addEventListener('pointercancel', stopPanHold);
|
||||
// 键盘方向键 + WASD = 等效于方向按钮(按住时系统自动重复 keydown → 连续平移)
|
||||
const KEY_PAN = {
|
||||
ArrowUp: [0, -PAN_STEP],
|
||||
ArrowDown: [0, PAN_STEP],
|
||||
ArrowLeft: [-PAN_STEP, 0],
|
||||
ArrowRight: [ PAN_STEP, 0],
|
||||
ArrowUp: [0, -PAN_STEP], ArrowDown: [0, PAN_STEP],
|
||||
ArrowLeft: [-PAN_STEP, 0], ArrowRight: [ PAN_STEP, 0],
|
||||
w: [0, -PAN_STEP], s: [0, PAN_STEP],
|
||||
a: [-PAN_STEP, 0], d: [ PAN_STEP, 0],
|
||||
W: [0, -PAN_STEP], S: [0, PAN_STEP],
|
||||
A: [-PAN_STEP, 0], D: [ PAN_STEP, 0],
|
||||
};
|
||||
window.addEventListener('keydown', e => {
|
||||
if (!(e.key in KEY_PAN)) return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user