game/website/intro.html
李鹏宇 760cd517c2 feat: 规则货币合并+规则类别配置菜单 + 世界数据大报表 + 背景/论坛同步
后端 game_base:
- 货币并入 game.rule(rate/note 字段),新增可配置规则类别 game.rule.category(world/faction/character/currency)
- 新增「配置」菜单与规则类别配置子项
- /game/api/rules 按类别分组返回,/game/api/currencies 保持兼容
- 列表分组取消等级累计(aggregator=False,适配 Odoo18)
- 1.2 pre-migrate 清理旧 game.currency 记录,避免外部ID冲突

前端/官网:
- 世界数据大报表(index.html + .wd-* 样式)
- 背景 5 板块同步、论坛汉化、过渡资源与 views 拆分(announcement/codex/forum/page/rule/story)
2026-07-24 17:58:17 +08:00

315 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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;
}
#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,.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; 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: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: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:14px; margin-bottom:32px; }
h2 {
font-size:22px; color:#e0e0e0; margin:36px 0 14px; padding-left:12px;
border-left:3px solid #4af;
}
h3 {
font-size:17px; color:#d0dce8; margin:20px 0 10px;
}
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; }
/* ===== 其他面板通用 ===== */
.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>
<body>
<canvas id="particles"></canvas>
<nav>
<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>
</nav>
<div class="page">
<!-- ========== 介绍 ========== -->
<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>
<!-- ========== 背景(特色 + 世界背景 + 致玩家 整合) ========== -->
<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="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="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 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">官方通知 · ANNOUNCEMENTS</p>
<div id="announce" class="announce-list" aria-live="polite"></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 = [];
function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }
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+.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);}
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>