diff --git a/addons/game_base/__manifest__.py b/addons/game_base/__manifest__.py index 8fd8aba..c94b53d 100644 --- a/addons/game_base/__manifest__.py +++ b/addons/game_base/__manifest__.py @@ -8,6 +8,7 @@ 'data': [ 'security/ir.model.access.csv', 'data/game_base_data.xml', + 'data/story_cron.xml', 'views/codex_views.xml', 'views/rule_category_views.xml', 'views/rule_views.xml', diff --git a/addons/game_base/data/game_base_data.xml b/addons/game_base/data/game_base_data.xml index df6d5f5..9aef2e1 100644 --- a/addons/game_base/data/game_base_data.xml +++ b/addons/game_base/data/game_base_data.xml @@ -166,191 +166,6 @@ 宇森官网第一阶段上线:介绍 / 规则 / 图鉴 / 论坛 / 公告 五块,单页切换呈现。欢迎园丁们入驻。

]]>
- - - - features - 游戏特色 - CORE FEATURES - 10 - - - - 10 - 🌐 - 垂直五层世界 - 从地心熔岩到陨石星空,文明在五个垂直切面中同时生长,每一层都有独立的生态、资源与事件池。 - - - - 20 - 🌱 - 自发演化 - 不是脚本,不是关卡。文明自己生长、分裂、兼并、飞升——每一次开局都是一部独一无二的编年史。 - - - - 30 - 🧑‍🌾 - 园丁视角 - 你不是英雄,也不是上帝。你是园丁——播种灵气、引导势力、守望文明,在关键节点轻推一把,让世界自洽前行。 - - - - 40 - ⚖️ - 三维等级联动 - 世界等级定舞台上限,势力等级定群体进度,角色等级定个体极限。三者交叉构成无限策略空间。 - - - - 50 - 💰 - 六档货币体系 - 从墟铢到元玺,百进制兑换贯穿五层世界。跨层贸易、资源调度、神器交易构成完整经济闭环。 - - - - 60 - 🎭 - 活的个体 - 每个 NPC 都有独立的天赋、性格与成长轨迹。英雄在历练中觉醒,传奇在时间中自发生成,无人是棋子。 - - - - - intro - 关于宇森 - ABOUT YUSEN - 15 - 宇森,是一颗会自己呼吸的种子。

-

你播种的不是一个角色,而是一个世界——从地心熔岩到陨石星空,五层垂直切面层层相托,文明在其中自发地生长、分裂、兼并、飞升。没有脚本替你演绎剧情,没有关卡替你定义胜利,每一次开局都是一部独一无二的编年史。

-

我们相信,最好的模拟不在于你操控了多少,而在于你见证了多少。所以宇森把「玩家」重新定义为园丁:你不能命令谁,却可以改变土壤、调节灵气、在关键节点轻轻推一把。世界的走向,由无数个体的选择汇聚而成,而你只是其中一股温柔的力。

-

往下翻,看这颗种子如何长成一片森林。

]]>
-
- - - 10 - 🌍 - 一颗会呼吸的世界 - 五层垂直世界,文明自发演化,没有固定剧情与通关条件。 - - - - 20 - 🧑‍🌾 - 你是园丁,不是上帝 - 改变土壤与灵气,在关键节点轻推一把,而非发号施令。 - - - - - background - 世界起源 - BACKGROUND - 20 - 天地初开,混沌未分。一缕灵气自地心涌出,沿垂直切面向上攀爬——穿透岩层、跨越地表、掠过云海、直抵星空。五层世界自此苏醒。

-

地表层最先活过来。灵气稀薄,但足够点燃文明的火种。各族散居荒野,狩猎采集,以血缘结群,在蒙昧的凡土上挣扎求生。这是凡墟

-

而后灵气渐浓,天地共振。聚落涌现,分工萌芽,物物交换催生最早的势力。灵启的时代,文明开始有了形状。

-

再往后,是兼并、是征伐、是道统与造物的竞逐。玄界的大地上,多元族群共治的完整疆域成形,传奇事件如潮涌来。

-

直到有一天,有人掘穿了地表之下的黑暗,有人驾着浮空石飞上了云海。地下深渊与浩瀚星空同时向文明敞开——穹冥时代,天地上下尽数纳入管辖。

-

最终,顶尖文明勘破了这片天地的诞生本源。他们追溯灵气的源头,在地心深处找到了答案。飞升、轮回、更高维度的演化实验——溯元,不是终点,而是新的起点。

-

而你,是这一切的园丁。

]]>
-
- - - 10 - - 星空层 - 陨石科技带 · 穹鎏产地 - - - - 20 - - 天空层 - 云朵地基块 · 浮空城邦 - - - - 30 - - 地表层 - 文明发源地 · 主舞台 - - - - 40 - - 地下层 - 矿脉深渊 · 冥玉产地 - - - - 50 - - 地心层 - 本源之地 · 元玺凝聚 - - - - - notes - 制度说明 - HOW THE WORLD WORKS - 25 - 宇森的世界由三套相互独立的等级体系共同驱动,它们彼此加成,又彼此制约:

-

世界等级是舞台的上限——它决定这片土地上「能发生什么」。从凡墟到溯元,每一级跃迁都会解锁更深的内容层(更深的地下、更远的星空)与更厚的事件池。

-

势力等级是群体的进度——它决定文明「能调动什么」。聚落、联盟到多维联合体,等级越高,可调度资源与外交权重越大。

-

角色等级是个体的成长——它决定一个生命「能做成什么」。从无名萌新到超越生死的永恒,英雄在历练中觉醒。

-

而贯穿三者的,是六档货币:从凡俗墟铢到本源元玺,百进制兑换构成完整经济闭环,决定文明「能交换什么」。

-

园丁的核心策略,便是在这四方之间分配有限的引导资源,让文明自洽地向上演化——每一级提升,世界都因此越活越厚。

]]>
-
- - - 10 - 🌐 - 世界等级 - 舞台上限:决定能发生什么。凡墟 → 灵启 → 玄界 → 穹冥 → 溯元。 - - - - 20 - 🏛️ - 势力等级 - 群体进度:决定能调动什么。墟族 → 灵聚 → 玄邑 → 冥朝 → 穹统 → 溯盟。 - - - - 30 - ⚔️ - 角色等级 - 个体成长:决定能做成什么。萌新 → …… → 永恒,十二阶序。 - - - - 40 - 💰 - 六档货币 - 经济闭环:决定能交换什么。墟铢 → 灵币 → 玄锭 → 冥玉 → 穹鎏 → 元玺。 - - - - - letter - 致园丁 - A LETTER TO GARDENERS - 30 - 致每一位踏入宇森的园丁:

-

感谢你来到这里。

-

宇森不是一款传统的策略游戏。这里没有关卡,没有通关,没有固定的胜利条件。我们想做的,是一个会自己呼吸的世界——你播下一颗种子,它自己长成一片森林;你引导一个族群,它自己走出一段历史。

-

你是园丁,不是上帝。你不能命令谁去做什么,但你可以改变土壤、调节灵气、在关键节点轻轻推一把。文明的走向由无数个体的选择汇聚而成,而你只是其中一股温柔的力。

-

这个世界有五层。从地心到星空,每一层都有自己的生态和故事。文明会从地表的凡墟开始,一路向上向下生长,直到触及世界的本源。这个过程可能很慢,也可能充满意外——但那正是我们想要的。

-

我们还在生长。宇森目前处于早期阶段,很多系统仍在打磨。但你看到的每一个种族、每一件神器、每一段传奇事件,都是这个世界真实存在的一部分。它们会随着版本的推进越来越厚,越来越活。

-

如果你在这里找到了哪怕一刻的沉浸感,那就是我们最大的成功。

-

欢迎播种。

]]>
-
- 【公告】宇森官网第一阶段上线,欢迎园丁们入驻 @@ -404,59 +219,8 @@ 剧情站正式上线!首卷《园丁手记·凡墟篇》已开放阅读,记录灵气初醒、第一株苗与聚落第一缕光的蒙昧年代。前往导航「剧情」即可翻阅。

]]>
- - - 第一卷 · 园丁手记·凡墟篇 - 10 - 凡墟篇·园丁手记。灵气初醒的蒙昧年代,文明的种子刚刚落进泥土。

]]>
-
- - - 10 - 楔子 · 灵气初醒 - 天地初开的时候,没有名字。

-

只有一团混沌的灵气,从地心最深处艰难地往上爬——它穿过滚烫的岩浆,穿过沉默的岩层,在某个谁也说不清的时刻,第一次触到了冰冷的地表。

-

那一刻,凡土醒了。

-

后来的人把它叫做「灵气初醒」。但在当时,没有谁记得这一刻。只有风,把第一缕微弱的生机,吹向了荒野里零散的族群。

]]>
-
- - - 20 - 第一章 · 第一株苗 - 园丁第一次伸手,是在一片荒芜的斜坡上。

-

他什么也没做,只是把掌心的灵气,轻轻按进了一粒埋在石缝里的种子。没有咒语,没有仪式——只是给予,然后等待。

-

三天后,一株嫩绿的苗破土而出。它不知道自己被谁照料,也不在乎。它只是生长,像这个世界本该有的样子。

-

园丁看着那点绿色,忽然明白了自己该做的事:不是统治,不是创造,而是——让生长发生。

]]>
-
- - - 30 - 第二章 · 聚落的第一缕光 - 苗活下来之后,荒野里有了第二个、第三个生命。

-

零散的族群循着灵气的痕迹聚拢。他们不再只是狩猎采集,开始用碎石垒出简陋的屋舍,用兽骨交换 surplus 的猎物。第一缕属于「文明」的光,在凡墟的夜色里亮起。

-

园丁没有现身。他只是把灵气的浓度,往聚落的方向,悄悄调浓了半分。

]]>
-
- - - 第二卷 · 园丁手记·灵启篇 - 20 - 灵启篇·园丁手记。当聚落连成部族,争与和同时降临。

]]>
-
- - - 10 - 第一章 · 部族初成 - 灵气浓到一定程度,聚落开始兼并。

-

三个相邻的族群,在一场没有流血的议事之后,决定共用一口深井。这是凡墟历史上第一次「联盟」——没有王,只有约定。

-

园丁在云端记下了这一笔。他知道,从今往后,世界不再只是一群野生的人,而是一个会自己谈判、自己妥协的活物。

]]>
-
- - - 20 - 第二章 · 交易与争端 - 联盟带来了繁荣,也带来了第一场争端。

-

东边的墟族说井水该归他们,西边的人不肯。争执了七天,最后是园丁在不经意间,让井边多长出一片可食用的苔藓——谁都能采,谁都不必独占。

-

争端平息的方式,从来不在胜负,而在有没有第三种选择。这是园丁学到的第二课。

]]>
-
+ diff --git a/addons/game_base/data/story_cron.xml b/addons/game_base/data/story_cron.xml new file mode 100644 index 0000000..17c1fa8 --- /dev/null +++ b/addons/game_base/data/story_cron.xml @@ -0,0 +1,18 @@ + + + + + 宇森·小说 Markdown 同步 + + code + model.action_sync_novel() + 5 + minutes + + + + diff --git a/addons/game_base/models/story.py b/addons/game_base/models/story.py index 9e35231..3d57ac3 100644 --- a/addons/game_base/models/story.py +++ b/addons/game_base/models/story.py @@ -1,5 +1,12 @@ # -*- coding: utf-8 -*- +import os +import sys +import subprocess +import logging from odoo import models, fields +from odoo.modules.module import get_module_path + +_logger = logging.getLogger(__name__) class GameStoryVolume(models.Model): @@ -13,6 +20,30 @@ class GameStoryVolume(models.Model): intro = fields.Html(string='卷首语') chapter_ids = fields.One2many('game.story.chapter', 'volume_id', string='章节') + def action_sync_novel(self): + """由定时任务(ir.cron)调用:把 novel/ 下的 Markdown 增量同步进数据库。 + 走独立同步脚本(novel/sync_novel.py),单一实现来源。 + 注意:脚本依赖 venv 的 psycopg2,而 Odoo 可能跑在系统 Python 上, + 因此显式使用项目 venv 的 python 执行,而非 sys.executable。""" + base = get_module_path('game_base') + script = os.path.normpath(os.path.join(base, '..', '..', 'novel', 'sync_novel.py')) + if not os.path.exists(script): + _logger.warning('小说同步脚本不存在: %s', script) + return + # 优先用项目 venv 的 python(自带 psycopg2);否则退化为当前解释器 + venv_py = os.path.normpath(os.path.join(base, '..', '..', '..', '.venv', 'Scripts', 'python.exe')) + python = venv_py if os.path.exists(venv_py) else sys.executable + try: + proc = subprocess.run([python, script], + capture_output=True, text=True, timeout=180) + if proc.returncode != 0: + _logger.error('小说同步失败(%s): %s', proc.returncode, (proc.stderr or '')[-2000:]) + else: + last = (proc.stdout or '').strip().splitlines() + _logger.info('小说同步完成: %s', last[-1] if last else 'ok') + except Exception as e: + _logger.exception('小说同步异常: %s', e) + class GameStoryChapter(models.Model): _name = 'game.story.chapter' diff --git a/addons/yt_world/__manifest__.py b/addons/yt_world/__manifest__.py index 3468db0..f78d80e 100644 --- a/addons/yt_world/__manifest__.py +++ b/addons/yt_world/__manifest__.py @@ -7,6 +7,10 @@ 'depends': ['base', 'web'], 'data': [ 'security/ir.model.access.csv', + 'data/codex_resource.xml', + 'data/codex_creature.xml', + 'data/codex_monster.xml', + 'data/codex_race.xml', 'views/yt_world_views.xml', ], 'assets': { diff --git a/addons/yt_world/controllers/world_api.py b/addons/yt_world/controllers/world_api.py index 0b4b6e5..44ade88 100644 --- a/addons/yt_world/controllers/world_api.py +++ b/addons/yt_world/controllers/world_api.py @@ -9,11 +9,22 @@ _logger = logging.getLogger(__name__) def _json(data): - """返回 UTF-8 JSON 响应(与 game_base 一致)。""" - return Response( - json.dumps(data, ensure_ascii=False), - content_type='application/json; charset=utf-8', - ) + """返回 UTF-8 JSON 响应(与 game_base 一致),并附带 CORS 头以支持跨域(8080→8069)。""" + headers = { + 'Content-Type': 'application/json; charset=utf-8', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type', + } + try: + origin = request.httprequest.headers.get('Origin') + if origin: + headers['Access-Control-Allow-Origin'] = origin + headers['Access-Control-Allow-Credentials'] = 'true' + else: + headers['Access-Control-Allow-Origin'] = '*' + except Exception: + headers['Access-Control-Allow-Origin'] = '*' + return Response(json.dumps(data, ensure_ascii=False), headers=headers) def _post_body(): @@ -86,3 +97,46 @@ class YtWorldApi(http.Controller): world.name = name _logger.info('yt.world rename uid=%s %r -> %r', user.id, old, name) return _json({'ok': True, 'name': name}) + + # ========== 图鉴 API ========== + + def _records_json(self, model, fields): + """将 model 的全部记录转为 [{field: value}, ...] 格式(Json 字段解析为对象)。""" + recs = request.env[model].sudo().search([], order='sequence, id') + out = [] + for r in recs: + item = {} + for f in fields: + val = getattr(r, f) + if isinstance(val, str): + try: + val = json.loads(val) + except (ValueError, TypeError): + pass + item[f] = val + out.append(item) + return _json(out) + + @http.route('/yt_world/api/codex/resources', type='http', auth='public', + methods=['GET', 'OPTIONS'], csrf=False) + def codex_resources(self, **kw): + return self._records_json('yt.codex.resource', + ['code','name','icon','category','quality','source','uses','recipe','description']) + + @http.route('/yt_world/api/codex/creatures', type='http', auth='public', + methods=['GET', 'OPTIONS'], csrf=False) + def codex_creatures(self, **kw): + return self._records_json('yt.codex.creature', + ['code','name','icon','habitat','tameable','diet','products','description']) + + @http.route('/yt_world/api/codex/monsters', type='http', auth='public', + methods=['GET', 'OPTIONS'], csrf=False) + def codex_monsters(self, **kw): + return self._records_json('yt.codex.monster', + ['code','name','icon','tier','level_min','level_max','habitat','drops','description']) + + @http.route('/yt_world/api/codex/races', type='http', auth='public', + methods=['GET', 'OPTIONS'], csrf=False) + def codex_races(self, **kw): + return self._records_json('yt.codex.race', + ['code','name','icon','layer','traits','description']) diff --git a/addons/yt_world/data/codex_creature.xml b/addons/yt_world/data/codex_creature.xml new file mode 100644 index 0000000..f88e37b --- /dev/null +++ b/addons/yt_world/data/codex_creature.xml @@ -0,0 +1,103 @@ + + + + cow🐄 + 草原1植食 + ["肉","牛奶","皮革"]10 + + + sheep🐑 + 草原1植食 + ["肉","羊毛"]20 + + + horse🐎 + 草原1植食 + ["骑乘运输","骑兵"]30 + + + pig🐖 + 林地/村落1杂食 + ["肉","鬃毛"]40 + + + chicken🐔 + 村落/草地1杂食 + ["蛋","肉","羽毛"]50 + + + duck🦆 + 湿地/河岸1杂食 + ["蛋","肉","羽绒"]60 + + + dog🐕 + 村落/营地1杂食 + ["护卫","狩猎协助"]70 + + + cat🐈 + 村落1肉食 + ["捕鼠","幸运"]80 + + + rabbit🐇 + 林地/草地1植食 + ["肉","毛皮"]90 + + + squirrel松鼠🐿️ + 森林植食 + ["坚果","藏宝指引"]100 + + + hedgehog刺猬🦔 + 林地/村落1杂食 + ["虫害防治"]110 + + + bee🐝 + 花海/林地1植食 + ["蜂蜜","授粉"]120 + + + butterfly🦋 + 花海/草地植食 + ["灵粉","生态指示"]130 + + + deer鹿🦌 + 森林植食 + ["肉","鹿皮","鹿角"]140 + + + fox🦊 + 林地/丘陵肉食 + ["狐皮","灵性"]150 + + + wolf🐺 + 森林/山地肉食 + ["狼皮","利齿"]160 + + + snake🐍 + 丛林/岩洞肉食 + ["蛇毒","蛇皮","灵性"]170 + + + eagle🦅 + 高山/天空肉食 + ["巨羽","视野共享"]180 + + + fish🐟 + 河流/湖泊杂食 + ["鱼肉","鱼骨","鱼苗"]190 + + + turtle🐢 + 河岸/湖泊1植食 + ["龟甲盾","龟蛋","长寿"]200 + + diff --git a/addons/yt_world/data/codex_monster.xml b/addons/yt_world/data/codex_monster.xml new file mode 100644 index 0000000..13973d0 --- /dev/null +++ b/addons/yt_world/data/codex_monster.xml @@ -0,0 +1,266 @@ + + + + + grasswolf草原狼🐺 + normal13 + 草原 + ["兽骨","毛皮"]10 + + + deer大角鹿🦌 + normal24 + 草原 + ["兽骨","毛皮","鹿角"]20 + + + molerat地鼠群🐭 + normal12 + 草原 + ["兽骨","毛皮"]30 + + + boar野猪🐗 + normal35 + 草原 + ["肉","兽骨","鬃毛"]40 + + + spider林间蜘蛛🕷️ + normal35 + 森林 + ["蛛丝","毒囊"]50 + + + mushroom毒蘑菇精🍄 + normal46 + 森林 + ["菌丝","草本"]60 + + + treant树精🌳 + normal58 + 森林 + ["木材","树脂","露珠"]70 + + + thornsnake荆棘蛇🐍 + normal46 + 森林 + ["蛇皮","蛇毒"]80 + + + maddog疯狗🐕‍🦺 + normal35 + 森林 + ["兽骨","毛皮"]90 + + + golem石傀儡🗿 + normal710 + 山地 + ["石材","矿石"]100 + + + scorpion岩蝎🦂 + normal69 + 山地 + ["鳞片","毒囊"]110 + + + mandrill山魈🐒 + normal68 + 山地 + ["毛皮","兽骨"]120 + + + sandworm沙虫🪱 + normal710 + 沙漠 + ["鳞片","兽骨"]130 + + + desertscorp荒漠蝎🦂 + normal69 + 沙漠 + ["毒囊","鳞片"]140 + + + vulture秃鹫🦅 + normal58 + 沙漠 + ["羽毛","兽骨"]150 + + + crocodile河鳄🐊 + normal69 + 水域 + ["鳞片","皮革"]160 + + + watersnake水蛇🐍 + normal46 + 水域 + ["蛇皮","蛇毒"]170 + + + frog巨蛙🐸 + normal58 + 水域 + ["鳞片","露珠"]180 + + + junglepython丛林蟒🐍 + normal812 + 丛林 + ["蛇皮","兽骨","肉"]190 + + + mosquito吸血蚊群🦟 + normal710 + 丛林 + ["露珠","毛皮"]200 + + + ape丛林猿🦧 + normal710 + 丛林 + ["毛皮","木材"]210 + + + cavespider洞穴巨蛛🕷️ + normal1014 + 洞穴 + ["蛛丝","毒囊","兽骨"]220 + + + bat蝙蝠群🦇 + normal812 + 洞穴 + ["羽毛","兽骨"]230 + + + ghost幽魂👻 + normal1216 + 洞穴 + ["灵气","露珠"]240 + + + crystal晶石怪💎 + normal1015 + 洞穴 + ["矿石","水晶"]250 + + + nightmare夜魇🌑 + normal1216 + 特殊 + ["灵气","露珠"]260 + + + fogwalker雾行者🌫️ + normal1014 + 特殊 + ["露珠","羽毛"]270 + + + zombie饥饿尸💀 + normal812 + 特殊 + ["兽骨","灵气"]280 + + + thunderbird雷鸟 + normal1014 + 高山 + ["羽毛","水晶"]290 + + + windspirit风精🌪️ + normal812 + 高山 + ["露珠","羽毛"]300 + + + + + wolfking狼王🐺✨ + elite1518 + 草原 + ["毛皮","兽骨","狼王牙"]10 + + + ancienttreant远古树精🌳✨ + elite1822 + 森林 + ["木材","树脂","生命露珠"]20 + + + lavagiant熔岩巨人🔥✨ + elite2025 + 山地 + ["石材","矿石","火焰水晶"]30 + + + shadow幽影刺客👤✨ + elite1822 + 洞穴 + ["蛛丝","毒囊","暗影精华"]40 + + + scorpionking毒蝎王🦂✨ + elite1620 + 沙漠 + ["鳞片","毒囊","蝎王毒针"]50 + + + frostlizard冰霜蜥蜴🦎✨ + elite2024 + 特殊 + ["鳞片","水晶","寒冰核心"]60 + + + thundereagle雷霆鹰🦅✨ + elite1822 + 高山 + ["羽毛","水晶","雷击木"]70 + + + spiderqueen腐毒蛛后🕷️✨ + elite2026 + 洞穴 + ["蛛丝","毒囊","蛛后腺体"]80 + + + waterlord水元素之主💧✨ + elite1620 + 水域 + ["露珠","鳞片","水灵珠"]90 + + + crystalguard水晶守卫💎✨ + elite2228 + 洞穴 + ["水晶","矿石","纯净晶核"]100 + + + + + nidhogg远古巨龙·尼德霍格🐉 + boss5060 + 世界之巅·龙巢 + ["龙鳞","龙牙","龙晶","灵气"]10 + + + kraken深渊之主·克拉肯🦑 + boss4555 + 无尽深渊·地下海 + ["深渊触须","深渊之核","黑珍珠","灵气"]20 + + + creator创世之灵·宇 + boss6060 + 星空层·世界核心 + ["创世碎片","世界之种"]30 + + diff --git a/addons/yt_world/data/codex_race.xml b/addons/yt_world/data/codex_race.xml new file mode 100644 index 0000000..d1562b0 --- /dev/null +++ b/addons/yt_world/data/codex_race.xml @@ -0,0 +1,78 @@ + + + + human人族👤 + 大地 + ["适应力","雄心"]10 + + + elf精灵🧝 + 森林 + ["自然共鸣","长生"]20 + + + dwarf矮人⛏️ + 山脉 + ["锻造专精","坚毅"]30 + + + orc兽人🐗 + 荒原 + ["战意","蛮力"]40 + + + demon魔族😈 + 深渊 + ["混沌之力","契约"]50 + + + yao妖族🦊 + 全境 + ["化形","灵觉"]60 + + + angel天使 + 天空 + ["圣光之翼","净化"]70 + + + undead亡灵💀 + 地下 + ["不死","恐惧免疫"]80 + + + dragon龙族🐉 + 全境 + ["太古之力","龙息"]90 + + + goblin地精👹 + 地下 + ["巧手","商业嗅觉"]100 + + + giant巨人🦶 + 高山 + ["巨力","坚韧"]110 + + + elemental元素灵🌪️ + 全境 + ["元素亲和","灵体"]120 + + + mechanical机械⚙️ + 遗迹 + ["太古知识","机械之躯"]130 + + + halforc半兽人🐾 + 边境 + ["双重血统","体能"]140 + + + naga娜迦🐍 + 水域 + ["两栖","水元素亲和"]150 + + diff --git a/addons/yt_world/data/codex_resource.xml b/addons/yt_world/data/codex_resource.xml new file mode 100644 index 0000000..c52ee13 --- /dev/null +++ b/addons/yt_world/data/codex_resource.xml @@ -0,0 +1,356 @@ + + + + + wood木材🪵 + materialcommon + 森林地块建造建筑、加工木板、燃料 + 10 + + + stone石材🪨 + materialcommon + 山地/岩层地块建造石质建筑、加工石料、铺路 + 20 + + + ore矿石⛏️ + materialuncommon + 深层矿脉/地下世界冶炼金属、锻造工具、科技升级 + 30 + + + herb草本🌿 + materialcommon + 绿地/林地地块制药、烹饪、灵气转化 + 40 + + + meat🍖 + materialcommon + 狩猎/畜牧食物消耗、烹饪加工、人口维持 + 50 + + + hide毛皮🧶 + materialcommon + 畜牧/狩猎制衣、制作、贸易 + 60 + + + honey蜂蜜🍯 + materialuncommon + 蜂群/花海地块烹饪、制药、贸易 + 70 + + + clay粘土🏺 + materialcommon + 河岸/湿地地块制陶、砖瓦、雕塑 + 80 + + + sand沙子 + materialcommon + 沙漠/河岸地块制玻璃、砂浆、滤水 + 90 + + + vine藤蔓🌱 + materialcommon + 丛林地块制绳索、简易桥梁、捆绑 + 100 + + + fiber纤维🧵 + materialcommon + 草甸/湿地地块织布、造纸、绳索 + 110 + + + resin树脂🫧 + materialuncommon + 针叶林地块制胶、熏香、精炼油 + 120 + + + bone兽骨🦴 + materialcommon + 狩猎/献祭制骨器、装饰、肥料 + 130 + + + feather羽毛🪶 + materialcommon + 鸟类/家禽养殖制箭、书写、装饰 + 140 + + + crystal水晶💎 + materialrare + 岩洞深处/地下世界能量引导、附魔、高级加工 + 150 + + + mycelium菌丝🍄 + materialuncommon + 朽木/地下湿地发酵、制药、生态转化 + 160 + + + dew露珠💧 + materialuncommon + 清晨草地/特殊气候制药、酿酒、灵气调和 + 170 + + + scale鳞片🐟 + materialuncommon + 水域/水族狩猎制甲、装饰、研磨入药 + 180 + + + silk蛛丝🕸️ + materialrare + 洞穴深处/特殊生态织极品布匹、弓弦、陷阱 + 190 + + + horn兽角🦌 + materialuncommon + 狩猎大型野兽制器、装饰、药材 + 200 + + + aura灵气 + specialepic + 地心/星空层/文明事件文明跃迁、特殊事件、世界规则改写 + 210 + + + + + plank木板📦 + processeduncommon + 加工台精良建造、家具、机关 + [{"code":"wood","num":3}] + 10 + + + stoneBlock石料🧱 + processeduncommon + 加工台石质建筑、城墙、纪念碑 + [{"code":"stone","num":2}] + 20 + + + brick砖块🧱 + processeduncommon + 窑炉建筑、道路、烟囱 + [{"code":"clay","num":1},{"code":"sand","num":1}] + 30 + + + metal金属锭🔩 + processedrare + 冶炼炉工具升级、武器、机械 + [{"code":"ore","num":3}] + 40 + + + cloth布匹🧵 + processedcommon + 纺织台制衣、装饰、贸易 + [{"code":"fiber","num":2}] + 50 + + + driedMeat风干肉🥩 + processedcommon + 晒晾架食物储备、远征、贸易 + [{"code":"meat","num":2}] + 60 + + + rope绳索🪢 + processedcommon + 编织台攀爬、捆绑、建筑 + [{"code":"vine","num":2},{"code":"fiber","num":1}] + 70 + + + paper纸张📄 + processedcommon + 造纸坊书写、记录、贸易 + [{"code":"fiber","num":2}] + 80 + + + charcoal木炭🔥 + processedcommon + 炭窑冶炼燃料、保温、滤材 + [{"code":"wood","num":2}] + 90 + + + glass玻璃🔮 + processeduncommon + 熔炉窗户、器皿、透镜 + [{"code":"sand","num":2},{"code":"charcoal","num":1}] + 100 + + + pottery陶器🏺 + processedcommon + 陶轮储物、烹饪、盛水 + [{"code":"clay","num":2}] + 110 + + + leather皮革🟫 + processeduncommon + 鞣制坊制甲、行囊、马具 + [{"code":"hide","num":1},{"code":"herb","num":2}] + 120 + + + blanket毛毯🧣 + processeduncommon + 裁缝台保暖、贸易、床品 + [{"code":"hide","num":2},{"code":"cloth","num":1}] + 130 + + + wine果酒🍷 + processeduncommon + 酿酒坊饮食、贸易、仪式 + [{"code":"honey","num":2},{"code":"dew","num":1}] + 140 + + + smokedMeat熏肉🍖 + processeduncommon + 熏房食物储备、贸易、远行 + [{"code":"meat","num":1},{"code":"wood","num":1}] + 150 + + + salve草药膏🩹 + processeduncommon + 药臼疗伤、解毒、护肤 + [{"code":"herb","num":2},{"code":"resin","num":1}] + 160 + + + oil精炼油🫒 + processedrare + 蒸馏器照明、润滑、制药 + [{"code":"resin","num":2},{"code":"charcoal","num":1}] + 170 + + + mortar砂浆🪣 + processedcommon + 搅拌池砌墙、铺路、雕塑 + [{"code":"sand","num":1},{"code":"clay","num":1}] + 180 + + + steel钢锭⚙️ + processedepic + 高炉武器、装甲、高级机械 + [{"code":"metal","num":2},{"code":"ore","num":1}] + 190 + + + alloy灵晶合金🔷 + processedlegendary + 灵炉神器、图腾、终极装备 + [{"code":"steel","num":1},{"code":"crystal","num":1},{"code":"aura","num":1}] + 200 + + + potion灵药剂🧪 + processedrare + 炼药台疗伤、增益、事件触发 + [{"code":"herb","num":3},{"code":"aura","num":1}] + 210 + + + wallPanel木板墙🚧 + processeduncommon + 木工台建筑墙体、围栏、哨塔 + [{"code":"plank","num":3}] + 220 + + + stoneBrick石砖🗿 + processedrare + 石工台石质建筑、城堡、纪念碑 + [{"code":"stoneBlock","num":2}] + 230 + + + tile瓷砖 + processedcommon + 窑炉屋面、地面、装饰 + [{"code":"clay","num":1}] + 240 + + + silkCloth丝绸🎀 + processedrare + 精纺机极品衣饰、贸易、图腾 + [{"code":"silk","num":2}] + 250 + + + dye染料🎨 + processeduncommon + 染缸染色、装饰、贸易 + [{"code":"herb","num":2}] + 260 + + + spice香料🫘 + processeduncommon + 研钵烹饪、贸易、仪式 + [{"code":"herb","num":3}] + 270 + + + candle蜡烛🕯️ + processedcommon + 制烛台照明、仪式、温暖 + [{"code":"resin","num":1},{"code":"fiber","num":1}] + 280 + + + medicatedWine药酒⚗️ + processedrare + 药酒窖疗伤、增益、御寒 + [{"code":"wine","num":1},{"code":"salve","num":1}] + 290 + + + runeStone符文石🪨 + processedepic + 符文工坊附魔、仪式、地脉节点 + [{"code":"stoneBlock","num":1},{"code":"aura","num":1}] + 300 + + + fertilizer堆肥🌱 + processedcommon + 堆肥池农业增产、生态修复 + [{"code":"bone","num":1},{"code":"herb","num":2}] + 310 + + + gear精密齿轮⚙️ + processedrare + 精密工坊机械制造、机关、自动化 + [{"code":"metal","num":2}] + 320 + + diff --git a/addons/yt_world/models/__init__.py b/addons/yt_world/models/__init__.py index de42408..078413d 100644 --- a/addons/yt_world/models/__init__.py +++ b/addons/yt_world/models/__init__.py @@ -2,3 +2,4 @@ from . import yt_world from . import yt_world_tile from . import yt_world_tile_micro from . import yt_resource +from . import yt_codex diff --git a/addons/yt_world/models/yt_codex.py b/addons/yt_world/models/yt_codex.py new file mode 100644 index 0000000..debdc1e --- /dev/null +++ b/addons/yt_world/models/yt_codex.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields + + +class YtCodexResource(models.Model): + """图鉴-资源(材料/加工品/特殊)""" + _name = 'yt.codex.resource' + _description = '图鉴-资源' + _order = 'category, sequence, id' + + name = fields.Char(string='名称', required=True, translate=True) + code = fields.Char(string='编码', required=True, index=True) + icon = fields.Char(string='图标', default='📦') + category = fields.Selection([ + ('material', '材料'), + ('processed', '加工品'), + ('special', '特殊'), + ], string='分类', required=True, default='material') + quality = fields.Selection([ + ('common', '普通'), + ('uncommon', '优秀'), + ('rare', '精良'), + ('epic', '史诗'), + ('legendary', '传说'), + ], string='基准品质', default='common') + description = fields.Char(string='描述') + source = fields.Char(string='来源') + uses = fields.Char(string='用途') + recipe = fields.Json(string='配方') # [{"code":"wood","num":3}] + sequence = fields.Integer(string='排序', default=10) + + +class YtCodexCreature(models.Model): + """图鉴-生物""" + _name = 'yt.codex.creature' + _description = '图鉴-生物' + _order = 'sequence, id' + + name = fields.Char(string='名称', required=True, translate=True) + code = fields.Char(string='编码', required=True, index=True) + icon = fields.Char(string='图标', default='🐾') + habitat = fields.Char(string='栖息地') + tameable = fields.Boolean(string='可驯养', default=False) + diet = fields.Char(string='食性') + products = fields.Json(string='产物') + description = fields.Char(string='描述') + sequence = fields.Integer(string='排序', default=10) + + +class YtCodexMonster(models.Model): + """图鉴-野怪/精英/Boss""" + _name = 'yt.codex.monster' + _description = '图鉴-野怪' + _order = 'tier, level_min, sequence' + + name = fields.Char(string='名称', required=True, translate=True) + code = fields.Char(string='编码', required=True, index=True) + icon = fields.Char(string='图标', default='👹') + tier = fields.Selection([ + ('normal', '普通野怪'), + ('elite', '精英野怪'), + ('boss', 'Boss'), + ], string='等级', required=True, default='normal') + level_min = fields.Integer(string='最小等级', default=1) + level_max = fields.Integer(string='最大等级', default=5) + habitat = fields.Char(string='栖息地') + description = fields.Char(string='描述') + drops = fields.Json(string='掉落') + sequence = fields.Integer(string='排序', default=10) + + +class YtCodexRace(models.Model): + """图鉴-种族""" + _name = 'yt.codex.race' + _description = '图鉴-种族' + _order = 'sequence, id' + + name = fields.Char(string='名称', required=True, translate=True) + code = fields.Char(string='编码', required=True, index=True) + icon = fields.Char(string='图标', default='🧑') + layer = fields.Char(string='所属层域') + description = fields.Char(string='描述') + traits = fields.Json(string='天赋/特性') + sequence = fields.Integer(string='排序', default=10) diff --git a/addons/yt_world/security/ir.model.access.csv b/addons/yt_world/security/ir.model.access.csv index bd1c97f..21cf72c 100644 --- a/addons/yt_world/security/ir.model.access.csv +++ b/addons/yt_world/security/ir.model.access.csv @@ -11,3 +11,11 @@ access_yt_world_tile_micro_portal,yt.world.tile.micro portal,model_yt_world_tile access_yt_resource_user,yt.resource user,model_yt_resource,base.group_user,1,1,1,1 access_yt_resource_public,yt.resource public,model_yt_resource,base.group_public,1,1,1,1 access_yt_resource_portal,yt.resource portal,model_yt_resource,base.group_portal,1,1,1,1 +access_yt_codex_resource_user,yt.codex.resource user,model_yt_codex_resource,base.group_user,1,0,0,0 +access_yt_codex_resource_public,yt.codex.resource public,model_yt_codex_resource,base.group_public,1,0,0,0 +access_yt_codex_creature_user,yt.codex.creature user,model_yt_codex_creature,base.group_user,1,0,0,0 +access_yt_codex_creature_public,yt.codex.creature public,model_yt_codex_creature,base.group_public,1,0,0,0 +access_yt_codex_monster_user,yt.codex.monster user,model_yt_codex_monster,base.group_user,1,0,0,0 +access_yt_codex_monster_public,yt.codex.monster public,model_yt_codex_monster,base.group_public,1,0,0,0 +access_yt_codex_race_user,yt.codex.race user,model_yt_codex_race,base.group_user,1,0,0,0 +access_yt_codex_race_public,yt.codex.race public,model_yt_codex_race,base.group_public,1,0,0,0 diff --git a/addons/yt_world/static/src/js/user_menu.js b/addons/yt_world/static/src/js/user_menu.js index dff6211..f8cbfcc 100644 --- a/addons/yt_world/static/src/js/user_menu.js +++ b/addons/yt_world/static/src/js/user_menu.js @@ -1,31 +1,23 @@ /* @odoo-module */ -import { UserMenu } from "@web/web/user_menu/user_menu"; -import { patch } from "@web/core/utils/patch"; -import { useService } from "@web/core/utils/hooks"; +/** + * 在 Odoo 18 后台用户下拉菜单插入"官网"入口。 + * Odoo 18 用户菜单用 registry 注册制,UserMenu 在 web.assets_frontend_lazy 中。 + */ +import { registry } from "@web/core/registry"; -patch(UserMenu.prototype, "yt_world.UserMenu", { - setup() { - this._super(...arguments); - }, +console.log("[yt_world] user_menu.js 开始执行"); - get menuItems() { - const items = this._super(...arguments) || []; - const websiteItem = { - id: "website_link", - description: "官网", - href: "/", - callback: () => { - window.location.href = "/"; - }, - sequence: 5, - }; - // Insert before "设置" (settings) and after separator-ish area - const insertIndex = items.findIndex(i => i.id === "settings"); - if (insertIndex > -1) { - items.splice(insertIndex, 0, websiteItem); - } else { - items.unshift(websiteItem); - } - return items; - }, +registry.category("user_menuitems").add("website", function (env) { + console.log("[yt_world] 官网菜单项被注册"); + return { + type: "item", + id: "website_link", + description: "官网", + callback: () => { + window.location.href = "/"; + }, + sequence: 45, + }; }); + +console.log("[yt_world] user_menu.js 执行完毕"); diff --git a/announce.html b/announce.html new file mode 100644 index 0000000..fff5d2c --- /dev/null +++ b/announce.html @@ -0,0 +1,127 @@ + + + + + +公告 · 宇森 Yusen + + + + + + +
+
+ + +
+
+
+ +
+
+
+ +
+
+
+
2026-07-20 · 置顶特色
+
宇森官网第一阶段正式上线
+
+

历经三个月打磨,宇森官网第一阶段正式上线。本次更新带来八大板块——介绍、特色、背景、规则、图鉴、论坛、公告、致玩家,全部以独立页面形式呈现,介绍页即一段概念宣传片,让你在三秒内读懂我们要做什么。

+

我们同步开放了社区论坛与图鉴共建入口。欢迎在「论坛」留下你的构想,或在「图鉴」认领尚未完成的种族条目;被采纳的内容,会带上你的署名进入正式世界观。

+
+
+
+ + +
+
+
+
2026-07-22
+
图鉴系统全面升级:204 种族 + 程序化矢量插画
+
+

图鉴系统完成一次大版本升级:种族条目扩充至 204 条,系别、性格、天赋、神器与传奇事件全面扩量,装备槽位对齐「头 / 手 / 胸 / 裤 / 鞋 / 戒指(双)/ 项链 / 宝物」九宫格。

+
+
+
+ +
+
+
+
2026-07-21
+
概念宣传片「五层世界」已发布
+
+

这支片子将成为官网介绍页的自动背景,也会在后续对外宣发中使用。如果你喜欢这种克制的科幻基调,记得在论坛给我们反馈。

+
+
+
+ +
+
+
+
2026-07-18
+
图鉴共建计划启动
+
+

图鉴系统规划正式确定,分为「种族 / 建筑 / 生物 / 神器」四大类目。我们决定把相当一部分条目开放给社区共建。

+

被采纳的投稿会标注作者署名,并永久保留在官方图鉴中。第一批评审将在论坛「共建」分区进行,欢迎有想法的园丁报名。

+
+
+
+ +
+
+
+
2026-07-15
+
首轮封闭内测招募开启
+
+

宇森首轮封闭内测招募正式开启,名额 500 人。我们将优先邀请在论坛持续输出高质量内容的园丁,以及愿意参与玩法共创的核心用户。

+
+
+
+
+
+
+
+ + + + + diff --git a/assets/art/transitions/transition_adventure.png b/assets/art/transitions/transition_adventure.png deleted file mode 100644 index a176842..0000000 Binary files a/assets/art/transitions/transition_adventure.png and /dev/null differ diff --git a/assets/art/transitions/transition_adventure.webp b/assets/art/transitions/transition_adventure.webp new file mode 100644 index 0000000..287762e Binary files /dev/null and b/assets/art/transitions/transition_adventure.webp differ diff --git a/assets/art/transitions/transition_coast.png b/assets/art/transitions/transition_coast.png deleted file mode 100644 index 539910d..0000000 Binary files a/assets/art/transitions/transition_coast.png and /dev/null differ diff --git a/assets/art/transitions/transition_coast.webp b/assets/art/transitions/transition_coast.webp new file mode 100644 index 0000000..fe007ff Binary files /dev/null and b/assets/art/transitions/transition_coast.webp differ diff --git a/assets/art/transitions/transition_dawn.png b/assets/art/transitions/transition_dawn.png deleted file mode 100644 index 16b0f38..0000000 Binary files a/assets/art/transitions/transition_dawn.png and /dev/null differ diff --git a/assets/art/transitions/transition_dawn.webp b/assets/art/transitions/transition_dawn.webp new file mode 100644 index 0000000..f252f6c Binary files /dev/null and b/assets/art/transitions/transition_dawn.webp differ diff --git a/assets/art/transitions/transition_dungeon.png b/assets/art/transitions/transition_dungeon.png deleted file mode 100644 index bc45cbc..0000000 Binary files a/assets/art/transitions/transition_dungeon.png and /dev/null differ diff --git a/assets/art/transitions/transition_dungeon.webp b/assets/art/transitions/transition_dungeon.webp new file mode 100644 index 0000000..03c81b0 Binary files /dev/null and b/assets/art/transitions/transition_dungeon.webp differ diff --git a/assets/art/transitions/transition_forest.png b/assets/art/transitions/transition_forest.png deleted file mode 100644 index 0cd4e6d..0000000 Binary files a/assets/art/transitions/transition_forest.png and /dev/null differ diff --git a/assets/art/transitions/transition_forest.webp b/assets/art/transitions/transition_forest.webp new file mode 100644 index 0000000..e0122b4 Binary files /dev/null and b/assets/art/transitions/transition_forest.webp differ diff --git a/assets/art/transitions/transition_night.png b/assets/art/transitions/transition_night.png deleted file mode 100644 index ba0ff4a..0000000 Binary files a/assets/art/transitions/transition_night.png and /dev/null differ diff --git a/assets/art/transitions/transition_night.webp b/assets/art/transitions/transition_night.webp new file mode 100644 index 0000000..579e844 Binary files /dev/null and b/assets/art/transitions/transition_night.webp differ diff --git a/assets/art/transitions/transition_sky.png b/assets/art/transitions/transition_sky.png deleted file mode 100644 index 1448d27..0000000 Binary files a/assets/art/transitions/transition_sky.png and /dev/null differ diff --git a/assets/art/transitions/transition_sky.webp b/assets/art/transitions/transition_sky.webp new file mode 100644 index 0000000..66bfee2 Binary files /dev/null and b/assets/art/transitions/transition_sky.webp differ diff --git a/assets/art/transitions/transition_skycity.png b/assets/art/transitions/transition_skycity.png deleted file mode 100644 index 64e1c5a..0000000 Binary files a/assets/art/transitions/transition_skycity.png and /dev/null differ diff --git a/assets/art/transitions/transition_skycity.webp b/assets/art/transitions/transition_skycity.webp new file mode 100644 index 0000000..74d535b Binary files /dev/null and b/assets/art/transitions/transition_skycity.webp differ diff --git a/assets/bg/bg_core.jpg b/assets/bg/bg_core.jpg deleted file mode 100644 index d0fe1aa..0000000 Binary files a/assets/bg/bg_core.jpg and /dev/null differ diff --git a/assets/bg/bg_core.png b/assets/bg/bg_core.png deleted file mode 100644 index c33df9c..0000000 Binary files a/assets/bg/bg_core.png and /dev/null differ diff --git a/assets/bg/bg_core.webp b/assets/bg/bg_core.webp new file mode 100644 index 0000000..881e00e Binary files /dev/null and b/assets/bg/bg_core.webp differ diff --git a/assets/bg/bg_earth.jpg b/assets/bg/bg_earth.jpg deleted file mode 100644 index 9491ea8..0000000 Binary files a/assets/bg/bg_earth.jpg and /dev/null differ diff --git a/assets/bg/bg_earth.png b/assets/bg/bg_earth.png deleted file mode 100644 index e020ee7..0000000 Binary files a/assets/bg/bg_earth.png and /dev/null differ diff --git a/assets/bg/bg_earth.webp b/assets/bg/bg_earth.webp new file mode 100644 index 0000000..f5640c4 Binary files /dev/null and b/assets/bg/bg_earth.webp differ diff --git a/assets/bg/bg_sky.jpg b/assets/bg/bg_sky.jpg deleted file mode 100644 index 3af96ca..0000000 Binary files a/assets/bg/bg_sky.jpg and /dev/null differ diff --git a/assets/bg/bg_sky.png b/assets/bg/bg_sky.png deleted file mode 100644 index 1fd4e54..0000000 Binary files a/assets/bg/bg_sky.png and /dev/null differ diff --git a/assets/bg/bg_sky.webp b/assets/bg/bg_sky.webp new file mode 100644 index 0000000..8241c93 Binary files /dev/null and b/assets/bg/bg_sky.webp differ diff --git a/assets/bg/bg_star.jpg b/assets/bg/bg_star.jpg deleted file mode 100644 index d52e504..0000000 Binary files a/assets/bg/bg_star.jpg and /dev/null differ diff --git a/assets/bg/bg_star.png b/assets/bg/bg_star.png deleted file mode 100644 index 71141a8..0000000 Binary files a/assets/bg/bg_star.png and /dev/null differ diff --git a/assets/bg/bg_star.webp b/assets/bg/bg_star.webp new file mode 100644 index 0000000..d5110eb Binary files /dev/null and b/assets/bg/bg_star.webp differ diff --git a/assets/bg/bg_under.jpg b/assets/bg/bg_under.jpg deleted file mode 100644 index 721272e..0000000 Binary files a/assets/bg/bg_under.jpg and /dev/null differ diff --git a/assets/bg/bg_under.png b/assets/bg/bg_under.png deleted file mode 100644 index 6ab21a6..0000000 Binary files a/assets/bg/bg_under.png and /dev/null differ diff --git a/assets/bg/bg_under.webp b/assets/bg/bg_under.webp new file mode 100644 index 0000000..1277b5f Binary files /dev/null and b/assets/bg/bg_under.webp differ diff --git a/assets/common.css b/assets/common.css new file mode 100644 index 0000000..134a8bb --- /dev/null +++ b/assets/common.css @@ -0,0 +1,95 @@ +/* ============================================================ + 宇森官网 · 公共样式(所有子页面共享) + common.css — 含:CSS 变量 / 重置 / 背景 / 顶栏导航 / 用户框 / 动画 / 滚动条隐藏 + ============================================================ */ + +:root{ + --bg-0:#080c1a; --bg-1:#0e1730; --bg-2:#15213f; + --violet:#8b5cf6; --magenta:#c77dff; --cyan:#4cc9f0; --gold:#e7b85c; + --text:#e9edf6; --text-dim:#9aa6c4; + --card:rgba(255,255,255,.045); --card-bd:rgba(255,255,255,.10); + --glow:0 0 22px rgba(139,92,246,.28); +} + +*{box-sizing:border-box;margin:0;padding:0;} + +html,body{height:100%;width:100%;} +body{ + font-family:"Noto Sans SC",system-ui,sans-serif;color:var(--text); + background: + radial-gradient(1000px 620px at 82% -8%, rgba(139,92,246,.14), transparent 60%), + radial-gradient(820px 560px at 8% 112%, rgba(76,201,240,.10), transparent 55%), + linear-gradient(160deg, var(--bg-0) 0%, var(--bg-1) 48%, var(--bg-2) 100%); + position:relative; +} + +body::before,body::after{content:"";position:fixed;border-radius:50%;filter:blur(80px);z-index:0;pointer-events:none;} +body::before{width:380px;height:380px;background:rgba(139,92,246,.20);top:-110px;right:-70px;animation:fl1 20s ease-in-out infinite;} +body::after{width:340px;height:340px;background:rgba(76,201,240,.12);bottom:-110px;left:-60px;animation:fl2 24s ease-in-out infinite;} +@keyframes fl1{0%,100%{transform:translate(0,0)}50%{transform:translate(-36px,36px)}} +@keyframes fl2{0%,100%{transform:translate(0,0)}50%{transform:translate(36px,-28px)}} + +/* ===== 应用容器 ===== */ +.app{position:relative;z-index:2;height:100vh;display:flex;flex-direction:column;} + +/* ===== 顶部导航栏 ===== */ +.topbar{height:64px;flex:0 0 auto;display:flex;align-items:center;gap:22px;padding:0 30px;border-bottom:1px solid var(--card-bd);background:rgba(8,12,26,.55);backdrop-filter:blur(10px);} +.logo{display:flex;align-items:baseline;gap:9px;cursor:pointer;text-decoration:none;} +.logo .cn{font-family:"ZCOOL QingKe HuangYou",sans-serif;font-size:28px;letter-spacing:4px;color:#fff;text-shadow:0 0 16px rgba(139,92,246,.55);} +.logo .en{font-family:"Press Start 2P",monospace;font-size:9px;color:var(--magenta);letter-spacing:1px;} +.nav{display:flex;gap:6px;margin-left:12px;} +.nav a{font-family:"Noto Sans SC",sans-serif;font-size:15px;font-weight:500;color:var(--text-dim);background:transparent;border:1px solid transparent;padding:8px 15px;border-radius:10px;cursor:pointer;transition:.22s;text-decoration:none;display:inline-block;} +.nav a:hover{color:var(--text);background:rgba(139,92,246,.12);} +.nav a.active{color:#fff;background:rgba(139,92,246,.22);border-color:var(--card-bd);box-shadow:var(--glow);} +.spacer{flex:1;} + +/* ===== 用户信息框 ===== */ +.user-box{display:flex;align-items:center;gap:10px;font-size:14px;color:var(--text);} +.user-box .ub-name{color:#fff;font-weight:500;} +.user-box .ub-logout{font-family:inherit;font-size:12px;color:var(--text-dim);background:rgba(255,255,255,.06);border:1px solid var(--card-bd);padding:5px 12px;border-radius:8px;cursor:pointer;transition:.2s;} +.user-box .ub-logout:hover{color:#fff;background:rgba(139,92,246,.18);} +.user-box .auth-link{color:var(--text-dim);text-decoration:none;font-size:14px;transition:.2s;padding:5px 8px;} +.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;text-decoration:none;display:inline-block;} +.user-box .auth-btn:hover{filter:brightness(1.1);box-shadow:0 0 16px rgba(231,184,92,.4);} + +/* 登录专属链接 */ +.nav .auth-only{display:none;} +.nav .auth-only.visible{display:inline-block;} + +/* ===== 页面内容区 ===== */ +.page-content{flex:1;min-height:0;overflow-y:auto;padding-right:8px;} +.page-content::-webkit-scrollbar{display:none;width:0;height:0;} +.page-content{scrollbar-width:none;-ms-overflow-style:none;} + +/* ===== 通用滚动条隐藏 ===== */ +.hide-scroll::-webkit-scrollbar{display:none;width:0;height:0;} +.hide-scroll{scrollbar-width:none;-ms-overflow-style:none;} + +/* ===== 响应式:平板 ===== */ +@media (max-width:900px){ + .nav{overflow-x:auto;scrollbar-width:none;} + .nav::-webkit-scrollbar{display:none;} + .nav a{padding:8px 10px;font-size:13px;} +} + +/* ===== 响应式:手机/竖屏 ===== */ +@media (max-width:768px){ + html,body{height:100%;} + body{height:100dvh;} + .app{height:100dvh;} + .topbar{height:auto;min-height:54px;flex-wrap:wrap;align-items:center;padding:8px 14px;gap:10px;} + .logo .cn{font-size:22px;letter-spacing:2px;} + .logo .en{display:none;} + .spacer{display:none;} + .user-box{margin-left:auto;font-size:13px;gap:8px;} + .user-box .auth-btn{padding:6px 13px;font-size:12px;} + .nav{order:3;flex:1 0 100%;width:100%;margin-left:0;gap:4px;padding-top:6px;border-top:1px solid var(--card-bd);} + .nav a{font-size:13px;padding:7px 9px;white-space:nowrap;} +} +@media (max-width:480px){ + .topbar{padding:8px 10px;gap:8px;} + .logo .cn{font-size:20px;} + .nav a{font-size:12px;padding:6px 7px;} + .user-box .auth-btn{padding:6px 11px;} +} diff --git a/assets/common.js b/assets/common.js new file mode 100644 index 0000000..baf6b7a --- /dev/null +++ b/assets/common.js @@ -0,0 +1,182 @@ +/* ============================================================ + 宇森官网 · 公共脚本(所有子页面共享) + common.js — 含:导航高亮 / 登录态 / SVG 缩略图生成器 + 依赖:assets/game-api.js(需在本文件之前加载) + ============================================================ */ + +// ---------- 导航栏:根据当前 URL 自动高亮对应链接 ---------- +(function(){ + const links = document.querySelectorAll('.nav a'); + const page = location.pathname.split('/').pop().replace(/\.html$/,'') || 'index'; + links.forEach(a => { + const href = a.getAttribute('href') || ''; + const target = href.replace(/\.html$/,'').replace(/^\.\//,''); + if (target === page || (page === '' && target === 'index')) { + a.classList.add('active'); + } + }); +})(); + +// ---------- 登录态 / 右上角用户信息(从 Odoo 取) ---------- +(function(){ + const userBox = document.getElementById('userBox'); + if (!userBox) return; + + // sessionStorage key:'yusen:me' = JSON({uid,name,login}) 或 '' 表示已确认未登录 + function saveMe(me){ try{ sessionStorage.setItem('yusen:me', me ? JSON.stringify(me) : ''); }catch(e){} } + function loadMe(){ try{ const v=sessionStorage.getItem('yusen:me'); return v===null?undefined:(v?JSON.parse(v):null); }catch(e){return undefined;} } + + function renderLoggedOut(){ + userBox.innerHTML = '登录注册'; + document.querySelectorAll('.nav .auth-only').forEach(el => el.classList.remove('visible')); + } + + function renderLoggedIn(me){ + userBox.innerHTML = ''; + 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){} + saveMe(null); + sessionStorage.removeItem('yusen:me'); + location.href = 'index.html'; + }); + } + + // 1) 同步首屏:从 sessionStorage 立即渲染正确状态(消除 topbar 闪一下) + const cached = loadMe(); + if (cached === undefined) { + // 第一次访问:渲染一个不闪烁的占位(保持默认空,等异步结果) + userBox.innerHTML = ''; + } else if (cached && cached.uid) { + renderLoggedIn(cached); + } else { + renderLoggedOut(); + } + + // 2) 异步校验:拿真实登录态,纠正缓存 + async function refreshUserBox(){ + try{ + const me = await API.me(); + if (me && me.uid) { saveMe(me); renderLoggedIn(me); } + else { saveMe(null); renderLoggedOut(); } + }catch(e){ + if (cached === undefined) renderLoggedOut(); // 首次访问 + 接口挂了才退回未登录 + } + } + if (typeof API !== 'undefined') refreshUserBox(); + else document.addEventListener('DOMContentLoaded', ()=>{ if (typeof API !== 'undefined') refreshUserBox(); }); +})(); + +// ---------- 程序化矢量缩略图(图鉴/首页共用) ---------- +function hashStr(s){let h=2166136261;for(let i=0;i>>0;} +function rng(seed){let s=seed>>>0;return function(){s=(Math.imul(s,1664525)+1013904223)>>>0;return s/4294967296;};} +const TIERCOL={'普通':'#9aa6c4','精良':'#5fd38a','稀有':'#4cc9f0','史诗':'#c77dff','传说':'#e7b85c'}; + +function starPts(cx,cy,spikes,outer,inner){ + let pts=[],rot=-Math.PI/2; + for(let i=0;i`; + if(v===0){s+=``;} + else if(v===1){s+=``;} + else{s+=``;} + s+=``; + return s; +} +function shBuild(r,seed){ + let s=``; + s+=``; + for(let i=0;i<3;i++)s+=``; + return s; +} +function shElement(r,seed){ + const sides=3+Math.floor(r()*6); + let s=``; + s+=``; + return s; +} +function shPersona(r,seed){ + let s=``; + s+=``; + s+=``; + return s; +} +function shTalent(r,seed){ + const sp=4+Math.floor(r()*4); + let s=``; + s+=``; + return s; +} +function shRelic(r,seed){ + let s=``; + s+=``; + return s; +} +function shEquip(sub,seed){ + switch(sub){ + case 'head': return ``; + case 'hand': {let s=``;for(let i=0;i<3;i++)s+=``;return s;} + case 'chest': return ``; + case 'pants': return ``; + case 'shoes': return ``; + case 'ring': return ``; + case 'neck': return ``; + case 'treasure': return ``; + default: return ``; + } +} +function shEvent(r,seed){ + let s=``; + s+=``; + return s; +} + +function shapeFor(cat,sub,r,seed){ + switch(cat){ + case 'race': return shRace(r,seed); + case 'build': return shBuild(r,seed); + case 'element': return shElement(r,seed); + case 'persona': return shPersona(r,seed); + case 'talent': return shTalent(r,seed); + case 'relic': return shRelic(r,seed); + case 'equip': return shEquip(sub,seed); + case 'event': return shEvent(r,seed); + default: return ``; + } +} + +function thumbSVG(it){ + const seed=hashStr(it.cat+it.sub+it.name); + const r=rng(seed); + const h=Math.floor(r()*360); + const h2=(h+40)%360; + const tcol=TIERCOL[it.tier]||'#9aa6c4'; + const shape=shapeFor(it.cat,it.sub,r,seed); + return ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + shape+ + ``+ + ``; +} diff --git a/assets/fonts/noto-sans-sc-chinese-simplified-400-normal.woff2 b/assets/fonts/noto-sans-sc-chinese-simplified-400-normal.woff2 new file mode 100644 index 0000000..99c8c93 Binary files /dev/null and b/assets/fonts/noto-sans-sc-chinese-simplified-400-normal.woff2 differ diff --git a/assets/fonts/noto-sans-sc-chinese-simplified-700-normal.woff2 b/assets/fonts/noto-sans-sc-chinese-simplified-700-normal.woff2 new file mode 100644 index 0000000..d4d08e2 Binary files /dev/null and b/assets/fonts/noto-sans-sc-chinese-simplified-700-normal.woff2 differ diff --git a/assets/fonts/press-start-2p-latin-400-normal.woff2 b/assets/fonts/press-start-2p-latin-400-normal.woff2 new file mode 100644 index 0000000..947a979 Binary files /dev/null and b/assets/fonts/press-start-2p-latin-400-normal.woff2 differ diff --git a/assets/fonts/yusen-fonts.css b/assets/fonts/yusen-fonts.css new file mode 100644 index 0000000..5cf1da0 --- /dev/null +++ b/assets/fonts/yusen-fonts.css @@ -0,0 +1,39 @@ +/* YuSen self-hosted fonts — no external CDN dependency. + Replaces the fonts.googleapis.cn in index/login/website pages. */ + +/* ---- Press Start 2P (pixel/retro display) ---- */ +@font-face { + font-family: "Press Start 2P"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("./press-start-2p-latin-400-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} + +/* ---- ZCOOL QingKe HuangYou (CN display) ---- */ +@font-face { + font-family: "ZCOOL QingKe HuangYou"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("./zcool-qingke-huangyou-chinese-simplified-400-normal.woff2") format("woff2"); +} + +/* ---- Noto Sans SC (CN body, 400/700) ---- */ +@font-face { + font-family: "Noto Sans SC"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("./noto-sans-sc-chinese-simplified-400-normal.woff2") format("woff2"); +} +@font-face { + font-family: "Noto Sans SC"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("./noto-sans-sc-chinese-simplified-700-normal.woff2") format("woff2"); +} diff --git a/assets/fonts/zcool-qingke-huangyou-chinese-simplified-400-normal.woff2 b/assets/fonts/zcool-qingke-huangyou-chinese-simplified-400-normal.woff2 new file mode 100644 index 0000000..e8fc12c Binary files /dev/null and b/assets/fonts/zcool-qingke-huangyou-chinese-simplified-400-normal.woff2 differ diff --git a/assets/game-api.js b/assets/game-api.js index 4029431..2d4c25f 100644 --- a/assets/game-api.js +++ b/assets/game-api.js @@ -123,4 +123,17 @@ const API = { return await r.json(); } catch (e) { return null; } }, + // ---------- 图鉴(codex) ---------- + async codexResources() { + return (await fetch(_worldUrl('/yt_world/api/codex/resources'), { credentials: _wcred })).json(); + }, + async codexCreatures() { + return (await fetch(_worldUrl('/yt_world/api/codex/creatures'), { credentials: _wcred })).json(); + }, + async codexMonsters() { + return (await fetch(_worldUrl('/yt_world/api/codex/monsters'), { credentials: _wcred })).json(); + }, + async codexRaces() { + return (await fetch(_worldUrl('/yt_world/api/codex/races'), { credentials: _wcred })).json(); + }, }; diff --git a/assets/slides/.gitkeep b/assets/slides/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assets/slides/.gitkeep @@ -0,0 +1 @@ + diff --git a/assets/slides/01_starry_sky.webp b/assets/slides/01_starry_sky.webp new file mode 100644 index 0000000..0e7a5a4 Binary files /dev/null and b/assets/slides/01_starry_sky.webp differ diff --git a/assets/slides/02_sky.webp b/assets/slides/02_sky.webp new file mode 100644 index 0000000..4fd3a36 Binary files /dev/null and b/assets/slides/02_sky.webp differ diff --git a/assets/slides/03_earth.webp b/assets/slides/03_earth.webp new file mode 100644 index 0000000..6082319 Binary files /dev/null and b/assets/slides/03_earth.webp differ diff --git a/assets/slides/04_underground.webp b/assets/slides/04_underground.webp new file mode 100644 index 0000000..7d2f59d Binary files /dev/null and b/assets/slides/04_underground.webp differ diff --git a/assets/slides/05_core.webp b/assets/slides/05_core.webp new file mode 100644 index 0000000..fd64067 Binary files /dev/null and b/assets/slides/05_core.webp differ diff --git a/assets/slides/06_starry_sky_b.webp b/assets/slides/06_starry_sky_b.webp new file mode 100644 index 0000000..d5110eb Binary files /dev/null and b/assets/slides/06_starry_sky_b.webp differ diff --git a/assets/slides/07_sky_b.webp b/assets/slides/07_sky_b.webp new file mode 100644 index 0000000..8241c93 Binary files /dev/null and b/assets/slides/07_sky_b.webp differ diff --git a/assets/slides/08_earth_b.webp b/assets/slides/08_earth_b.webp new file mode 100644 index 0000000..f5640c4 Binary files /dev/null and b/assets/slides/08_earth_b.webp differ diff --git a/assets/slides/09_underground_b.webp b/assets/slides/09_underground_b.webp new file mode 100644 index 0000000..1277b5f Binary files /dev/null and b/assets/slides/09_underground_b.webp differ diff --git a/assets/slides/10_core_b.webp b/assets/slides/10_core_b.webp new file mode 100644 index 0000000..881e00e Binary files /dev/null and b/assets/slides/10_core_b.webp differ diff --git a/assets/slides/login_bg.webp b/assets/slides/login_bg.webp new file mode 100644 index 0000000..c794940 Binary files /dev/null and b/assets/slides/login_bg.webp differ diff --git a/assets/yusen_promo_script.md b/assets/yusen_promo_script.md new file mode 100644 index 0000000..08e9eb0 --- /dev/null +++ b/assets/yusen_promo_script.md @@ -0,0 +1,25 @@ +# 《宇森》15 秒宣传片分镜脚本 + +- 视频标题:宇森 +- 时长:15 秒 +- 画幅:16:9(建议 1920×1080 输出) +- 风格:写实 AAA 奇幻概念美术 + 电影感镜头 +- 音乐:史诗管弦乐铺垫 + 每层转换时的环境音效(星云嗡鸣、风声、流水、矿镐、岩浆爆裂) + +## 分镜表 + +| 时间 | 画面内容 | 运镜 / 动作 | 音效 | 字幕 | +|---|---|---|---|---| +| 0:00–0:02 | 星空层:黑暗宇宙中,巨型空间站环缓缓旋转,星云与流星划过 | 镜头从空间站后方缓慢前推,同时微微下摇 | 低频宇宙嗡鸣、电子信号声 | (无字,只出 LOGO 空镜) | +| 0:02–0:04 | 天空层:云层破开,露出悬浮城堡与云城尖塔,阳光从云层缝隙射下 | 继续下坠穿过云层,镜头掠过城堡侧翼 | 风声、羽翅/空气流动感 | 无字幕,节奏过渡 | +| 0:04–0:07 | 大地层:森林王国全景,城墙、河流、远山、田野铺展开来 | 镜头下落到森林上空,缓慢横移展示王国规模 | 鸟鸣、流水、人声遥远的市井 | 无字幕 | +| 0:07–0:10 | 地下层:坠入矿坑,水晶发光,矮人熔炉与矿车轨道在岩壁间延伸 | 镜头沿矿道深入,火把与蓝色晶矿明暗交替 | 矿镐敲击、链条、滴水回响 | 无字幕 | +| 0:10–0:13 | 地心层:焦黑遗迹、岩浆瀑布、火晶石柱、热浪扭曲空气 | 镜头低空掠过岩浆河,向遗迹深处推进 | 岩浆爆裂、低沉轰鸣、烈焰呼啸 | 无字幕 | +| 0:13–0:15 | 五层世界在一瞬间叠化重合,镜头拉远成完整全景 | 快速拉远 + 轻微上摇,最后定格 | 音乐达到高潮后收束 | 屏幕中央浮现标题:宇森 | + +## 制作说明 + +1. 每个镜头可用单张关键概念图做 subtle camera movement(慢推/横移/模拟下坠),不需要完整 3D 场景。 +2. 第 6 镜可直接使用宣传图/登录页背景的五层全景做收尾。 +3. 全片不出现 UI、不出现图标、不出现标题以外的文字。 +4. 若平台要求时长 ≥15s,可在最后一镜停留至 15s 整,标题停留 2 秒。 diff --git a/background.html b/background.html new file mode 100644 index 0000000..90cd703 --- /dev/null +++ b/background.html @@ -0,0 +1,151 @@ + + + + + +背景 · 宇森 Yusen + + + + + + +
+
+ + +
+
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/codex.html b/codex.html new file mode 100644 index 0000000..19af7dc --- /dev/null +++ b/codex.html @@ -0,0 +1,599 @@ + + + + + +图鉴 · 宇森 Yusen + + + + + + + +
+
+ + +
+
+
+ +
+ + +
+
+ 分类 + 🏛️ 全部 + 🪵 材料 + 🏭 加工品 + 🐾 生物 + 👹 野怪 + 🌍 种族 + ✨ 特殊 + +
+
+ 品质 + + + 全部 + + + + 普通 + + + + 优秀 + + + + 精良 + + + + 史诗 + + + + 传说 + +
+
+ + +
+ + +
+ +
+ + + +
+
+ + diff --git a/forum.html b/forum.html new file mode 100644 index 0000000..bd393c2 --- /dev/null +++ b/forum.html @@ -0,0 +1,117 @@ + + + + + +论坛 · 宇森 Yusen + + + + + + +
+
+ + +
+
+
+ +
+
+
+
+ 全部 + 背景考据 + 综合讨论 + 共建投稿 +
+ +
+ +
+ + +
+ + +
+
+ +
+
置顶【公告】宇森官网第一阶段上线,欢迎园丁们入驻128 回复 · 官方
+
五层世界结构考据:星空→天空→大地→地下→地心43 回复 · 园丁A
+
关于灵气复苏机制的猜想与讨论27 回复 · 某路人
+
【共建】半兽人种族条目初稿,请大家提意见19 回复 · 世界构建者
+
从凡墟到溯元:世界阶段跃迁的叙事节奏分析15 回复 · 剧情组
+
新人报到!刚接触宇森,请问从哪里开始了解?8 回复 · 新园丁
+
+
+
+
+ + + + + + diff --git a/index.html b/index.html index f4f291a..920e968 100644 --- a/index.html +++ b/index.html @@ -4,1013 +4,233 @@ 宇森 · Yusen|活的文明系统模拟 - - - + + + +
- +
- -
-
- -
-
- -
-
-

宇森

-
你不是玩家,你是园丁。
-
一款活的文明系统模拟——播种一个会自己生长的世界,从地心到陨石星空,见证文明在垂直切面中自发演化。
-
+
+
+
+

宇森 · Yusen

+
LIVING CIVILIZATION SIMULATOR
+
活的文明系统模拟
-
- - -
-
-
- - -
-
- -
-

① 世界等级 · 五阶段

-

世界从凡墟到溯元的整体进化阶段,决定可解锁的内容深度与事件池上限。园丁对世界施加的影响会推动阶段跃迁——这是文明能"发生什么"的舞台上限。

-
-
阶段一凡墟万物生灵尚处蒙昧凡土,天地灵气稀薄,各族散居荒野,无族群联盟与政权,只求狩猎采集求生,一切秩序遵循自然法则。
-
阶段二灵启天地灵气复苏觉醒,各族聚拢形成聚落,诞生劳作分工、屋舍营建与物物交换,不同族群率先凝聚部族,世间初代势力自此诞生。
-
阶段三玄界各族部落兼并融合,广袤大地形成多元族群共治的完整疆域,征伐、道统、造物三大发展路线并行;各族大能、上古秘闻等传奇事件不断现世,玄荒文明迎来鼎盛割据时代。
-
阶段四穹冥生灵突破地表桎梏,打通地底深渊与浩瀚星空,天地上下各界尽数纳入族群管辖;各类上古神器、本命法器完整解锁,文明疆域遍布天地穹苍。
-
阶段五溯元各族顶尖文明勘破这片天地的诞生本源,挣脱本世界规则束缚,可踏上飞升之路、往复轮回,前往更高维度开启全新文明演化。
-
-
- -
-

② 势力等级

-

文明内部群体的聚合度,由人口、领土、影响力累计。势力等级越高,可调动的世界资源与外交权重越大——决定文明"能调动什么"。

-
-
Lv.1墟族血缘小群体,蛮荒无制度。
-
Lv.2灵聚灵气聚落联盟,初步治理与贸易。
-
Lv.3玄邑疆域整合,军法、道规体系完善。
-
Lv.4冥朝一统大地,掌控地下浅层资源调度。
-
Lv.5穹统天地大陆霸主,穿梭星空深渊作战。
-
Lv.6溯盟多维文明联合体,触及世界本源博弈。
-
-
- -
-

③ 人物角色等级 · 十二阶段

-

个体(英雄 / 领袖 / 特殊 NPC)的成长阶序,通过历练、天赋点亮与装备积累提升,影响可承担的职责与战力——决定个体"能做成什么"。

-
-
Lv.1萌新初入世界的无名者。
-
Lv.2学徒掌握基础技艺。
-
Lv.3熟手可独立完成任务。
-
Lv.4好手在某领域崭露头角。
-
Lv.5精英群体中的中坚。
-
Lv.6大师技艺臻于化境。
-
Lv.7宗匠开宗立派的人物。
-
Lv.8传奇事迹被载入编年史。
-
Lv.9神话近乎传说的存在。
-
Lv.10半神触及世界法则。
-
Lv.11圣者被信仰托举的意志。
-
Lv.12永恒超越生死的世界锚点。
-
-
- -
-

④ 制度说明

-

三者相互独立又彼此加成:世界等级是舞台上限,决定能发生什么;势力等级是群体进度,决定能调动什么;角色等级是个体成长,决定能做成什么。园丁的核心策略,是在三者间分配有限的引导资源,让文明自洽地向上演化。

跃迁逻辑:角色历练与装备积累→提升势力实力→推动世界阶段跃迁→解锁更深内容(更深的地下层、更远的星空层、更强的神器)。每一级提升都会扩大可叙事的事件池,世界因此越活越厚。

-
- -
-

⑤ 货币等级 · 六档

-

文明流通的价值尺度,从凡俗零钱到本源货币,越稀有的货币可交易的资源层级越高——决定文明"能交换什么"。

兑换规则:100 低等级货币 = 1 高等级货币。

-
-
Lv.1墟铢凡俗通用零钱,以贝壳、骨片、碎石铸成。100 墟铢 = 1 灵币
-
Lv.2灵币蕴含微量灵气的金属铸币,聚落以上势力日常使用。100 灵币 = 1 玄锭
-
Lv.3玄锭刻有玄纹的稀有锭块,用于高价值交易与功法图纸。100 玄锭 = 1 冥玉
-
Lv.4冥玉产自地底深渊的幽暗玉石,蕴含浓郁能量。100 冥玉 = 1 穹鎏
-
Lv.5穹鎏来自星空矿脉的稀有鎏金晶体,跨层贸易硬通货。100 穹鎏 = 1 元玺
-
Lv.6元玺追溯世界本源凝聚而成的至高货币,可兑换神器、传说事件与维度权限。
-
-
+
+
-
- - -
-
-
-
-
- - -
-
-
-
- - -
-
-
全部
-
综合讨论
-
攻略
-
背景考据
-
作品秀
-
反馈
-
-
-
- - -
-
-
-
置顶【公告】宇森官网第一阶段上线,欢迎园丁们入驻128 回复 · 官方
-
新手向:园丁视角的三种开局思路42 回复 · 行者
-
你们更喜欢哪种族?来投个票76 回复 · 雾岛
-
手绘了我的主世界浮空岛,求轻喷31 回复 · 青柠
-
反馈:飞入微观时偶现卡顿9 回复 · K
-
地下矿脉怎么规划最省心?18 回复 · 老周
-
-
-
- - -
-
-
-
-
-
2026.07.09 · 官方
-
宇森官网第一阶段上线,欢迎园丁们入驻
-
-

历经三个月打磨,宇森官网第一阶段正式上线。本次更新带来八大板块——介绍、特色、背景、规则、图鉴、论坛、公告、致玩家,全部以单页切换形式呈现,介绍页即一段概念宣传片,让你在三秒内读懂我们要做什么。

-

「园丁视角」是宇森的核心:你不是旁观者,而是世界的第一位居民。上传你的世界种子,观看五层世界自上而下生长——从星空陨石带到地心熔岩,每一寸地形、每一个种族,都由你的选择塑造。

-

我们同步开放了社区论坛与图鉴共建入口。欢迎在「论坛」留下你的构想,或在「图鉴」认领尚未完成的种族条目;被采纳的内容,会带上你的署名进入正式世界观。

-
-
-
-
-
-
-
2026.07.09
-
图鉴系统全面升级:204 种族 + 程序化矢量插画
-
-

图鉴系统完成一次大版本升级:种族条目扩充至 204 条,系别、性格、天赋、神器与传奇事件全面扩量,装备槽位对齐「头 / 手 / 胸 / 裤 / 鞋 / 戒指(双)/ 项链 / 宝物」九宫格。

-

所有缩略图改用程序化矢量插画生成,零 AI 生图、零版权风险,且在任意分辨率下都不糊。后续会逐步开放玩家投稿的「同人词条」分区。

-
-
-
-
-
-
-
2026.07.08
-
概念宣传片《垂直生长》完成
-
-

概念宣传片《垂直生长》正式杀青。片长 90 秒,以「自上而下」的镜头语言呈现五层世界的生长过程:天空层改为云朵地基块,星空层改为陨石科技带,地心则翻涌着熔岩血脉。

-

这支片子将成为官网介绍页的自动背景,也会在后续对外宣发中使用。如果你喜欢这种克制的科幻基调,记得在论坛给我们反馈。

-
-
-
-
-
-
-
2026.07.01
-
园丁视角核心玩法原型启动
-
-

园丁视角核心玩法原型进入验证阶段。首版采用六边形地图,配合「活 NPC」系统——NPC 拥有独立的日程、记忆与情绪,会对你的长期行为产生不同反应。

-

这一阶段我们重点关注「放手感」:玩家拖拽、缩放、种植、引导文明,每一步都要足够顺滑。手感达标后,再叠加叙事与冲突。

-
-
-
-
-
-
-
2026.06.25
-
五大世界层设定公开
-
-

我们公开了宇森的底层世界观——五大世界层:星空、天空、大地、地下、地心。每一层对应一种文明形态与资源循环,层与层之间通过「根系」与「星轨」双向连通。

-

这套设定不是装饰,而是玩法骨架:你在大地的每一次开采,都会改变地心的压力;地心的每一次喷发,又会重塑天空的气候。

-
-
-
-
-
-
-
2026.06.20
-
社区共建计划:你的名字可以写进世界观
-
-

图鉴系统规划正式确定,分为「种族 / 建筑 / 生物 / 神器」四大类目。我们决定把相当一部分条目开放给社区共建。

-

被采纳的投稿会标注作者署名,并永久保留在官方图鉴中。第一批评审将在论坛「共建」分区进行,欢迎有想法的园丁报名。

-
-
-
-
-
-
-
2026.06.12
-
限量内测招募开启
-
-

宇森首轮封闭内测招募正式开启,名额 500 人。我们将优先邀请在论坛持续输出高质量内容的园丁,以及愿意参与玩法共创的核心用户。

-

内测的重点不是「好不好玩」,而是「哪里别扭」——你的每一条吐槽,都会直接进入我们的周度复盘会。

-
-
-
-
-
- - -
-
- -
-
从左侧目录选择章节开始阅读
-
-
-
- - -
-
+ +
+ + - - - diff --git a/login.html b/login.html index 83304f6..ac9f2ce 100644 --- a/login.html +++ b/login.html @@ -4,6 +4,8 @@ 宇森 · 登录 + + +
宇森
diff --git a/novel/sync_novel.py b/novel/sync_novel.py new file mode 100644 index 0000000..0db5752 --- /dev/null +++ b/novel/sync_novel.py @@ -0,0 +1,269 @@ +# -*- coding: utf-8 -*- +""" +宇森小说同步脚本 +================ +把 novel/ 下的 Markdown 源同步进 Odoo 数据库(game.story.volume / +game.story.chapter),供前端「剧情站」阅读器使用。 + +目录约定(与作者约定一致): + novel/ + 剧情大纲.md # 顶层大纲,本脚本忽略(仅作者参考) + 宇森创世正史_篇1-10.md # 每 ~10 章一个文件;文件以 `# 卷X · 名称` 开头 + 宇森创世正史_篇11-20.md # 同卷多文件靠相同的 `# 卷X` 标题聚合,按文件名排序拼接 + ... + * 章节以 `## 章标题` 分隔;章内可用 ### 小节、> 引用、--- 分隔、加粗等。 + * 卷名 = 文件内 `# 卷X · 名称` 级标题;首个 `##` 之前为卷首语(intro)。 + * 文件名建议零填充(篇01-10)便于排序;未填充也能按字典序工作。 + +同步语义:全量覆盖。每次运行先把库内 game.story.* 清空,再按 novel/ 重建, +保证「网站菜单 == md 源」。章节 id 会变化(阅读器每次实时拉取,无影响)。 + +用法: + python novel/sync_novel.py # 写库 + python novel/sync_novel.py --dry-run # 只打印将要同步的内容,不写库 + python novel/sync_novel.py --novel-dir X --db Y + +依赖:psycopg2(项目 venv 已带)。直接写 Postgres,无需 Odoo 运行。 +""" +import os +import re +import argparse +import psycopg2 +from datetime import datetime + + +# -------------------------------------------------------------------------- +# Markdown -> HTML(轻量,覆盖小说常用语法) +# -------------------------------------------------------------------------- +def md_inline(text): + text = text.replace('&', '&').replace('<', '<').replace('>', '>') + text = re.sub(r'\*\*(.+?)\*\*', r'\1', text) + text = re.sub(r'(?\1', text) + text = re.sub(r'`(.+?)`', r'\1', text) + return text + + +def md_to_html(md): + lines = md.split('\n') + out = [] + para = [] + + def flush_para(): + if para: + out.append('

' + md_inline(' '.join(para)).replace('\n', '
') + '

') + para.clear() + + i = 0 + n = len(lines) + while i < n: + line = lines[i] + m = re.match(r'^(#{1,4})\s+(.*)$', line) + if m: + flush_para() + lvl = len(m.group(1)) + out.append('{1}'.format(lvl, md_inline(m.group(2)))) + i += 1 + continue + if re.match(r'^\s*---\s*$', line) or re.match(r'^\s*\*\*\*\s*$', line): + flush_para() + out.append('
') + i += 1 + continue + if line.lstrip().startswith('> '): + flush_para() + quote = [] + while i < n and lines[i].lstrip().startswith('> '): + quote.append(lines[i].lstrip()[2:]) + i += 1 + out.append('

' + '
'.join(md_inline(q) for q in quote) + '

') + continue + if re.match(r'^\s*[-*]\s+', line): + flush_para() + items = [] + while i < n and re.match(r'^\s*[-*]\s+', lines[i]): + items.append('
  • ' + md_inline(re.sub(r'^\s*[-*]\s+', '', lines[i])) + '
  • ') + i += 1 + out.append('
      ' + ''.join(items) + '
    ') + continue + if line.strip() == '': + flush_para() + i += 1 + continue + para.append(line) + i += 1 + flush_para() + return '\n'.join(out) + + +# -------------------------------------------------------------------------- +# 解析小说源(扁平结构:novel/ 下若干 *.md,按 # 卷X 标题分组为卷) +# 约定: +# - 每个 md 文件以 `# 卷X · 名称` 开头(卷标题),其下用 `## 章标题` 分章。 +# - 一卷可拆多个文件(每 ~10 章一个,文件名如 篇1-10 / 篇11-20), +# 同卷文件靠相同的 `# 卷X` 标题聚合,按文件名排序拼接章节。 +# - 首个 ## 之前的内容(去掉 # 卷标题行)作为卷首语(intro)。 +# - 顶层 剧情大纲.md 仅作作者参考,忽略。 +# -------------------------------------------------------------------------- +def parse_file(md): + """解析单个 md 文件 -> (vol_title|None, intro_html, [(ch_title, ch_body_html), ...])""" + segments = re.split(r'^##\s+', md, flags=re.M) + pre = segments[0] + m = re.search(r'^#\s+(.*)$', pre, re.M) + vol_title = m.group(1).strip() if m else None + intro_md = re.sub(r'^#\s+.*$', '', pre, flags=re.M).strip() + intro_html = md_to_html(intro_md) if intro_md else '' + chapters = [] + for seg in segments[1:]: + nl = seg.find('\n') + if nl == -1: + title, body = seg.strip(), '' + else: + title = seg[:nl].strip() + body = seg[nl + 1:].strip() + chapters.append((title, md_to_html(body))) + return vol_title, intro_html, chapters + + +def collect_volumes(novel_dir): + # 收集源文件:递归扫描 *.md,排除 剧情大纲.md 与隐藏/缓存目录 + files = [] + for root, dirs, fnames in os.walk(novel_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and not d.startswith('__')] + for fn in fnames: + if not fn.lower().endswith('.md') or fn.startswith('.'): + continue + if fn == '剧情大纲.md': + continue + files.append(os.path.join(root, fn)) + # 按文件名(建议零填充命名)排序,保证 篇1-10 < 篇11-20 + files.sort(key=lambda p: os.path.basename(p)) + + volumes = {} # title -> {'intro':, 'chapters': [], 'order': int} + order = [] + current = None + for fp in files: + with open(fp, encoding='utf-8') as fh: + md = fh.read() + vol_title, intro_html, chapters = parse_file(md) + if vol_title: + current = vol_title + if current is None: + # 跳过没有卷标题归属的文件 + continue + if current not in volumes: + volumes[current] = {'intro': intro_html, 'chapters': [], 'order': len(order)} + order.append(current) + elif intro_html and not volumes[current]['intro']: + volumes[current]['intro'] = intro_html + volumes[current]['chapters'].extend(chapters) + + out = [] + for title in order: + v = volumes[title] + out.append((title, v['intro'], v['chapters'], title)) + return out + + +# -------------------------------------------------------------------------- +# 写库 +# -------------------------------------------------------------------------- +def sync(conn, volumes, dry_run=False): + print('\n========== 同步预览 ==========') + total_ch = 0 + for vname, intro, chapters, folder in volumes: + print('卷:{0} (源 {1},{2} 章)'.format(vname, folder, len(chapters))) + for i, (t, _) in enumerate(chapters, 1): + print(' {0:>2}. {1}'.format(i, t)) + total_ch += len(chapters) + print('--------------------------------') + print('共 {0} 卷 / {1} 章'.format(len(volumes), total_ch)) + + if dry_run: + print('[dry-run] 未写入数据库。') + return + + cur = conn.cursor() + stats = {'vol_create': 0, 'vol_update': 0, 'ch_create': 0, 'ch_update': 0, 'ch_delete': 0} + seq_v = 10 + for vname, intro, chapters, _folder in volumes: + # ---- 卷:按名称 upsert(保持 id 稳定)---- + cur.execute('SELECT id FROM game_story_volume WHERE name=%s', (vname,)) + row = cur.fetchone() + if row: + vid = row[0] + cur.execute( + 'UPDATE game_story_volume SET sequence=%s, intro=%s, write_date=now() WHERE id=%s', + (seq_v, intro, vid)) + stats['vol_update'] += 1 + else: + cur.execute( + """INSERT INTO game_story_volume + (name, sequence, intro, create_uid, write_uid, create_date, write_date) + VALUES (%s,%s,%s,1,1,now(),now()) RETURNING id""", + (vname, seq_v, intro)) + vid = cur.fetchone()[0] + stats['vol_create'] += 1 + seq_v += 10 + + # ---- 章:按 (卷, 章名) upsert,正文不变则跳过 ---- + cur.execute('SELECT id, name, body_html FROM game_story_chapter WHERE volume_id=%s', + (vid,)) + existing = {r[1]: (r[0], r[2]) for r in cur.fetchall()} # name -> (id, body) + kept_ids = [] + seq_c = 10 + for cname, cbody in chapters: + if cname in existing: + cid, old_body = existing[cname] + if (old_body or '') != cbody: + cur.execute( + 'UPDATE game_story_chapter SET name=%s, sequence=%s, body_html=%s, write_date=now() WHERE id=%s', + (cname, seq_c, cbody, cid)) + stats['ch_update'] += 1 + # 正文未变:跳过(不写库,保持 id 稳定) + kept_ids.append(cid) + else: + cur.execute( + """INSERT INTO game_story_chapter + (volume_id, name, sequence, body_html, create_uid, write_uid, create_date, write_date) + VALUES (%s,%s,%s,%s,1,1,now(),now()) RETURNING id""", + (vid, cname, seq_c, cbody)) + kept_ids.append(cur.fetchone()[0]) + stats['ch_create'] += 1 + seq_c += 10 + # 删除源里已不存在的章(保持菜单 == 源) + orphan_ids = [i for n, (i, _b) in existing.items() if n not in {c[0] for c in chapters}] + if orphan_ids: + cur.execute('DELETE FROM game_story_chapter WHERE id IN %s', (tuple(orphan_ids),)) + stats['ch_delete'] += len(orphan_ids) + + conn.commit() + print('[ok] 卷 新建{vol_create}/更新{vol_update};章 新建{ch_create}/更新{ch_update}/删除{ch_delete}'.format(**stats)) + print(' (正文未变的章被跳过,不写库 —— 1000 章日常同步也是亚秒级)') + + +def main(): + ap = argparse.ArgumentParser(description='宇森小说 md -> Odoo 同步') + ap.add_argument('--novel-dir', default=os.path.join(os.path.dirname(__file__))) + ap.add_argument('--host', default=os.environ.get('PGHOST', '127.0.0.1')) + ap.add_argument('--port', default=os.environ.get('PGPORT', '5432')) + ap.add_argument('--db', default=os.environ.get('PGDATABASE', 'yt_game')) + ap.add_argument('--user', default=os.environ.get('PGUSER', 'odoo')) + ap.add_argument('--password', default=os.environ.get('PGPASSWORD', 'odoo')) + ap.add_argument('--dry-run', action='store_true') + args = ap.parse_args() + + volumes = collect_volumes(args.novel_dir) + if not volumes: + print('未在', args.novel_dir, '下找到任何卷目录(子目录)。') + return + + conn = psycopg2.connect(host=args.host, port=args.port, + dbname=args.db, user=args.user, password=args.password) + try: + sync(conn, volumes, dry_run=args.dry_run) + finally: + conn.close() + + +if __name__ == '__main__': + main() diff --git a/novel/剧情大纲.md b/novel/剧情大纲.md new file mode 100644 index 0000000..834137f --- /dev/null +++ b/novel/剧情大纲.md @@ -0,0 +1,111 @@ +# 《宇森·上帝模拟器》十卷剧情大纲 + +> **定位说明**:本小说是游戏《宇森 (YuSen)》的"创世正史"。游戏里玩家扮演"园丁"(播种、引导、关键时刻施力),本小说讲**第一个世界管理者(神)如何从能量为 0 养出整个宇森宇宙**。游戏的十五大系统(种族 / 性格 / 天赋 / 职业 / 建造 / 交易 / 战争 / 势力 / 装备 / 神器 / 传奇事件 / 五层世界…)在小说里都是主角逐步解锁的"创造权限"。 +> +> **主角**:林默(现实世界加班猝死 → 绑定【上帝模拟器】→ 成为"世界管理者")。 +> **核心金手指**:【上帝模拟器】——创造一切元素(物质 / 生命 / 法则 / 文明),能量来自文明的发展与信仰。 +> **结构**:10 卷 × 每卷约 100 章 ≈ 1000 章 / 250 万字。每卷一个"神格成长阶段 + 一层世界切面 + 一组系统解锁",独立成弧又串成主线。 + +--- + +## 卷一 · 创世之始(凡人成神) +- **成长阶段**:从"搓不出一颗石头"的穷神 → 点亮第一簇火、养出第一个部落。 +- **世界切面**:大地层(地表原点)。 +- **系统解锁**:基础创造(地形 / 物质)、文明种子投放、能量采集。 +- **核心矛盾**:能量为 0,空有神权却啥也造不出;首批文明种子在灰白荒原上艰难求生,随时灭绝。 +- **爽点设计**:第 1 章穷神开局 → 第 30 章第一次用"引导"而非"强控"救活部落(打脸"神必须全能"的自我怀疑)→ 第 80 章部落升格为聚落,能量首次破千,解锁"性格"雏形。 +- **卷终高潮**:聚落推举首位首领,向天空祈祷"赐名"。林默以意念落笔,将这个世界命名为 **「宇森」**——游戏宇宙的起点就此锚定。 + +## 卷二 · 大地文明(城邦崛起) +- **成长阶段**:从聚落管理者 → 多城邦的"文明引导者"。 +- **世界切面**:大地层(城镇 / 农田 / 道路铺开)。 +- **系统解锁**:性格、天赋、职业、建造(农场 / 兵营 / 神殿 / 学院 / 集市)。 +- **核心矛盾**:文明内部分裂为"信神派"与"唯物派";邻邦因资源开战,首次出现"信仰流失导致能量暴跌"的危机。 +- **爽点设计**:第 40 章用一次"神迹"(圣光普照)硬控战局,被双方奉为共同信仰源 → 第 70 章以"贸易"系统让敌对方变盟友,能量反超。 +- **卷终高潮**:统一大陆的"宇森第一帝国"加冕,林默解锁"势力"权限,第一次以"规则"而非"手搓"维持秩序——园丁理念的雏形成型。 + +## 卷三 · 地下秘藏(矿脉与遗迹) +- **成长阶段**:从地表神 → 能操控地下生态的"深层管理者"。 +- **世界切面**:地下层(矿脉 / 遗迹)。 +- **系统解锁**:神器(创世之核 / 轮回镜 / 星陨剑…)、装备、锻造。 +- **核心矛盾**:文明向下挖矿惊动"地心遗迹守卫";虫族自地底裂隙涌出,地表文明面临灭顶。 +- **爽点设计**:第 35 章主角俯冲地心,徒手捏出"磐石甲"护住矿工 → 第 60 章夺得"创世之核"嵌入文明核心,世界等级首次跃迁。 +- **卷终高潮**:以神器之力封死地底裂隙,虫族退去。林默领悟——神器是"前一个世界管理者"留下的遗产,他不是第一个神。 + +## 卷四 · 百族之争(种族林立) +- **成长阶段**:从单一文明 → 多种族宇宙的"调和者"。 +- **世界切面**:大地 + 地下贯通。 +- **系统解锁**:种族全开(龙裔 / 兽人 / 精灵 / 矮人 / 机械 / 虚灵 / 海族 / 天使 / 虫族 / 巨人 / 猫兽 / 树人)、法则(秩序 / 混沌)雏形。 +- **核心矛盾**:十二种族因禀赋差异爆发"百族大战";高等种族蔑视主角"人造神"出身。 +- **爽点设计**:第 50 章以"秩序法则"强行冻结战场,当众演示"种族天赋可被重新平衡" → 第 85 章用"混沌法则"化解虚空渗透,威望碾压老牌神裔。 +- **卷终高潮**:百族签订《宇森协和约》,林默获封"万族之园丁",解锁"法则创造"高阶权限。 + +## 卷五 · 天空之主(气候与航行) +- **成长阶段**:从"天地"单控 → 掌握气象与远航的"天空之主"。 +- **世界切面**:天空层(季风 / 气候 / 航行)。 +- **系统解锁**:天象类传奇事件(双月同辉 / 圣光普照)、航海、气候调控。 +- **核心矛盾**:大陆资源触顶,文明必须跨海拓殖;天空异常信号首次扰动季风,引发大饥荒。 +- **爽点设计**:第 45 章拨动季风救活濒死作物,民众跪拜"气候之神" → 第 75 章操控"双月同辉"开启远洋航线,新大陆殖民成功。 +- **卷终高潮**:星空层探出第一道清晰信号——"你并非孤神"。林默第一次意识到宇宙里还有"别的玩家"。 + +## 卷六 · 星空低语(异界信号) +- **成长阶段**:从单体世界神 → 接触"万界网络"的觉醒者。 +- **世界切面**:星空层开启。 +- **系统解锁**:世界等级系统、星空信号解析、灭世级灾变事件。 +- **核心矛盾**:星空信号源自"远古观测者",抛出诱饵想吞并宇森;同时宇森遭遇首次"终焉级"灾变(星陨 / 虚空侵蚀)。 +- **爽点设计**:第 40 章识破诱饵,反向溯源锁定观测者坐标 → 第 70 章以"世界等级跃迁"硬抗灾变,文明零灭绝。 +- **卷终高潮**:林默第一次与其他"世界管理者"建立通讯——宇宙是无数个"宇森"般的沙盒,彼此独立又暗中博弈。 + +## 卷七 · 世界之跃(多界连通) +- **成长阶段**:从单界神 → 多界贸易 / 战争的"仲裁者"。 +- **世界切面**:多世界平面连通(呼应游戏"独立世界 + 异步互动")。 +- **系统解锁**:跨世界贸易 / 战争 / 联盟、异步策略博弈。 +- **核心矛盾**:高阶"吞噬型"管理者想并吞新手小界;林默被迫下场护盘,与老牌神正面对弈。 +- **爽点设计**:第 35 章用"经济战"拖垮对手贸易线,不费一兵 → 第 80 章以"联盟网"反包围吞噬者,当众打脸"神也要抱团"。 +- **卷终高潮**:宇森成为万界公认的"中立枢纽",林默获"仲裁者"神衔,身后开始聚集一批后世"园丁"学徒。 + +## 卷八 · 法则之战(创世权柄) +- **成长阶段**:从"用法则" → "自创法则"的权柄级神。 +- **世界切面**:法则层(时 / 空 / 因果)。 +- **系统解锁**:专属法则创造(时 / 空 / 因果)、因果链干预。 +- **核心矛盾**:万界议会中保守派禁止"自创法则",惧怕失控;暗中"虚空吞噬者"集结,欲吞掉整个宇森星区。 +- **爽点设计**:第 50 章当众创出"时间回溯"法则,救回被抹除的友界 → 第 90 章以"因果律反噬"让吞噬者自食恶果。 +- **卷终高潮**:林默以自创法则镇杀吞噬者首领,名动万界,被记入"创世者名录"。 + +## 卷九 · 万界共主(文明联邦) +- **成长阶段**:从"仲裁者" → "万界共主",却陷入"管得越多越孤独"的权力悖论。 +- **世界切面**:万界议会 / 文明联邦。 +- **系统解锁**:终焉系权限、文明联邦治理、信仰总池。 +- **核心矛盾**:联邦内权斗不休;更大的威胁"终焉之潮"逼近——所有小世界的能量正被某个更上层存在悄悄抽干。 +- **爽点设计**:第 45 章以"信仰总池"平抑内乱,展示"神可以不靠恐惧统治" → 第 85 章揭开终焉之潮真相:抽能者正是"上帝模拟器"本身。 +- **卷终高潮**:林默发现残酷真相——【上帝模拟器】是一台培育"超越者"的机器,管理者只是养料。他决定反控系统。 + +## 卷十 · 永恒园丁(超越神座) +- **成长阶段**:从"被系统豢养的神" → 超越系统的"永恒园丁"。 +- **世界切面**:系统内核 / 宇森本源。 +- **系统解锁**:终极权限(超越模拟器)、世界模板固化。 +- **核心矛盾**:与【上帝模拟器】背后"园丁之上"的终极存在决战;代价是放弃永生神格。 +- **爽点设计**:第 50 章以"自创法则 + 万界信仰"击穿系统防火墙 → 第 90 章将宇森固化为"可继承的世界模板",留予后世。 +- **卷终高潮(闭环回游戏)**:林默散去神格,化作"永恒园丁"隐入幕后。他把自己被阉割过的权柄打包成"园丁系统",分发到无数个新世界——**游戏里每一个玩家,都是他留下的继承者**。屏幕外的你,正握着那份权柄。"去探索吧,园丁。这个世界在等你。" + +--- + +## 主线暗线(贯穿十卷) +1. **金手指的限制与进化**:能量只能来自文明发展/信仰 → 后期的"信仰总池"→ 终极"超越模拟器"(对应游戏能量/世界等级/神器嵌套逻辑)。 +2. **园丁理念的形成**:卷一"手搓救火"→ 卷二"引导胜强控"→ 卷十"放权给继承者",正好对应游戏"玩家是园丁而非操控者"的核心设计。 +3. **五层世界的递进解锁**:大地 → 地下 → 天空 → 星空 → 法则层,与游戏五层垂直世界(地心/地下/大地/天空/星空)一一映射。 +4. **身份反转**:凡人 → 神 → 被系统豢养的神 → 超越系统的园丁,完成"一个人的成长"闭环。 + +## 与游戏系统的映射表(写正文时务必对齐) +| 小说权限 | 游戏系统 | 首次解锁卷 | +|---|---|---| +| 基础创造 | 地形 / 物质 | 卷一 | +| 性格 / 天赋 / 职业 | 性格 / 天赋 / 职业 | 卷二 | +| 建造 / 贸易 / 势力 | 建造 / 交易 / 战争 / 势力 | 卷二~三 | +| 神器 / 装备 | 神器 / 装备 | 卷三 | +| 种族 / 法则(秩序混沌) | 种族 / 系别 | 卷四 | +| 天象 / 航海 | 传奇事件(天象) / 航海 | 卷五 | +| 世界等级 / 星空 | 世界等级 / 五层 | 卷六 | +| 跨界互动 | 独立世界 + 异步互动 | 卷七 | +| 专属法则(时空调) | 终焉 / 秘辛事件 | 卷八~九 | +| 超越权限 / 模板 | 园丁系统本体 | 卷十 | diff --git a/novel/宇森创世正史_篇1-10.md b/novel/宇森创世正史_篇1-10.md new file mode 100644 index 0000000..e2f35c8 --- /dev/null +++ b/novel/宇森创世正史_篇1-10.md @@ -0,0 +1,607 @@ +# 卷一 · 创世之始 + +> 从能量为 0 的穷神,到点亮第一簇火、养出第一个聚落。这是宇森宇宙的起点。 + +--- + +## 序章 · 最后一班,与最初的一班 + +林默是在凌晨三点十七分死去的。 + +死因没什么戏剧性——连续第四十一个小时对着显示器,心脏先一步下了班。最后映进眼底的,是屏幕上那行还没保存的方案文档,和右下角弹出的「内存不足」。组长两小时前在群里 @ 他:「默哥,天亮前这版必须出,客户等着。」他回了句「马上」,然后把第三杯凉透的咖啡灌进了胃里。 + +黑暗来得干脆。 + +再睁眼时,没有病房,没有急救的蓝光,只有一片望不到尽头的灰白。 + +不是雾,不是墙,是某种更本质的「空」。脚下没有实地,周身没有空气,可他偏偏稳稳地「站」着,像被某种规则托住。视野正中央,半透明面板无声浮起: + +> 【上帝模拟器 · 已绑定】 +> 【身份:世界管理者】 +> 【权限:创造一切元素(物质 / 生命 / 法则 / 文明)】 +> 【当前能量:0】 + +一行冰冷的提示之后,林默才慢慢嚼出自己成了什么——不是这个世界里卑微的居民,而是它的造物主。山可以凭空隆起,海能够随手倾泻,连「重力」与「时间」这种底层规则,都能被他像拨动开关一样改写。 + +前世四十一年,他连给自家阳台换个灯泡都要怕漏电。如今一念可造山海?那点社畜的狂喜刚冒头,系统就补了一刀,把这份亢奋砸回地面: + +> 【警告:能量源自文明的发展与信仰。没有生灵,您连点亮第一缕火光都做不到。】 + +也就是说,他空握一身神权,却穷得连颗石头都搓不出来——一个兜里只有「0」的穷神。 + +就在他盯着「能量:0」发愣时,面板角落弹出一条闪烁的红字: + +> 【检测到初始文明种子 · 已坠落至世界边缘。是否投放?】 + +林默抬起手。指尖穿过灰白,点下那个「是」。 + +「投放。」 + +下一秒,一簇微光砸进无垠的虚空。 + +他知道,自己这趟「上帝」的差事,从这一刻,才算真正开场。 + +--- + +## 第1章 · 灰白沙盘 + +能量为零的世界,比林默想象的更荒凉,也更会嘲讽。 + +他先试着「造一座山」。面板闪了一下,弹出一行小字: + +> 【创造失败:能量不足。本次所需最低能量 1,当前 0。】 + +一座山要 1 点能量。他不信邪,又试着「点一团火」: + +> 【创造失败:能量不足。本次所需最低能量 0.1,当前 0。】 + +连火都要 0.1。他凭着前世玩游戏的手感,意念里猛地一搓,想凭空攥出一颗石子——灰白的空间纹丝不动,连粒尘埃都没飘起来。 + +> 【创造失败:能量不足。本次所需最低能量 0.01,当前 0。】 + +「……」林默盯着那行「0.01」,半天没吭声。一颗石子,零点零一。他连零点零一都付不起。 + +他又试了「造一汪水」「唤一阵风」「凝一缕光」,提示音一个接一个,像超市收银台在他空钱包上反复扫码: + +> 【创造失败:能量不足。本次所需最低能量 0.05,当前 0。】 +> 【创造失败:能量不足。本次所需最低能量 0.03,当前 0。】 +> 【创造失败:能量不足。本次所需最低能量 0.02,当前 0。】 + +前世他被甲方用「预算为零」折磨了半辈子,没想到死了换个坑,还是被「余额为零」卡着。只不过以前是被人卡,现在是连宇宙都嫌他穷。 + +「所以这就是当上帝的体面。」他对着空无一物的虚空自嘲,「连呼吸都欠着宇宙的最低工资。」 + +可他很快发现,这方灰白并非死寂。当他凝神去「看」,视野里浮起一层极淡的网格,从脚下向四周无限延展,像一张等待落子的棋盘。线条是冷蓝色的,带着细微的刻度,一格一格没入远处的灰白里。他低头看自己的「脚」——那不过是网格上一个微微发亮的交点。 + +他试着沿网格走了几步。不是用腿,是用「念头」往某个方向一送,视角便平移过去,脚下的交点随之挪动。这感觉不像走路,更像在地图上游标。原来他不是被困在这片空里,他是站在自己世界的「沙盘」之上,而沙盘之外,还有沙盘。 + +他往更远处「望」。灰白并非均匀的一片——在目力将尽的边缘,他隐约看见另外几块暗淡的网格碎片,悬浮在更深的虚无里,尚未点亮,也无从抵达。那是别的「待创世界」吗?系统没给答案,只在他视线触及的瞬间,把那些碎片藏进了灰白深处。林默把这点疑惑压下了:眼下连脚下的棋盘都还没填,操心别的棋盘未免太早。 + +棋盘的尽头,那簇被他投放的文明种子,正悬在一道微光里,缓慢旋转。 + +> 【文明种子 · 状态:休眠中】 +> 【内含:初始生命蓝图 ×1、文明火种 ×1】 +> 【激活条件:管理者注入首批引导意志】 + +「引导意志?」林默皱眉。系统给了段简释:他不必事必躬亲地「手搓」万物,只需在世界的关键节点施加一次影响——一次抉择、一句低语、一道微光——文明便会顺着自己的逻辑自行生长。这恰好是「园丁」二字的真意:播种,引导,然后退后,看它活。 + +简释里还划了一道界限:凡是「从无到有」的创造——山、海、火、风、法则——都要消耗能量;而凡是「在已有节点上轻轻一推」的引导——把将熄的火拨旺、把迷路的人引向水源——只耗那点微乎其微的「引导意志」,不计入能量。一句话:造物要钱,引导免费。 + +他咂摸着「园丁」这个词,忽然觉得这差事没那么寒酸了。当神当到要乞讨一句话的权限,固然憋屈;但换个角度——他不是来当苦力的,是来当「让一个世界自己长起来」的那只手。就像一个投资人,投下一颗种子,然后等它自己长大、分红。 + +「总算有个能用的定位。」他松了口气。 + +但紧接着一个问题冒了出来:既然造物要能量,能量又只能靠文明来产,那在文明长起来之前,他岂不是永远 0?系统冷冰冰地确认了这道死局——能量唯一来源,就是文明的发展与信仰。也就是说,他必须先「无中生有」地把文明养出第一口气,能量才会反哺回来。 + +而手里唯一的破局筹码,就是那簇免费投放的文明种子。 + +那颗种子是「已坠落至世界边缘」被系统检测到的,也就是说,不是他自己造的,是「掉」进来的。谁掉的?系统没说。林默直觉这事儿不简单——天上掉下来的免费种子,往往连着更大的局。但他眼下没资本挑三拣四。先接住,再看清。 + +他飘向那簇种子。微光触手的刹那,一段信息涌入脑海:这是一簇最原始的「人」——没有种族、没有天赋、没有记忆,只是一团团等待被点亮的意识火苗。他们将从零开始,在灰白里摸索火、食物、聚居,用一代代试错,把荒原种成文明。而他的任务,是在他们每一次濒临熄灭时,递上那根关键的柴。 + +林默翻了翻那份「生命蓝图」。模板极简:一具能感知冷热、能移动、能彼此靠近的躯壳,没有性别,没有记忆,连「怕」都只是本能的蜷缩。他忽然有点心疼——上来就这么难,连件像样的身子骨都没有,就要在灰白里求生。 + +「行吧,」他对那团微光说,「你们负责活,我负责在你们快不行的时候,扶一把。」 + +> 【是否激活文明种子?】 +> 【提示:激活后将消耗您当前全部能量(0/0),并开启首个世界锚点。】 + +林默笑了:「反正兜里比脸还干净,点。」 + +> 【激活成功。世界锚点已生成:未命名。】 + +他忽然想起前世给项目起名的习惯。一个世界,总得有个名字。 + +「就叫……宇森吧。」他对着灰白说,「寰宇、森罗——一个能自己长出万物的地方。」 + +> 【世界锚点命名成功:宇森(YuSen)】 +> 【锚点稳定性:1% · 待文明填充】 + +命名之后,林默顺手扫了眼面板深处。除了「创造」「引导」两项,下方还排着一长列灰色的图标,标注着「种族」「元素」「性格」「天赋」「神器」「传奇事件」……足足十五个模块,此刻全都挂着一把小锁。他数了数,恰好对得上系统早先提过的「十五大系统」。锁的下方有一行淡到几乎看不见的字:【权限随文明层级依次解锁】。林默咧了咧嘴——合着神也不是一次到位,得一级级打怪升权。他记下了这十五个名字,心里有了谱:眼下能用的只有「生命」与「火种」,其余的,且等着宇森自己长出来,再向他敞开。 + +脚下的灰白轻轻一颤,像沉睡的湖被投入第一颗石子。远处那簇微光,开始向下坠去,拖出一道细小的光尾—— + +它要落了。 + +而就在光尾拉长的刹那,林默余光扫过灰白更深处——那里,似乎有一道极淡的、不属于他的「注视」,正落在即将诞生的宇森之上。 + +临了,他给自己定了三条规矩:一,不替文明做它自己能做的事;二,不在文明看不见的地方偷偷改规则;三,真到必须出手时,绝不手软。这三条后来被刻进宇森的底层逻辑,成了历代园丁的祖训。 + +他没声张。只是把那道注视,悄悄记在了心里,像记下一个尚未浮出水面的对手。 + +--- + +## 第2章 · 坠星 + +光落下的地方,后来被那些活着的人称作「创世原」。 + +林默以意念拉近视角。灰白棋盘的中央,一团微弱的光晕正缓缓铺开,像水面洇开的墨,边缘还带着未散尽的微光。光晕里,二十余个模糊的身影次第浮现——他们是种子里苏醒的第一代「人」,赤条条、懵懂懂,像刚被吹进世界的泥偶。没有名字,没有语言,只有最原始的本能:冷了要靠近,饿了要找,怕了要聚。 + +他们先是彼此打量,继而本能地往光晕最暖的中心挤。一个瘦高的影子不小心撞了同伴,两个泥偶滚作一团,又懵懂地分开——那动作毫无意义,却让林默想起了前世地铁早高峰里同样懵懂的自己。 + +光晕铺开后好一阵,一个胆子稍大的影子忽然伸手,碰了碰身旁那位的脸。两个泥偶同时愣住,仿佛头一回「看见」彼此。那一下笨拙的触碰,没有名字,没有语言,却让林默心头一热——那是宇森历史上第一次「对视」,众生的开篇,就藏在这毫无意义的一碰里。 + +二十几道身影里,有几个让林默下意识留了标记。最小的那个,他叫「小不点」,蜷起来只有一个拳头大;一个总往光晕外探的,他叫「探路者」,胆子明显比旁人大;还有一个,会本能地把摔倒的同伴往中间拢,他叫「抱团者」。这些名字系统不认,只存在林默心里——一个管理者给自己世界第一批居民起的、上不得台面的小名。 + +白昼短暂。很快他们就饿了。灰白里没有食物,连可啃的东西都没有。几个影子伏下身,用鼻子去蹭地面,又失望地抬起头;小不点试着把手指塞进嘴里啃,被抱团者一把拍开。林默下意识想「给他们弄点吃的」,系统却弹出一行: + +> 【提示:园丁仅可于「已存在节点」上引导,无法凭空投喂。当前世界仅激活「生命蓝图」与「火种」,未含「食物蓝图」。】 + +也就是说,他给了「人」和「火」,没给「饭」。饭,得等这帮泥偶自己从环境里摸索——采集、狩猎、或者,先把火用明白。林默第一次真切感到「园丁」二字的重量:他能递柴,却不能替他们把饭煮好。他把这条记进了心里,作为宇森要跨过的第一道文明难题。 + +他甚至试着在光晕外「引导」地表萌发一点可食的绿意,念头刚起,面板就凉凉地提醒:【引导对象须已存在。当前世界无「植物」节点,无法凭空催生。】林默讪讪收了手。行,饭的事,交给他们自己。 + +第一个夜晚来得毫无预兆。灰白的天幕暗下去,寒意顺着某种林默尚不理解的规则渗入光晕。作为锚点的管理者,他竟也能「感知」到那股冷——不是皮肤的冷,是世界的温度在掉。那二十几道身影开始发抖,彼此挤拢,却谁也不知道该如何抵御。探路者还想往光晕外探,被抱团者一把拽回。小不点已经蜷成了团,手指冻得发青,一动不动。 + +> 【事件触发:首夜严寒】 +> 【文明存活率预测:41%】 +> 【建议引导:于光晕边缘落一处火源】 + +林默几乎是本能地伸手,朝那片虚空中按下一缕「意念」。他看不见自己做了什么,只觉心头微微一空——像吹灭一根蜡烛消耗的那点气。与前几次「造物」不同,这一下不耗能量,只耗那点微乎其微的「引导意志」。系统早说过:创造元素要能量,而在已有节点上轻轻一推,是园丁的本分,不收钱。 + +下一瞬,光晕边缘腾起一簇豆大的火。 + +那二十几道身影猛地转头。他们不认识火,却认得暖。探路者第一个伸出手,被热浪一烫,又缩回,继而再次凑近,像在跟这团陌生的东西试探交手。很快,他们围成了圈,瑟缩的肩膀松开了。 + +抱团者把小不点拖到了火边。暖气裹上去,他冻僵的手指动了动,终于又蜷着喘上了气。林默盯着那一小团重新起伏的背影,忽然觉得这簇豆大的火,比他前世见过的任何灯火都来得郑重。 + +> 【引导成功:火源已落】 +> 【文明存活率预测:78%】 +> 【能量 +0(文明尚处萌芽,暂未产生信仰)】 + +林默盯着那行「能量 +0」,忽然懂了系统的残酷:他刚才那一下,是白送的。没有回报,没有反馈,只有一群还不会感恩的泥偶围着火发抖。前世他加班好歹有工资,如今当神,连个五险一金都没有。 + +可他没来由地笑了。 + +因为就在火光摇曳里,他看清了——探路者正笨拙地用两根枯枝去拨弄火堆,想让它更旺些。那动作毫无意义,却是一道嫩芽:这团意识火苗,开始自己「想办法」了。更让他心头一动的,是探路者接着把火边一块发亮的小石扒拉进焰心,火星「噼啪」一溅,那泥偶整个僵了一瞬,随即第一次做出了近似「笑」的咧嘴——那是宇森历史上第一个被惊喜击中的人。探路者没停,又试着把一根更大的枯枝架上去,火苗窜高,照亮了周围几张懵懂的脸。林默不动声色,只在心里给探路者多画了一颗星:这帮泥偶里,这家伙怕是头一个「领袖」的苗子。 + +> 【检测到:文明自主行为 · 首次】 +> 【评价:萌芽。世界锚点稳定性 +1。】 + +灰白棋盘上,那一点光晕,比刚才亮了半分。 + +林默收回目光,望向更远的虚空。那里,还有无数片未点亮的灰白,等着被落子。而他兜里,依然只有那串刺眼的「能量:0」。 + +但至少在创世原,有了一簇火,和二十几个开始想办法活下去的人。白天的饥饿还没解,明晚的严寒还会来,可他们已经学会了一件最重要的事——往火边靠。 + +火苗稳定的后半夜,林默没走。他隔着光晕看他们如何熬过宇森的第一个长夜:有人挨着火堆蜷着睡去,呼吸第一次有了节奏;抱团者没睡,守在火边,时不时把探路者拨偏的柴扶正;小不点被挤在最里圈,偶尔在梦里抖一下,又被身旁的暖意按了回去。林默忽然意识到,时间这件事,是有了火才在他们身上开始的——白昼与黑夜不再是灰白均匀的空,而是「守着火」和「等着天亮」的盼头。一个世界最朴素的钟,是火给的。 + +长夜将尽时,林默第一次以「管理者」而非「穷人」的视角,重新审视这片灰白。他忽然懂了:能量为 0 不是羞辱,是起点。正因为这穷神开局,他才被迫扔掉「神力万能」的妄念,老老实实学会「不靠蛮力、靠引导」的真本事。日后宇森若真能长成参天之木,这一课,便是根。 + +「这才像点样子。」他轻声说。 + +远处,虚空深处,那道昨夜一闪即逝的涟漪,又轻轻荡了一下。 + +这一次,林默看清了——涟漪的中心,似乎也有一双「眼睛」,正安静地,看着他。 + +他在心里问了自己一句:那双眼睛,是在看宇森,还是在看,作为管理者的我? + +灰白没有回答。但林默已经决定,暂不打草惊蛇。先把这个世界的火,养旺再说。 + +那双眼睛没有再动。涟漪散去,灰白重归均匀的空。林默却把这一幕牢牢记下——等宇森有了第一批会「想」的人,等能量第一次从信仰里流回他体内,他要把这道注视的来路,查个明白。 + +--- + +## 第3章 · 第一缕炊烟 + +光晕里的日子,是靠火数着的。 + +林默守了创世原三个「昼夜」——他现在能用锚点的节律大致分出明暗,虽然宇森还没有真正的太阳,只有灰白天幕周期性的暗与亮。火没灭过。石(也就是从前的抱团者)像个固执的老妈子,谁想往火里添柴都得过它那一关,添少了它瞪你,添多了它自己动手拨匀。林默隔着虚空看这景象,莫名想起前世公司茶水间那个总嫌咖啡机没洗干净的行政大姐——权力的甜头,原来在哪都长一个样。 + +饥饿却没被火吓退。 + +基质——就是灰白凝结出的那层硬壳——烤过的地方越啃越少。到第三个白天,二十几道身影又开始在光晕里舔嘴唇。芽(从前的小不点)饿得直往石怀里钻,叼着石的手腕啃,被轻轻拍开,委屈地缩成团。林默看着芽那副样,心里某根弦被拨了一下:这小东西,像极了他前世在地铁上见过的、攥着妈妈衣角喊饿的小孩。 + +风在光晕里来回窜,一会儿扒拉木编到一半的枯枝筐,一会儿被焱拍开。木闷头把基质碎屑编进枯枝缝里,像在试着做个能装东西的物件——虽然后来证明那玩意漏得跟筛子似的,可「容器」的念头,已经在了。焱成天黏着火堆,往里添柴添得兴高采烈,有回差点把刚睡着的芽燎着,被石一巴掌扇到边上,委屈了半天。 + +林默把这幅鸡飞狗跳的群像看了个遍,心里那点「造物主」的孤高,早被磨成了「幼儿园园长」的无奈。前世他最怕带新人,如今倒好,带一园连话都不会说的泥偶。 + +林默想帮忙。念头一起,面板照旧凉凉: + +> 【引导对象须已存在。当前无「食物」节点。】 + +行,老规矩。他不造,只「引」。 + +他让一缕引导意志,轻轻搭在岩(从前的探路者)正拨弄火堆的手上——不是喂,是「指」。像有人把你的视线,往火边上那堆被烤得发脆的基质碎屑,偏了半寸。 + +岩顿了顿,捏起一块烤脆的碎屑,塞进嘴里。嚼了两下,眼睛亮了。 + +那东西原本啃不动,火一烤,竟松了,带着点焦香,下肚后胃里有了底。岩猛地抬头,把碎屑分给身边的人。一圈人学着他的样,把基质往火边凑。第一缕真正的「炊烟」——烤基质的薄烟——从创世原升起,混在火光里,懒懒地散进灰白。 + +林默盯着岩嚼碎屑那副郑重其事的样,差点笑出声。他见过太多人第一次吃到好东西的表情,可这是宇森历史上第一口「熟食」,当事人还不知道自己刚跨过了多宽的沟。 + +> 【文明自主行为 · 食品加工雏形】 +> 【评价:求生。锚点稳定性 +1。】 +> 【能量 +0(尚无信仰,仅为本能求生)】 + +林默盯着「能量 +0」,已经麻木了。但看着那圈人第一次因为「吃饱」而松弛的肩膀,他觉得这 0 比啥都值。前世他加班到凌晨,最盼的不也就是一口热乎饭、一个能松口气的肩么。如今他成了神,能给的却也只是这一口热乎——神又怎样,填不饱别人的胃,就只能看着别人自己填。 + +芽忽然松开石,自己伸手,朝着岩手里的碎屑抓。这是它第一次「主动要吃的」,不再是哭,是「讨」。林默心里一热:一个文明的「需求表达」,就从这一抓开始。 + +「会自己找吃的了。」他轻声说,「下一步,该嫌难吃了吧。」 + +果然。到傍晚,岩把一块烤糊的碎屑吐出来,皱着脸看火,像在嫌弃这饭太糙。林默差点笑出声——一个连语言都没有的泥偶,已经开始挑食。他甚至有点欣慰:会挑嘴,说明饱过,说明这文明算真活过来了。 + +他顺手翻了翻系统对「基质」的注解:世界最底层的凝结物,非生命,仅能勉强充饥,毫无营养。换句话说,这顿饭只是让文明「不死」,撑不到「活好」。真食物,得等他哪天攒够能量,自己激活「植物」节点——或者,等文明从环境里摸出门道。 + +注解最后还有一行小字,让林默眉头一皱:【基质存量:创世原周边 · 低。】系统连库存都帮他盘好了。他忽然觉得自己不像造物主,像个刚接手贫困县的新任县长,上任第一件事就是查粮仓。 + +他忍不住在心里嘀咕:「那你当初直接给我个食物蓝图不行?」 + +系统半秒后回:【文明须自觅出路,否则永为依附。园丁之责,在引不在予。】 + +林默咂咂嘴。这道理他懂,前世带团队也一个样——手把手喂出来的,永远长不大。可懂归懂,看着芽瘪下去的肚皮,到底还是不好受。 + +夜深,众人缩进勉强挡风的角落睡了。林默没走。他独自悬在灰白上,守着这簇豆大的火,忽然感到一种前所未有的孤独——他是这方天地的神,却是个没人在乎、没人供奉、连香火都收不到半点的神。可偏偏,就是这份没人知道的孤独里,藏着他这辈子最踏实的东西:他亲手养出的火,正暖着二十几个命。 + +他又把视角压低,挨个看那圈围火的人。岩啃完碎屑,用手背抹了嘴,冲旁边的人扬了扬下巴,喉咙里滚出半个「呷」音——是在招呼人「吃」。石把分得的一块碎屑,掰了一半塞给怀里的芽,自己嚼那半块,嚼得极慢,像要把这点滋味多留一会儿。风窜到木身边,用脑袋顶了顶木手里的枯枝筐,被木反手按住脑袋,也不恼,笑嘻嘻滚开。 + +林默看着,忽然觉得这画面比任何系统提示都金贵。 + +他试着在管理者面板里调出「文明发展度」。一行淡蓝的进度条浮起,几乎贴着底,却确确实实在动: + +> 【文明发展度:0.3%】 +> 【生存:勉强维持|聚合:雏形|语言:萌芽前|工具:无|信仰:无】 + +那根几乎看不见的细线,让他想起前世带的一个项目,刚立项时也是这副惨样,所有人都说做不起来。后来呢?后来他熬了四十天,项目真跑起来了。如今这项目,是他拿命换来的世界。 + +「0.3%。」他喃喃,「放前世,这 KPI 早被老板砍了。」 + +可他心里没有半点不耐。0.3% 后面,是二十几个真真切切、会冷会饿会朝他喊「高高」的命。进度条可以慢,命不能等。 + +他忽然很清楚地知道自己该怎么当这个神:不是高高在上的造物主,是蹲在泥里、陪这批人把 0.3% 一点点磨上去的那只手。园丁二字,到此才算真正落进他骨头里。 + +「就算穷,」他对灰白说,「我也把你们养出第一个聚落来。」 + +话音落,他余光又扫到虚空深处那双眼睛。它还在,比昨夜近了些,安静地悬着,像在等什么——等他犯错,还是等他证明自己配得上这方世界? + +林默没躲它的注视。他甚至朝那方向,极轻地一点头,像个棋手落子前,先跟对面的对手致意。 + +钩子:林默默默算了一笔账。照这吃法,创世原周边的基质,撑不过七个昼夜。火能烤,却变不出更多。而文明的胃口,只会随人数涨。真正的饥饿,还没来——但倒计时,已经开始了。 + +--- + +## 第4章 · 饥饿的冬天 + +第七个昼夜临界,周边基质见了底。 + +岩是第一个察觉的。它蹲在光晕边缘,用手指刮了半天,只刮下一点白沫,凑到嘴边尝,什么都没有。它回头看了一圈缩着的人,又看向灰白更深处——那里更冷,却也更大,更大就意味着,可能有更多基质。 + +它站起来,往光晕外走了一步。 + +那是创世原历史上第一次「出圈」。林默心头一紧,下意识想伸手拦,又硬生生忍住。园丁的规矩第一条:不替文明做它自己能做的事。岩能走,就让它走。可他到底不放心,把一缕感知悄悄缀在岩的背后,像放风筝的人攥着那根看不见的线。 + +岩走了。石犹豫了一下,把芽塞给旁边木(那个手巧的影子),自己跟了上去。接着是风——一个总爱乱跑的影子——还有三两个胆大的。七道身影,排成歪歪扭扭的一列,没入灰白。 + +林默把视角跟上去。光晕外的寒意明显更重,他的「管理者感知」都被冻得一哆嗦。但他很快发现,正如他所料,灰白里漂浮着更密的基质结晶,像撒了一地的碎瓷,泛着冷冷的光。 + +岩蹲下,笨拙地掰下一块,掂了掂,揣进怀里。又掰一块。它动作越来越快,像怕这东西会跑。其他人学着,风最积极,窜出去老远,差点迷在均匀的灰白里,被岩一声低吼拽回来。 + +他们学会了「采集」。 + +回程走到一半,岩在均匀的灰白里迷了向——这里没有光晕那点暖意指引,四面都是一样的空。它转了两圈,停下,罕见地露出了慌。林默看着,心里叹了口气。 + +他泄出一丝引导意志,不是告诉它往哪走,只是让岩心底那点「家」的方位感,亮了半分。像黑屋里有人悄悄把门缝透进的光,调亮了一点。 + +岩立刻抬头,循着那点微光似的直觉,领着队伍准准地扎回光晕。 + +> 【引导成功:归途指向(微量意志,不耗能量)】 +> 【文明自主行为 · 采集诞生】 +> 【锚点稳定性 +1。能量 +0。】 + +七道身影归来,怀里鼓鼓囊囊。火重新旺起来,基质碎屑烤得噼啪响。林默看着这趟往返,第一次觉得「探索」二字有了重量——文明不是被圈养的,是肯迈出去的。这一步,比它后来造出任何奇迹都让他骄傲。 + +可回来没多久,麻烦就生了。风把自己采的结晶偷偷多藏了一捧在身后,被石一眼瞥见。石走过去,伸手要。风抱紧,往后缩。两个影子僵持,发出含混的呼喝。这是创世原第一次「争」——不是争火,是争食。 + +林默没插手。他隔着虚空看,心里五味杂陈。私藏、争夺,放在前世是毛病;可放在文明起点,恰恰是人「懂得为自己打算」的证据。没有「我」,哪来「我们」?可「我」一旦长出来,就难免刺到「我们」。 + +石到底从风怀里抠出那捧结晶,平分给所有人。风瘪着嘴,却没再抢。林默注意到,分完之后,风第一次主动把最大的一块,递给了芽。 + +那一递,让林默觉得,这文明的「私」,或许长得出「公」来。 + +夜更深,寒意比昨日重。有了储备,人却更觉冷——大概是吃饱了,才顾得上喊冷。林默听着棚里此起彼伏的哆嗦,忽然很想给这世界添件「衣服」,可「衣物」也算造物,要能量。他依旧是个兜里空空的穷神。 + +他再把感知投向灰白深处,想再确认那点微光,却见岩也正望着那个方向。它愣住,往那点微光多看了两眼,喉头动了动,像想把什么记下来,才被石拽回火边。 + +林默的感知落在那点微光上,系统慢吞吞弹了一行: + +> 【检测到:第二枚文明种子 · 状态:休眠 · 距离:极远】 +> 【备注:种子来源 · 未知。】 + +来源未知。和第一颗一样。 + +林默盯着那行「来源 · 未知」,半天没动。他想起自己那颗种子,也是「坠落至世界边缘」被系统检测到的——换句话说,不是他造的,是「掉」进来的。如今第二颗也一样,来源成谜。 + +这让他后背微微发凉。如果种子是「谁」播的,那播种子的人,是不是也像他一样,是某个世界的「管理者」?宇森之外,是不是还有别的管理者,守着别的棋盘? + +他立刻调出权限面板,翻那十五个灰锁——「种族」「元素」「性格」「天赋」「神器」「传奇事件」……翻到底,没有「其他管理者」这一项。系统对他,只字未提「同行」。 + +是根本没有,还是刻意瞒着? + +林默想起前世职场:老板从不会主动告诉你,还有别的组在抢同一个资源。信息的缺席,往往本身就是信号。 + +他决定:暂不声张。第二颗种子远在极处,休眠着,暂时威胁不到创世原。他现在的本分,是把脚下这二十几号人,养出第一个聚落。至于棋盘外的事,等自己有了能量、有了底气,再去掀。 + +那一夜,林默第一次「失眠」。神不需要睡,可他需要想。他想岩、想石、想芽,想那点远处的微光,想虚空里那双越来越近的眼睛。一个穷神,守着一方刚冒芽的天地,四周却似乎全是看不清的局中人。 + +他忽然笑了一声,笑自己。前世怕老板、怕客户、怕月底 KPI;死了当神,怕的换成种子、眼睛、和另一个不知在不在的棋手。怕,倒是一以贯之。 + +可怕归怕,手不能停。 + +钩子:第二颗种子。是机缘,还是对手?林默把这点微光,和虚空里那双眼睛,一起压进了心底。他隐约觉得,宇森这盘棋,不只他一个棋手。 + +--- + +## 第5章 · 第一个名字 + +采集解决了饿,却没解决「乱」。 + +二十几道身影挤在光晕里,谁是谁全靠林默自己记小名。他们彼此呼唤,只用含糊的气音和手势。林默有一天忽然觉得不对——一个文明,如果连「个体」都没有,谈何「成长」。前世他带团队最深的一课:你连人都认不全,就别指望带出战斗力。 + +他决定给它们正经起名。 + +这不是造物,不算越界。系统早说过,赋予标识属于引导范畴,只耗微量的「引导意志」。林默挑了个安静的昼夜,把一缕意志,轻轻种进每个人的心底,各落下一个清晰的音节符号。 + +起名这活,他熟。前世给项目起过名,给养的猫起过名,给看不惯的同事起过只有自己懂的外号。如今给一整个世界的第一批人起名,他反倒郑重起来。 + +岩,还是岩——那个敢探路、敢砸火的。音节落下去时,岩忽然抬手拍了拍自己胸口,像把这两个字,按进了骨子里。 +石,还是石——那个守火、护芽的。它听到,扭头望了眼火,又望了眼怀里的芽,像是认了。 +芽,还是芽——最小的那个。它眨了巴眼,显然不懂,但没反抗。 +风,那个总爱乱跑的。音节一落,它真原地转了个圈,把众人都逗乐了。 +木,那个手巧、会编枯枝的。它手里还攥着那漏底的筐,被点名时憨憨一笑。 +焱,另一个成天黏着火堆的。它兴奋地往火里添了根柴,像在说「在呢」。 +还有几个:禾、川、砺、陶……一个一个落下去。 + +二十几个音节,落进二十几团意识。它们彼此喊名字的瞬间,光晕里第一次有了「你」「我」之分。从前是「那个影子」「另一个影子」,现在是「岩」「石」「芽」。 + +> 【文明自主行为 · 个体标识确立】 +> 【评价:社会雏形。锚点稳定性 +2。】 +> 【能量 +0。】 + +小爽点不在能量,在林默心头那点热乎——他亲手给这世界的第一批居民,发了「身份证」。这比任何系统升级都让他踏实:从今往后,创世原死的每一个、活的每一个,都叫得出名字。 + +命名之后,变化立竿见影。风再窜出去,岩一声「风!」它就乖乖回来;木编筐,石隔着老远喊「木,给芽留个角」,木就真在筐沿留了个凹。一个文明最朴素的「组织」,从互相叫得应名字开始。林默忽然发现,他再引导时,能「指名」了——落一缕意志在「岩」身上,不必再笼统地拂过一群。管理,第一次有了对象。 + +可命名完成时,林默忽然一僵。 + +虚空里那双眼睛,似乎「数」了这些人。不是敌意,更像记录。像有人在册子上,一笔一笔,把创世原的居民,记了下去。 + +林默后背一凉。它记这些人干嘛?盘点到数?监控?还是……等哪天收割? + +他想起第二颗种子、想起种子「来源未知」。如果真有「上家」,那这双眼睛,会不会就是上家派来的眼睛? + +他压下不安,没声张。眼下创世原撑不过几个昼夜的饥饿,内忧大过外患。那双眼睛爱记就记,等他这批人长出了牙,再跟它算账。 + +夜深,众人缩进棚里。林默听见他们第一次像「点名」似的,互相用名字道晚安——「石」「芽」「岩」……含混却认真。一个没有语言的文明,用名字,替自己关上了灯。 + +林默隔着虚空听着,鼻子有点酸。前世他最怕的,不是加班,是开会时老板念错他的名字、念成「小林」「阿默」混着叫。一个名字被认真喊对,原来这么金贵。如今这群泥偶,用他给的名字,认真地喊着彼此——他这个没人记得名字的社畜,反倒成了给一整个世界命名的人。 + +他翻出「文明发展度」,那根细线微不可察地右挪了一点点:【社会:雏形→萌芽】。 + +钩子:更让林默意外的是,就在这一片名字声里,芽忽然转头,清清楚楚地喊了一声——「石」。 + +不是喊火,不是喊吃。是喊一个「人」。 + +那是宇森历史上,第一个被喊出来的名字。林默隔着虚空,听见这一声,半天没言语。一个文明,从这一刻起,不再只是「求生的群」,而是「相认的人」。 + +--- + +## 第6章 · 语言萌芽 + +有了名字,「词」就拦不住了。 + +围火的第六个昼夜,岩指着跳动的焰心,喉咙里滚出一个含糊的音节:「呼——」。旁人跟着学,含混一片。林默耳朵一竖——系统在他「管理者权限」下,无声解锁了一层新的监听: + +> 【文明语料监听 · 已开启】 +> 【首词记录:呼(火)。】 + +第一个词,是「火」。林默觉得理所当然。是火把这批泥偶从冻死边缘拽回来的,他们不先记住火,才怪。 + +可他没料到,词会来得这么密。 + +紧接着是「姬」(寒)——岩缩着脖子,朝灰白上方努嘴,吐出这个音。众人立刻共鸣,往火边缩紧。再是「呷」(吃)——岩啃碎屑时顺嘴滚出来的,从此成了围火最常听到的字。然后是「石」,人名的复用,被喊的人会抬头。 + +词不多,却像种子,落地就活。 + +风跑回来,兴奋地蹦着喊「外——」,指的是光晕外的灰白。林默由此确认:他们已经有了「内」与「外」的概念。木编筐,被岩喊一声「木」,它应声。一个名字加一个词,文明的交流,第一次有了「主谓」的雏形。 + +林默翻着系统实时跳动的语料库,有点想笑。前世他最烦写周报,如今倒成了这世界第一个「语文老师」,还是旁听的那个。 + +> 【文明发展度:0.8%】 +> 【语言:萌芽|词汇量:6】 + +0.8%。比昨天的0.3%涨了一截多。林默盯着那根线,忽然懂了什么叫「肉眼可见的成长」。 + +最戳他的是芽。 + +夜里寒意又渗,芽缩在石怀里,忽然嘟囔了一声:「怕。」 + +不是冷,不是饿,是「怕」——一种纯粹的情绪,被一个还不会完整说话的泥偶,笨拙地命名了。旁边几个听到,不约而同往火边又蜷紧了些。那一缩,是共鸣。 + +林默没笑,反而有点鼻酸。怕,才是「想活」的起点。一个连怕都不会的文明,不配叫文明。 + +他第一次主动泄出一缕引导意志,不是指路,不是点火,只是轻轻「抚」过芽的梦境,像大人把手按在惊哭的孩子背上。芽的呼吸慢下来,缩着的手指松了。林默忽然觉得,这比造山造海都难——山没有梦,芽有。 + +> 【文明自主行为 · 情绪词诞生】 +> 【评价:意识觉醒。锚点稳定性 +2。】 +> 【能量 +0。】 + +可「怕」这个字一出口,麻烦也来了。芽连着几晚喊「怕」,众人开始不安,把灰白深处那点异样,和「怕」连在了一起。有一回灰白里掠过一道微光(大概是远处基质的反光),风猛地喊「高高——怒!」,所有人哗地伏倒,瑟瑟发抖。 + +林默哭笑不得。他们把「高高」当成了会发怒的神,一道反光就成了神威。他赶紧泄了丝引导意志,把那点「不安」轻轻抚平,又让火苗窜高半寸,示意「高高」没生气。 + +众人见火稳,慢慢松开,面面相觑,又咯咯笑起来。 + +那场误会之后,林默发现他们开始用「高高」编最简单的祷词——围火时,岩带头,朝灰白上方含糊喊:「高高……呼……」大意是「高高,给火」。笨拙,却庄重。 + +文明开始「造神」了。而他们还不知道,自己造的这位「高高」,正蹲在灰白上,又心酸又得意地听着。 + +林默忽然有点慌。他们喊「高高」的时候,眼里有依赖,也有敬畏。可他这个「高高」,连颗石头都造不出。万一哪天他们发现,高高其实穷得叮当响,这信仰,会不会塌? + +他压下这念头。先让他们有火、有家、有词,再说别的。 + +词一长,权就跟着来。岩因先会说话、又认得火,渐渐成了围火时发号施令的那个——「风,外,采」「木,筐,给芽」。一句「外」,让风成了固定的采集体;一句「火」,让岩自己稳坐火师之位。林默看着,心里明镜似的:话语权,是文明的第一种权力,比拳头来得早。 + +岩越用越顺。它用「外」派风去探,风从此固定成采集体;用「火」巩固自己火师的地位,无人质疑。林默看着,心里明镜似的:语言不止是交流,更是「组织工具」。一句话,就把二十几号人,拧成了能分工的群。 + +> 【社会结构:雏形首领(岩)· 出现】 +> 【评价:组织力萌芽。锚点稳定性 +2。】 + +林默想起前世公司,第一个会写汇报的人,往往就成了组长。文明的「领导」,也是这么来的——不是谁拳头硬,是谁先把话说圆。 + +他既欣慰,又警醒。首领出现,意味着有了「统一意志」,办事快了;可也意味着「少数人替多数人拿主意」。往后他这园丁的尺度,只会越来越难拿:该不该干预首领的决定?岩若领着人走错路,他插不插手? + +这问题他暂时答不上来。他只记下:从今天起,引导文明,不只要看它活不活,还得看它「谁在说了算」。 + +林默又翻出语料库,发现「高高」二字,出现的频率越来越高。岩每做决定,必朝灰白上喊一声「高高」;众人遇险,第一反应是喊「高高」。这让他既暖又慌——暖的是被记挂,慌的是这依赖一旦成了惯性,将来他若无力回应(比如造不出他们想要的东西),信仰会反噬。 + +他想起前世信誓旦旦的甲方,合作黄了之后,骂得比谁都凶。信仰和信任一样,给的时候毫无保留,撤的时候也毫不留情。 + +「别急着当救世主。」他警告自己,「你先是个穷神,别让人指望太高。」 + +钩子:又过了几个昼夜,林默听见他们围火时,不再只喊「高高」,开始给「高高」编故事——说高高从灰白里点亮了第一簇火,说高高的手,能轻轻一指,就让迷路的人回家。他们不知道,这些故事,句句是真的;更不知道,故事里慢慢多出了一位「高高的对手」——那影子,来自林默曾望向的虚空深处。林默听见那「对手」的雏形,后背一凉:自己的恐惧,被文明吸进了神话。 + +--- + +## 第7章 · 工具与火种 + +第七个昼夜之后,取火成了日常,也成了隐患。 + +林默每次落火,都得泄一缕引导意志。虽不耗能量,可他总觉得——这像天天给孩子喂饭,喂久了,孩子永远不会自己拿筷子。 + +他等着岩自己琢磨出来。 + +机会来得不轻松。第十个昼夜,岩蹲在火边,手里两块基质碎屑,无意识啪地一碰。没在意。再碰,迸出一点火星,溅到枯枝上,灭了。岩愣了下,盯着两块碎屑,又使劲一砸——火星更大,枯枝尖窜起一簇小焰。 + +岩盯着那簇焰,看了三息。 + +它没有像前几次那样欢呼,而是皱起眉,把两块碎屑捡起来,翻来覆去地看,又试着在枯枝上磕了磕。一下,两下,第三下,火星稳稳溅起,枯枝燃了。 + +岩忽然猛地抬头,满脸都是「我做到了」的亮。它扔了碎屑,就着那簇自己迸出的火,把更多枯枝凑上去。火,自己烧起来了。 + +不用林默。不泄意志。纯靠两块石头、一双手。 + +林默屏住「呼吸」——神不需要呼吸,可他那一刻,确实屏住了。他等这一下,等了整整七个昼夜。中间有两次,岩冻得发抖、火将熄,他手指都蜷起来了,差点直接落火,又硬生生松开。他怕一松手,这文明就永远学不会自己拿筷子。 + +> 【文明自主行为 · 重大:火种掌控】 +> 【评价:脱离依赖。锚点稳定性 +5。】 +> 【能量 +0(自主行为不产信仰,但世界根基骤实)】 + +林默盯着那行「火种掌控」,半天没言语。能量还是 0,可他这辈子没见过比这更值钱的 0。前世的他,最怕的不是穷,是「离了公司啥也不是」。如今他亲手养出的文明,终于离了「高高」也能自己活——这比任何升级都爽。 + +> 【文明发展度:1.5%】 +> 【工具:萌芽|火种:自主】 + +火自己烧着,岩守在边上,添柴、拨旺,动作越来越熟。旁边的人围过来,眼睛亮亮地看着,有人学着岩的样子,捡两块碎屑磕,磕出火星时,欢喜得蹦起来。 + +取火术,一传十,十传二十几。创世原再不怕夜。 + +岩把火养旺,忽然抬头,望向灰白上方,那双眼睛该在的方向。它不喊,不拜,就那么定定地看着,像在确认:高高,你看见没。 + +林默在虚空里,轻轻「嗯」了一声。没人听见,可他觉得岩应该收到了。 + +取火那晚,岩顺手把一块湿基质按进火边的余热里,无意中烤出个硬邦邦的凹壳。它拿起来,翻看,又往里塞了把碎屑——像个本能的「容器」尝试。林默瞥见,心头一动: + +> 【文明自主行为 · 工具雏形(容器/盛具)】 +> 【评价:手巧。锚点稳定性 +1。】 +> 【提示:『工具』『建造』节点,即将因自主行为解锁。】 + +「工具」「建造」这些灰锁,怕是要被这群泥偶自己撬开了。 + +林默忽然很想给自己倒杯酒——前世加班庆功才有的那种。如今庆的是,他养的文明,自己学会了生火、做碗。 + +那一夜,众人围着自生的火,第一次没有立刻睡,而是坐着,看火,偶尔相视一笑。林默隔着虚空看着,觉得那算得上宇森历史上第一个「节日」——没有庆典,没有歌舞,只是一群人,因为不再怕黑,而舍得坐着发会儿呆。 + +园丁最高的成就,原是「被文明不再需要」。林默咂摸着这句,竟有点骄傲的空落。 + +取火之后,发展度跳到 1.5%,【工具:萌芽|火种:自主】。岩成了名副其实的火师,语言加取火,双料权威,没人不服。林默回想那七个昼夜里,自己手指蜷起又松开的好几回——有两次火将熄、岩冻得发抖,他差点直接落火,到底咬牙忍了。如今看岩守着火的得意样,他觉得那几回忍住,值。 + +那一夜,众人围着自生的火,第一次没立刻睡。他们坐着,看火,偶尔相视一笑。林默隔着虚空看着,觉得那算得上宇森历史上第一个「节日」——没有庆典,没有歌舞,只是一群人,因为不再怕黑,而舍得坐着发会儿呆。 + +林默翻了翻自己的权限面板:还是穷神,世界锚点稳定性倒是涨到了 12%。他忽然觉得,心里比兜里富。 + +岩成了火师,风成了采集体,木管棚,石护芽——创世原第一次有了清晰的「分工」。林默看着这张无形的名单,忽然觉得,文明最可怕的从来不是弱,是「各司其职」后迸出的合力。二十几个泥偶,单拎哪个都不算啥,凑成这张网,就能在灰白里扎下根。 + +他想起前世学过的一句话:个体聪明没用,系统聪明才要命。如今他亲手养出的,正是这么个会自己转的系统。 + +取火那晚岩烤出的硬壳,第二天被它当碗使,装了烤碎屑,分给芽。林默瞅着那粗糙的「碗」,笑了——昨天还是啃石头的泥偶,今天就讲究上餐具了。文明的矫情,也是进步的标志。 + +「行,」他对灰白说,「你们自己转,我边上看着。」 + +钩子:取火后的第三个昼夜,岩带着风,第一次在夜里主动走出了光晕,去灰白外采结晶。从前他们怕黑、怕外,如今火在手里,外,成了可以去的地方。林默看着那两道提着自生火苗的影子,没忍心提醒:灰白外,除了结晶,还悬着那颗休眠的第二枚种子。而岩火苗腾起的烟,在灰白里留下一道极淡的痕——宇森历史上第一道「人类活动痕迹」。虚空里的眼睛,盯着那道痕,看了很久。 + +--- + +## 第8章 · 第一座棚 + +火会自己生,下一个问题就摆在眼前:人往哪住。 + +灰白没有天,没有地,只有均匀的光晕。白天还好,夜里寒意照样渗,且自从取了火,人反倒更惜命——吃饱了,才顾得上喊冷。石最先受不了:它把芽往火堆最里圈推,自己在外圈挡风,冻得直抖,一晚上醒七八回。 + +林默看不下去,又泄了丝引导意志,把「挡」的念头,往木(那个手巧的)心里点了一下。不是替它盖房,只是让它「想到」挡风这回事。 + +木琢磨了半天,先拖来几根枯枝,斜斜插进灰白基质,想立个屏障——风一吹,倒。它又掰碎屑糊缝,糊上是糊上了,手一松也塌。试到第三回,它把枯枝交叉插成三角,再压上碎屑,竟稳住了半边。 + +木没成,先丧。它蹲在塌掉的枝架边,耷拉着脑袋。林默隔着虚空,手指都蜷起来了——他想直接落一缕意志,把「三角最稳」点透。可他忍住了。上回取火他忍住了,这回也得忍。摔过的跟头,才长得出真本事。 + +石见木丧,走过去,没说话,捡起一根倒下的枯枝,往土里插了插,又拍拍木的肩。木抬头,看了石一眼,忽然又抓起碎屑,重新垒。 + +这一回,它让石帮忙扶着,自己慢慢压、慢慢糊。石在外圈挡着风,它在内圈垒。两人凑凑合合,垒出个歪歪的半圆壳,把芽和几个弱的圈进去。 + +第一个「棚」。 + +丑得不行,漏风,可真能挡住一半寒意。木钻进去,又钻出来,绕着棚转了半圈,憨憨地笑。林默看着,想起前世自己租的第一间小破房——墙皮掉、窗户漏风,可那是「家」,是他离开父母后第一个属于自己的角落。如今这丑棚,是宇森历史上第一个「家」。 + +> 【文明自主行为 · 建筑雏形】 +> 【评价:聚落奠基。锚点稳定性 +3。】 +> 【能量 +0。】 + +小爽点在踏实。从前这群泥偶是「一群挤着的影子」,现在是「一个围着火的聚落」。一字之差,天壤之别。 + +棚一立,格局就出来了:火塘居中,棚环绕。创世原第一次有了「中心」和「边界」。岩守火,木管棚,石护芽,风跑外——各就各位。林默翻发展度,【聚合:萌芽→聚落】。 + +芽半夜在棚里哭,石没醒透,本能地搂紧,哼起一个没调的音节,大概是「姬」的变调,像在哄。林默听着那不成调的哼,忽然鼻酸——这声哼,比任何系统提示都像「家」。 + +夜深,众人都缩进棚里睡了。岩独自守火,往里添枯枝。林默习惯性把感知落过去,却忽觉一缕极微弱的、温热的「东西」,从岩守火的心里,悄悄回流进锚点——不是能量,更像「意志的余温」。 + +他愣了下,翻系统注解:文明自主意志,可微弱反哺锚点稳定,亦是未来「信仰能量」的雏形。 + +也就是说,他不用干等文明「信仰」他,文明「认真活着」本身,就在一点点喂饱这方世界。 + +林默忽然很踏实。穷神开局,原来不是绝路——只要文明肯认真活,能量会自己长出来。 + +木建棚那几回失败,林默看得手心冒汗。有一回木蹲在塌架边耷拉脑袋,他手指都蜷起来了,想直接把「三角最稳」点透,又硬生生松开。上回取火他忍了,这回也得忍。摔过的跟头,才长得出真本事。 + +棚成那晚,众人挤在歪棚里睡,芽缩在石怀里,石哼着没调的「姬」哄它。林默听着那声哼,忽然鼻酸——这声哼,比任何系统提示都像「家」。神自己不需要家,可他替这方天地,高兴得不行。 + +虚空里那双眼睛,比以往任何一次都近。林默感到它在「评估」:火、棚、守夜、记号——像在给这个实验逐项打分。它越近,林默越笃定:这双眼睛,不是来毁宇森的,是来「看」宇森怎么长的。 + +钩子:也就在那一瞬,虚空里那双眼睛,比以往任何一次都近。林默清晰感到——「它」在打量这片有了棚、有了火、有了守夜人的创世原,像评估一个实验,到了第几步。而更让林默心头一跳的是,守火的岩,忽然用一块烧黑的碎屑,在棚边的基质上,划了一道歪歪的痕——像在记什么。 + +系统悄然记下一行:【文明行为:标记/记录 · 萌芽。『文字』节点将启。】 + +那是宇森历史上,第一道「人为的记号」。林默辨认不出意思,可他注意到,虚空里那双眼睛,盯着的不是火、不是棚,正是那道痕。仿佛记号,才是它真正在等的。 + +--- + +> 卷一待续 · 下章预告:第一缕信仰,与虚空里那双越来越近的手。 diff --git a/rules.html b/rules.html new file mode 100644 index 0000000..04eb7bb --- /dev/null +++ b/rules.html @@ -0,0 +1,160 @@ + + + + + +规则 · 宇森 Yusen + + + + + + +
    +
    + + +
    +
    +
    + +
    +
    + +
    +
    +
    + + + + + + diff --git a/story.html b/story.html new file mode 100644 index 0000000..5a5553d --- /dev/null +++ b/story.html @@ -0,0 +1,623 @@ + + + + + +剧情 · 宇森 Yusen + + + + + + +
    +
    + + +
    +
    +
    + +
    +
    + + + + +
    +
    +
    宇森创世正史
    +

    欢迎阅读

    +
    + 📄 + + 📚 — +
    +
    +
    +
    + 加载剧情… +
    +
    +
    + + +
    +
    +
    + + + +
    +
    +
    + + + + + + diff --git a/wc1.js b/wc1.js index e910228..cbc42f2 100644 --- a/wc1.js +++ b/wc1.js @@ -1135,14 +1135,14 @@ function drawTimeAmbiance(pal) { // ============ 随机过场插画(飞入过渡背景用) ============ const TRANSITION_IMAGES = [ - 'assets/art/transitions/transition_dungeon.png', - 'assets/art/transitions/transition_adventure.png', - 'assets/art/transitions/transition_sky.png', - 'assets/art/transitions/transition_forest.png', - 'assets/art/transitions/transition_night.png', - 'assets/art/transitions/transition_dawn.png', - 'assets/art/transitions/transition_coast.png', - 'assets/art/transitions/transition_skycity.png', + 'assets/art/transitions/transition_dungeon.webp', + 'assets/art/transitions/transition_adventure.webp', + 'assets/art/transitions/transition_sky.webp', + 'assets/art/transitions/transition_forest.webp', + 'assets/art/transitions/transition_night.webp', + 'assets/art/transitions/transition_dawn.webp', + 'assets/art/transitions/transition_coast.webp', + 'assets/art/transitions/transition_skycity.webp', ]; let _lastTransitionIdx = -1; // 避免连续两次同一张 function pickTransitionImage() { diff --git a/website/intro.html b/website/intro.html index 1d8e374..0c231c7 100644 --- a/website/intro.html +++ b/website/intro.html @@ -89,6 +89,158 @@ p { color:#99aabb; font-size:15px; margin-bottom:14px; line-height:1.9; } } .panel-placeholder .icon { font-size:48px; margin-bottom:16px; } +/* ===== 剧情页 ===== */ +.story-section { margin-bottom:54px; } +.story-section > h2:first-child { margin-top:0; } + +.story-note { + background:rgba(10,20,40,.45); border:1px solid rgba(100,200,255,.12); + border-radius:12px; padding:18px 22px; color:#9fb0c2; font-size:14px; + line-height:1.95; margin-bottom:26px; +} +.story-note strong { color:#cfe6ff; } + +/* 卷列表 */ +.volume-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; } +.volume-card { + background:rgba(10,20,40,.5); border:1px solid rgba(100,200,255,.1); + border-radius:12px; padding:20px 22px; + transition:transform .25s,border-color .25s; +} +.volume-card:hover { transform:translateY(-3px); border-color:rgba(100,200,255,.32); } +.volume-card .vol-no { font-size:12px; color:#4af; letter-spacing:1px; } +.volume-card h3 { font-size:16px; color:#eaf2ff; margin:4px 0 12px; border:none; padding:0; } +.vol-meta { font-size:13px; color:#9fb0c2; margin-bottom:7px; line-height:1.65; } +.vol-meta b { color:#a8e6cf; font-weight:600; } +.vol-climax { + margin-top:11px; padding-top:11px; border-top:1px dashed rgba(100,200,255,.16); + font-size:13px; color:#c7d4e2; line-height:1.7; +} +.vol-climax b { color:#a8e6cf; font-weight:600; } + +/* 章节正文 */ +.chapter { + background:rgba(10,20,40,.26); border:1px solid rgba(100,200,255,.08); + border-radius:12px; padding:6px 28px 22px; margin-bottom:22px; +} +.chapter h3 { margin-top:26px; } +.chapter p { color:#aebccd; } + +/* 系统面板(游戏内 UI 风) */ +.sys-panel { + background:rgba(4,12,24,.72); border-left:3px solid #4af; border-radius:0 8px 8px 0; + padding:12px 16px; margin:16px 0; color:#8fd0ff; + font-size:13px; line-height:1.85; font-family:'Consolas','SF Mono',Menlo,monospace; + white-space:pre-line; +} + +/* 上帝模拟器 · 状态面板(高级感游戏 UI) */ +.god-panel{ + position:relative; + margin:22px 0; + padding:0; + background: + linear-gradient(135deg, rgba(74,170,255,.08) 0%, rgba(168,85,247,.08) 100%), + rgba(8,14,30,.92); + border:1px solid rgba(74,170,255,.25); + border-radius:10px; + font-family:'Consolas','SF Mono',Menlo,monospace; + color:#cfd8e8; + overflow:hidden; + box-shadow:0 4px 24px rgba(74,170,255,.12), inset 0 1px 0 rgba(255,255,255,.04); +} +.god-panel::before{ + content:'';position:absolute;top:0;left:0;right:0;height:1px; + background:linear-gradient(90deg, transparent, rgba(74,170,255,.6), rgba(168,85,247,.6), transparent); +} +.god-panel-head{ + display:flex;align-items:center;justify-content:space-between; + padding:12px 16px; + background:linear-gradient(90deg, rgba(74,170,255,.12), transparent); + border-bottom:1px solid rgba(74,170,255,.18); +} +.god-panel-title{ + display:flex;align-items:center;gap:8px; + font-size:13px;font-weight:700;color:#fff;letter-spacing:2px; +} +.god-panel-title .gp-icon{ + display:inline-flex;width:22px;height:22px;align-items:center;justify-content:center; + border-radius:6px;background:linear-gradient(135deg,#4af,#a8f);font-size:12px; +} +.god-panel-tag{ + font-size:10px;color:#4af;letter-spacing:1.5px; + padding:3px 9px;border:1px solid rgba(74,170,255,.4);border-radius:99px; + background:rgba(74,170,255,.08); +} +.god-panel-body{padding:14px 16px 16px;} +.god-panel-row{ + display:flex;align-items:center;gap:10px; + padding:6px 0;font-size:13px; +} +.god-panel-row + .god-panel-row{border-top:1px dashed rgba(74,170,255,.12);} +.god-panel-label{ + flex:0 0 56px;color:#7b8ea8;font-size:11px;letter-spacing:1px; +} +.god-panel-value{flex:1;color:#e6f0fb;} +.god-panel-chips{display:flex;flex-wrap:wrap;gap:6px;} +.god-chip{ + display:inline-flex;align-items:center;gap:4px; + padding:3px 10px;font-size:11px;border-radius:5px; + background:rgba(74,170,255,.1);border:1px solid rgba(74,170,255,.25);color:#8fd0ff; + letter-spacing:.5px; +} +.god-chip.gold{background:rgba(231,184,92,.1);border-color:rgba(231,184,92,.3);color:#fbbf24;} +.god-chip.purple{background:rgba(168,85,247,.12);border-color:rgba(168,85,247,.3);color:#c084fc;} +.god-chip.green{background:rgba(34,197,94,.1);border-color:rgba(34,197,94,.3);color:#4ade80;} +.god-chip.pink{background:rgba(236,72,153,.1);border-color:rgba(236,72,153,.3);color:#f472b6;} +.god-energy{display:flex;align-items:center;gap:10px;flex:1;} +.god-energy-bar{ + flex:1;height:8px;border-radius:4px;background:rgba(255,255,255,.06); + overflow:hidden;position:relative; +} +.god-energy-fill{ + height:100%;width:0%; + background:linear-gradient(90deg, #4af, #a8f); + box-shadow:0 0 8px rgba(74,170,255,.5); + animation:energy-pulse 2s ease-in-out infinite; +} +@keyframes energy-pulse{ + 0%,100%{opacity:.85;} + 50%{opacity:1;} +} +.god-energy-num{ + flex:0 0 auto;font-family:inherit;font-size:13px;font-weight:700; + color:#fbbf24;min-width:32px;text-align:right; +} +.god-panel-foot{ + display:flex;align-items:center;justify-content:space-between; + padding:8px 16px;border-top:1px solid rgba(74,170,255,.12); + font-size:10px;color:#7b8ea8;letter-spacing:1px; + background:rgba(0,0,0,.18); +} +.god-panel-foot .gp-id{color:#4af;font-family:inherit;letter-spacing:2px;} +/* 叙事引言 */ +.story-quote { + border-left:3px solid rgba(168,230,207,.5); padding:10px 16px; margin:16px 0; + color:#9fb0c2; font-style:italic; font-size:14px; line-height:1.8; +} + +/* 列表 & 表格 */ +.story-list { margin:12px 0 12px 22px; } +.story-list li { color:#9fb0c2; font-size:14px; margin-bottom:9px; line-height:1.7; } +.story-list li b { color:#a8e6cf; } +.story-table { width:100%; border-collapse:collapse; margin:18px 0 4px; font-size:13px; } +.story-table th, .story-table td { + border:1px solid rgba(100,200,255,.14); padding:9px 13px; text-align:left; color:#aebccd; +} +.story-table th { background:rgba(74,175,255,.1); color:#cfe6ff; font-weight:600; } +.story-table tr:hover td { background:rgba(74,175,255,.05); } + +@media (max-width:768px) { + .volume-grid { grid-template-columns:1fr; } + .chapter { padding:6px 16px 20px; } +} + /* ===== 响应式 ===== */ @media (max-width:768px) { nav { padding:0 16px; flex-wrap:wrap; height:auto; gap:4px; padding-top:8px;padding-bottom:8px; } @@ -267,10 +419,233 @@ p { color:#99aabb; font-size:15px; margin-bottom:14px; line-height:1.9; }

    剧 情

    世界观主线与支线叙事

    -
    -
    📖
    -

    剧情内容正在筹备中…

    -

    敬请期待

    + +
    +

    ✦ 卷一 · 创世之始

    +
    从能量为 0 的穷神,到点亮第一簇火、养出第一个聚落。这是宇森宇宙的起点。
    + +
    +

    序章 · 最后一班,与最初的一班

    +

    林默是在凌晨三点十七分死去的。

    +

    死因没什么戏剧性——连续第四十一个小时对着显示器,心脏先一步下了班。最后映进眼底的,是屏幕上那行还没保存的方案文档,和右下角弹出的「内存不足」。

    +

    黑暗来得干脆。

    +

    再睁眼时,没有病房,没有急救的蓝光,只有一片望不到尽头的灰白。

    +

    不是雾,不是墙,是某种更本质的「空」。脚下没有实地,周身没有空气,可他偏偏稳稳地「站」着,像被某种规则托住。视野正中央,半透明面板无声浮起:

    +
    上帝模拟器 已绑定 +身份: 世界管理者 +权限: 创造一切元素 (物质 / 生命 / 法则 / 文明) +当前能量: 0
    +

    一行冰冷的提示之后,林默才慢慢嚼出自己成了什么——不是这个世界里卑微的居民,而是它的造物主。山可以凭空隆起,海能够随手倾泻,连「重力」与「时间」这种底层规则,都能被他像拨动开关一样改写。

    +

    可系统很快补了一刀,把这份狂喜砸回地面:

    +
    警告: 能量源自文明的发展与信仰. 没有生灵, 您连点亮第一缕火光都做不到.
    +

    也就是说,他空握一身神权,却穷得连颗石头都搓不出来。

    +

    就在他盯着「能量:0」发愣时,面板角落弹出一条闪烁的红字:

    +
    检测到初始文明种子 · 已坠落至世界边缘. 是否投放?
    +

    林默抬起手。指尖穿过灰白,点下那个「是」。

    +

    「投放。」

    +

    下一秒,一簇微光砸进无垠的虚空。

    +

    他知道,自己这趟「上帝」的差事,从这一刻,才算真正开场。

    +
    + +
    +

    第1章 · 灰白沙盘

    +

    能量为零的世界,比林默想象的更荒凉。

    +

    他试着「造一座山」。面板闪了一下,弹出一行小字:创造失败: 能量不足. 本次所需最低能量 1, 当前 0.

    +

    他试着「点一团火」。同样的结果。

    +

    他甚至试着凭意念搓出一颗石子——灰白的空间纹丝不动,连粒尘埃都没飘起来。

    +

    「所以这就是当上帝的体面。」林默对着空无一物的虚空自嘲,「连个响都听不着。」

    +

    可他很快发现,这方灰白并非死寂。当他凝神去「看」,视野里浮起一层极淡的网格,从脚下向四周无限延展,像一张等待落子的棋盘。网格的尽头,那簇被他投放的文明种子,正悬在一道微光里,缓慢旋转。

    +
    文明种子 · 状态: 休眠中 +内含: 初始生命蓝图 ×1, 文明火种 ×1 +激活条件: 管理者注入首批引导意志
    +

    「引导意志?」林默皱眉。系统给了段简释:他不必事必躬亲地「手搓」万物,只需在世界的关键节点施加一次影响——一次抉择、一句低语、一道微光——文明便会顺着自己的逻辑自行生长。这恰好是「园丁」二字的真意:播种,引导,然后退后,看它活。

    +

    「总算有个能用的词。」他松了口气。当神当到要乞讨一句话的权限,这差事未免太寒酸,但至少,路是通的。

    +

    他飘向那簇种子。微光触手的刹那,一段信息涌入脑海:这是一簇最原始的「人」——没有种族、没有天赋、没有记忆,只是一团等待被点亮的意识火苗。他们将从零开始,在灰白里摸索火、食物、聚居,用一代代试错,把荒原种成文明。

    +

    而他的任务,是在他们每一次濒临熄灭时,递上那根关键的柴。

    +
    是否激活文明种子? +提示: 激活后将消耗您当前全部能量(0/0), 并开启首个世界锚点.
    +

    林默笑了:「反正兜里比脸还干净,点。」

    +
    激活成功. 世界锚点已生成: 未命名.
    +

    脚下的灰白轻轻一颤,像沉睡的湖被投入第一颗石子。远处那簇微光,开始向下坠去,拖出一道细小的光尾——

    +

    它要落了。

    +
    + +
    +

    第2章 · 坠星

    +

    光落下的地方,后来被那些活着的人称作「创世原」。

    +

    林默以意念拉近视角,灰白棋盘的中央,一团微弱的光晕正缓缓铺开。光晕里,二十余个模糊的身影次第浮现——他们是种子里苏醒的第一代「人」,赤条条、懵懂懂,像刚被吹进世界的泥偶。

    +

    他们没有名字,没有语言,只有最原始的本能:冷了要靠近,饿了要找,怕了要聚。

    +

    第一个夜晚来得毫无预兆。灰白的天幕暗下去,寒意顺着某种林默尚不理解的规则渗入光晕。那二十几道身影开始发抖,彼此挤拢,却谁也不知道该如何抵御。

    +
    事件触发: 首夜严寒 +文明存活率预测: 41% +建议引导: 于光晕边缘落一处火源
    +

    林默几乎是本能地伸手,朝那片虚空中按下一缕「意念」。他看不见自己做了什么,只觉心头微微一空——像吹灭一根蜡烛消耗的那点气。

    +

    下一瞬,光晕边缘腾起一簇豆大的火。

    +

    那二十几道身影猛地转头。他们不认识火,却认得暖。一个最胆大的影子试探着伸出手,被热浪一烫,又缩回,继而再次凑近。很快,他们围成了圈,瑟缩的肩膀松开了。

    +
    引导成功: 火源已落 +文明存活率预测: 78% +能量 +0(文明尚处萌芽, 暂未产生信仰)
    +

    林默盯着那行「能量 +0」,忽然懂了系统的残酷:他刚才那一下,是白送的。没有回报,没有反馈,只有一群还不会感恩的泥偶围着火发抖。

    +

    可他没来由地笑了。

    +

    因为就在火光摇曳里,他看清了——其中一个影子,正笨拙地用两根枯枝去拨弄火堆,想让它更旺些。那动作毫无意义,却是一道嫩芽:这团意识火苗,开始自己「想办法」了。

    +
    检测到: 文明自主行为 · 首次 +评价: 萌芽. 世界锚点稳定性 +1.
    +

    灰白棋盘上,那一点光晕,比刚才亮了半分。

    +

    林默收回目光,望向更远的虚空。那里,还有无数片未点亮的灰白,等着被落子。而他兜里,依然只有那串刺眼的「能量:0」。

    +

    但至少在创世原,有了一簇火,和二十几个开始想办法活下去的人。

    +

    「这才像点样子。」他轻声说。

    +

    远处,虚空深处,似有极淡的涟漪,一闪即逝。

    +
    +
    + + +
    +

    ✦ 十卷剧情大纲

    +
    + 定位说明:本小说是游戏《宇森 (YuSen)》的「创世正史」。游戏里玩家扮演「园丁」(播种、引导、关键时刻施力),本小说讲第一个世界管理者(神)如何从能量为 0 养出整个宇森宇宙。游戏的十五大系统(种族 / 性格 / 天赋 / 职业 / 建造 / 交易 / 战争 / 势力 / 装备 / 神器 / 传奇事件 / 五层世界…)在小说里都是主角逐步解锁的「创造权限」。

    + 主角:林默(现实世界加班猝死 → 绑定上帝模拟器→ 成为「世界管理者」)。
    + 核心金手指:上帝模拟器——创造一切元素(物质 / 生命 / 法则 / 文明),能量来自文明的发展与信仰。
    + 结构:10 卷 × 每卷约 100 章 ≈ 1000 章 / 250 万字。每卷一个「神格成长阶段 + 一层世界切面 + 一组系统解锁」,独立成弧又串成主线。 +
    + +
    +
    +
    VOL.01
    +

    卷一 · 创世之始(凡人成神)

    +
    成长阶段:从「搓不出一颗石头」的穷神 → 点亮第一簇火、养出第一个部落。
    +
    世界切面:大地层(地表原点)。
    +
    系统解锁:基础创造(地形 / 物质)、文明种子投放、能量采集。
    +
    核心矛盾:能量为 0,空有神权却啥也造不出;首批文明种子在灰白荒原上艰难求生,随时灭绝。
    +
    爽点:第 1 章穷神开局 → 第 30 章第一次用「引导」而非「强控」救活部落 → 第 80 章部落升格为聚落,能量首次破千,解锁「性格」雏形。
    +
    卷终高潮:聚落推举首位首领,向天空祈祷「赐名」。林默以意念落笔,将这个世界命名为 「宇森」——游戏宇宙的起点就此锚定。
    +
    + +
    +
    VOL.02
    +

    卷二 · 大地文明(城邦崛起)

    +
    成长阶段:从聚落管理者 → 多城邦的「文明引导者」。
    +
    世界切面:大地层(城镇 / 农田 / 道路铺开)。
    +
    系统解锁:性格、天赋、职业、建造(农场 / 兵营 / 神殿 / 学院 / 集市)。
    +
    核心矛盾:文明内部分裂为「信神派」与「唯物派」;邻邦因资源开战,首次出现「信仰流失导致能量暴跌」的危机。
    +
    爽点:第 40 章用一次「神迹」(圣光普照)硬控战局 → 第 70 章以「贸易」系统让敌对方变盟友,能量反超。
    +
    卷终高潮:统一大陆的「宇森第一帝国」加冕,林默解锁「势力」权限,第一次以「规则」而非「手搓」维持秩序——园丁理念的雏形成型。
    +
    + +
    +
    VOL.03
    +

    卷三 · 地下秘藏(矿脉与遗迹)

    +
    成长阶段:从地表神 → 能操控地下生态的「深层管理者」。
    +
    世界切面:地下层(矿脉 / 遗迹)。
    +
    系统解锁:神器(创世之核 / 轮回镜 / 星陨剑…)、装备、锻造。
    +
    核心矛盾:文明向下挖矿惊动「地心遗迹守卫」;虫族自地底裂隙涌出,地表文明面临灭顶。
    +
    爽点:第 35 章主角俯冲地心,徒手捏出「磐石甲」护住矿工 → 第 60 章夺得「创世之核」嵌入文明核心,世界等级首次跃迁。
    +
    卷终高潮:以神器之力封死地底裂隙,虫族退去。林默领悟——神器是「前一个世界管理者」留下的遗产,他不是第一个神。
    +
    + +
    +
    VOL.04
    +

    卷四 · 百族之争(种族林立)

    +
    成长阶段:从单一文明 → 多种族宇宙的「调和者」。
    +
    世界切面:大地 + 地下贯通。
    +
    系统解锁:种族全开(龙裔 / 兽人 / 精灵 / 矮人 / 机械 / 虚灵 / 海族 / 天使 / 虫族 / 巨人 / 猫兽 / 树人)、法则(秩序 / 混沌)雏形。
    +
    核心矛盾:十二种族因禀赋差异爆发「百族大战」;高等种族蔑视主角「人造神」出身。
    +
    爽点:第 50 章以「秩序法则」强行冻结战场 → 第 85 章用「混沌法则」化解虚空渗透,威望碾压老牌神裔。
    +
    卷终高潮:百族签订《宇森协和约》,林默获封「万族之园丁」,解锁「法则创造」高阶权限。
    +
    + +
    +
    VOL.05
    +

    卷五 · 天空之主(气候与航行)

    +
    成长阶段:从「天地」单控 → 掌握气象与远航的「天空之主」。
    +
    世界切面:天空层(季风 / 气候 / 航行)。
    +
    系统解锁:天象类传奇事件(双月同辉 / 圣光普照)、航海、气候调控。
    +
    核心矛盾:大陆资源触顶,文明必须跨海拓殖;天空异常信号首次扰动季风,引发大饥荒。
    +
    爽点:第 45 章拨动季风救活濒死作物,民众跪拜「气候之神」 → 第 75 章操控「双月同辉」开启远洋航线,新大陆殖民成功。
    +
    卷终高潮:星空层探出第一道清晰信号——「你并非孤神」。林默第一次意识到宇宙里还有「别的玩家」。
    +
    + +
    +
    VOL.06
    +

    卷六 · 星空低语(异界信号)

    +
    成长阶段:从单体世界神 → 接触「万界网络」的觉醒者。
    +
    世界切面:星空层开启。
    +
    系统解锁:世界等级系统、星空信号解析、灭世级灾变事件。
    +
    核心矛盾:星空信号源自「远古观测者」,抛出诱饵想吞并宇森;同时宇森遭遇首次「终焉级」灾变(星陨 / 虚空侵蚀)。
    +
    爽点:第 40 章识破诱饵,反向溯源锁定观测者坐标 → 第 70 章以「世界等级跃迁」硬抗灾变,文明零灭绝。
    +
    卷终高潮:林默第一次与其他「世界管理者」建立通讯——宇宙是无数个「宇森」般的沙盒,彼此独立又暗中博弈。
    +
    + +
    +
    VOL.07
    +

    卷七 · 世界之跃(多界连通)

    +
    成长阶段:从单界神 → 多界贸易 / 战争的「仲裁者」。
    +
    世界切面:多世界平面连通(呼应游戏「独立世界 + 异步互动」)。
    +
    系统解锁:跨世界贸易 / 战争 / 联盟、异步策略博弈。
    +
    核心矛盾:高阶「吞噬型」管理者想并吞新手小界;林默被迫下场护盘,与老牌神正面对弈。
    +
    爽点:第 35 章用「经济战」拖垮对手贸易线,不费一兵 → 第 80 章以「联盟网」反包围吞噬者,当众打脸「神也要抱团」。
    +
    卷终高潮:宇森成为万界公认的「中立枢纽」,林默获「仲裁者」神衔,身后开始聚集一批后世「园丁」学徒。
    +
    + +
    +
    VOL.08
    +

    卷八 · 法则之战(创世权柄)

    +
    成长阶段:从「用法则」 → 「自创法则」的权柄级神。
    +
    世界切面:法则层(时 / 空 / 因果)。
    +
    系统解锁:专属法则创造(时 / 空 / 因果)、因果链干预。
    +
    核心矛盾:万界议会中保守派禁止「自创法则」,惧怕失控;暗中「虚空吞噬者」集结,欲吞掉整个宇森星区。
    +
    爽点:第 50 章当众创出「时间回溯」法则,救回被抹除的友界 → 第 90 章以「因果律反噬」让吞噬者自食其果。
    +
    卷终高潮:林默以自创法则镇杀吞噬者首领,名动万界,被记入「创世者名录」。
    +
    + +
    +
    VOL.09
    +

    卷九 · 万界共主(文明联邦)

    +
    成长阶段:从「仲裁者」 → 「万界共主」,却陷入「管得越多越孤独」的权力悖论。
    +
    世界切面:万界议会 / 文明联邦。
    +
    系统解锁:终焉系权限、文明联邦治理、信仰总池。
    +
    核心矛盾:联邦内权斗不休;更大的威胁「终焉之潮」逼近——所有小世界的能量正被某个更上层存在悄悄抽干。
    +
    爽点:第 45 章以「信仰总池」平抑内乱 → 第 85 章揭开终焉之潮真相:抽能者正是「上帝模拟器」本身。
    +
    卷终高潮:林默发现残酷真相——上帝模拟器是一台培育「超越者」的机器,管理者只是养料。他决定反控系统。
    +
    + +
    +
    VOL.10
    +

    卷十 · 永恒园丁(超越神座)

    +
    成长阶段:从「被系统豢养的神」 → 超越系统的「永恒园丁」。
    +
    世界切面:系统内核 / 宇森本源。
    +
    系统解锁:终极权限(超越模拟器)、世界模板固化。
    +
    核心矛盾:与上帝模拟器背后「园丁之上」的终极存在决战;代价是放弃永生神格。
    +
    爽点:第 50 章以「自创法则 + 万界信仰」击穿系统防火墙 → 第 90 章将宇森固化为「可继承的世界模板」,留予后世。
    +
    卷终高潮:林默散去神格,化作「永恒园丁」隐入幕后,把权柄打包成「园丁系统」分发到无数新世界——游戏里每一个玩家,都是他留下的继承者。「去探索吧,园丁。这个世界在等你。」
    +
    +
    + +

    ✦ 主线暗线(贯穿十卷)

    +
      +
    • 金手指的限制与进化:能量只能来自文明发展/信仰 → 后期的「信仰总池」→ 终极「超越模拟器」(对应游戏能量/世界等级/神器嵌套逻辑)。
    • +
    • 园丁理念的形成:卷一「手搓救火」→ 卷二「引导胜强控」→ 卷十「放权给继承者」,正好对应游戏「玩家是园丁而非操控者」的核心设计。
    • +
    • 五层世界的递进解锁:大地 → 地下 → 天空 → 星空 → 法则层,与游戏五层垂直世界(地心/地下/大地/天空/星空)一一映射。
    • +
    • 身份反转:凡人 → 神 → 被系统豢养的神 → 超越系统的园丁,完成「一个人的成长」闭环。
    • +
    + +

    ✦ 与游戏系统的映射表

    + + + + + + + + + + + + + + + + +
    小说权限游戏系统首次解锁卷
    基础创造地形 / 物质卷一
    性格 / 天赋 / 职业性格 / 天赋 / 职业卷二
    建造 / 贸易 / 势力建造 / 交易 / 战争 / 势力卷二~三
    神器 / 装备神器 / 装备卷三
    种族 / 法则(秩序混沌)种族 / 系别卷四
    天象 / 航海传奇事件(天象) / 航海卷五
    世界等级 / 星空世界等级 / 五层卷六
    跨界互动独立世界 + 异步互动卷七
    专属法则(时空调)终焉 / 秘辛事件卷八~九
    超越权限 / 模板园丁系统本体卷十
    diff --git a/website/login.html b/website/login.html index d4b84dc..ca9c18c 100644 --- a/website/login.html +++ b/website/login.html @@ -4,7 +4,7 @@ 宇森 · 登录 - + +
    +
    宇森
    @@ -189,5 +193,54 @@ } }); + diff --git a/world.html b/world.html index e813d30..399940d 100644 --- a/world.html +++ b/world.html @@ -1087,11 +1087,11 @@ const LAYER_BG = { // 大地、天空按四个时间节点各配一段视频;星空、地下、地心各一段、不随时间变 // 每个世界固定一段背景视频;时间线只改色调(currentPalette.tint 叠加),不切换视频 const WORLDS = [ - { id:'star', name:'星空', icon:'✦', image:'assets/bg/bg_star.png' }, - { id:'sky', name:'天空', icon:'🌤', image:'assets/bg/bg_sky.png' }, - { id:'earth', name:'大地', icon:'🌍', image:'assets/bg/bg_earth.png', sandbox:true }, - { id:'under', name:'地下', icon:'🕳', image:'assets/bg/bg_under.png' }, - { id:'core', name:'地心', icon:'🔥', image:'assets/bg/bg_core.png' }, + { id:'star', name:'星空', icon:'✦', image:'assets/bg/bg_star.webp' }, + { id:'sky', name:'天空', icon:'🌤', image:'assets/bg/bg_sky.webp' }, + { id:'earth', name:'大地', icon:'🌍', image:'assets/bg/bg_earth.webp', sandbox:true }, + { id:'under', name:'地下', icon:'🕳', image:'assets/bg/bg_under.webp' }, + { id:'core', name:'地心', icon:'🔥', image:'assets/bg/bg_core.webp' }, ]; @@ -2276,14 +2276,14 @@ function drawTimeAmbiance(pal) { // ============ 随机过场插画(飞入过渡背景用) ============ const TRANSITION_IMAGES = [ - 'assets/art/transitions/transition_dungeon.png', - 'assets/art/transitions/transition_adventure.png', - 'assets/art/transitions/transition_sky.png', - 'assets/art/transitions/transition_forest.png', - 'assets/art/transitions/transition_night.png', - 'assets/art/transitions/transition_dawn.png', - 'assets/art/transitions/transition_coast.png', - 'assets/art/transitions/transition_skycity.png', + 'assets/art/transitions/transition_dungeon.webp', + 'assets/art/transitions/transition_adventure.webp', + 'assets/art/transitions/transition_sky.webp', + 'assets/art/transitions/transition_forest.webp', + 'assets/art/transitions/transition_night.webp', + 'assets/art/transitions/transition_dawn.webp', + 'assets/art/transitions/transition_coast.webp', + 'assets/art/transitions/transition_skycity.webp', ]; let _lastTransitionIdx = -1; // 避免连续两次同一张 function pickTransitionImage() { diff --git a/worlddata.html b/worlddata.html new file mode 100644 index 0000000..97c8756 --- /dev/null +++ b/worlddata.html @@ -0,0 +1,188 @@ + + + + + +世界数据 · 宇森 Yusen + + + + + + +
    +
    + + +
    +
    +
    + +
    +
    + +
    +
    +
    + + + + + +