ReMode').value : modeSel;
var r = G.reincarnate(forms[sel].id, mode);
if (r.end) {
modal('归墟', '
' + esc(r.txt) + '
这一局到此为止。世界会继续转,只是里面没有你了。
',
[{ label: '重开一局', cls: 'btn-gold', act: function () { backToStart(); } }]);
return;
}
var html2 = '' + esc(r.txt) + '
' +
'新的名字:' + esc(r.newName) + ' 形态:' + esc(r.form.name) + ' 方式:' + esc({ suhui: '宿慧', curse: '诅咒', karma: '因果继承', random: '随机新生' }[mode]) + '
' +
(r.inherited.length ? '继承的伏笔(权重 30%,优先级已提升):
' + r.inherited.map(function (k) { return '· ' + esc(k.text) + '(' + esc(k.level) + ')'; }).join('
') + '
' : '没有继承任何伏笔。
') +
(r.crossLinks.length ? '跨档因果:
' + r.crossLinks.map(function (c) { return '· ' + esc(c.txt); }).join('
') + '
' : '') +
'世界简报:' + esc(r.brief) + '
' +
'你不在的这些年,世界照常运转。有人老了,有人死了,有些约定期限到了。
';
modal('转世·' + r.form.name, html2, [{ label: '继续这一世', cls: 'btn-gold' }]);
/* 叙事插入 */
var rnd = el('div', 'round');
rnd.appendChild(headRow('转世', '第 ' + G.turn + ' 回合'));
rnd.appendChild(el('div', 'sysbox box-death', '【死亡转化·' + esc(r.form.name) + '】' + esc(r.txt) + '
' +
'新名:' + esc(r.newName) + ' | 继承伏笔 ' + r.inherited.length + ' 条 | 跨档因果 ' + r.crossLinks.length + ' 条
'));
$('#storyWrap').appendChild(rnd);
renderAll(); renderChoices(G.makeOptions()); autoSave();
}
}]);
setTimeout(function () {
var list = $('#dfList');
if (!list) return;
list.querySelectorAll('.death-form').forEach(function (d) {
d.addEventListener('click', function () {
list.querySelectorAll('.death-form').forEach(function (x) { x.classList.remove('sel'); });
d.classList.add('sel'); sel = parseInt(d.getAttribute('data-i'), 10);
});
});
}, 0);
}
/* ============================================================
SECTION: montage 岁月如梭
============================================================ */
function doMontage() {
if (G.over) { toast('你已经死了', 'err'); return; }
var html = '时间是最公平的天道法则——你闭关百年,外界不会为你停止转动。
' +
'触发后将强制结算:① 全部存活人物的年龄、境界、势力地位、关系演变与生死判定 ② 大事件日历强制推进 ③ 全部未解伏笔的「剩余时间」到期检查 ④ 物价更新与世界简报。禁止「主角出关,外界时间停滞」。
' +
'' +
'';
modal('岁月如梭', html, [{ label: '取消', cls: 'btn-ghost' }, {
label: '让时间走', cls: 'btn-gold', act: function () {
var y = parseInt($('#selYears').value, 10);
var during = $('#selDuring').value;
var pc = G.pc;
var before = { age: pc.age, realm: pc.realmIdx, qi: pc.qi };
/* 期间收益 */
var gainMult = { '闭关修炼': 1.0, '长途赶路': 0.3, '养伤': 0.2, '经营坊市': 0.4, '打磨百艺': 0.5, '陪伴故人': 0.25 }[during] || 0.5;
var qiGain = Math.round(6 * pc.cultMult * (1 + (pc.six['灵性'] - 3) * 0.06) * (pc.worldQi || 1) * y * 8 * gainMult);
pc.qi += qiGain;
if (during === '养伤') {
pc.hp = pc.hpMax;
D.INJURY_TYPES.forEach(function (t) { if (pc.injury[t] > 0 && t !== '道基' && t !== '因果') pc.injury[t] = Math.max(0, pc.injury[t] - 1); });
}
if (during === '经营坊市') { var m = Math.round(y * (300 + Math.random() * 900)); pc.money += m; }
if (during === '打磨百艺') {
var aid = Object.keys(pc.arts)[0] || '炼丹术';
if (!pc.arts[aid]) pc.arts[aid] = { bp: 0, level: 0 };
G.addArtBp(aid, y * 6);
}
if (during === '陪伴故人') {
G.npcs.filter(function (n) { return n.alive && n.met; }).slice(0, 3).forEach(function (n) {
G.npcAttitude(n, '感恩', 12, '你陪了他一段时间');
n.bond = Math.min(pc.bondCap, (n.bond || 0) + 1); pc.bonds[n.id] = n.bond;
});
pc.pollution = Math.max(0, pc.pollution - 1);
pc.costLevel = Math.max(0, pc.costLevel - 1);
}
if (during === '闭关修炼') G.addBp(pc.daoBias ? '无' : '无', y * 1.2, '长期闭关');
/* 强制结算 */
var logs = G.passYears(y, during);
var settle = G._lastSettle || { npcChanges: [], deaths: [], foreshadow: [], brief: '' };
/* 突破 */
var br = null;
if (pc.qi >= pc.qiNeed) br = G.tryBreakthrough();
/* 寿元耗尽 */
if (pc.age >= pc.lifeMax) { G.die('寿元耗尽(' + during + '期间)'); }
var txt = N.montage(G, y, {
npcChanges: settle.npcChanges || [], deaths: settle.deaths || [],
foreshadow: logs.filter(function (l) { return l.indexOf('伏笔') >= 0 || l.indexOf('物价') >= 0; }),
brief: settle.brief || G.worldBrief()
});
curRound++;
var rnd = el('div', 'round');
rnd.appendChild(headRow('岁月如梭', during + '·' + y + ' 年'));
rnd.appendChild(el('div', 'story', txt));
var sum = el('div', 'sysbox');
sum.innerHTML = '【强制结算】
' +
'' +
'| 时间 | ' + before.age + ' 岁 → ' + pc.age + ' 岁(+' + y + ' 年) |
' +
'| 道蕴积累 | +' + n2s(qiGain) + '(' + during + '效率 ×' + gainMult + ') |
' +
'| 境界 | ' + esc(D.REALMS[before.realm].name) + ' → ' + esc(D.REALMS[pc.realmIdx].name) + ' |
' +
(br ? '| 突破 | ' + (br.ok ? '成功' : '失败') + ' ' + esc(br.txt) + ' |
' : '') +
'| 人物结算 | ' + (settle.npcChanges || []).length + ' 项变化,' + (settle.deaths || []).length + ' 位故去 |
' +
'| 回合推进 | +' + y + ' 回合 → 第 ' + G.turn + ' 回合 |
' +
'
';
rnd.appendChild(sum);
if (logs.length) rnd.appendChild(el('div', 'sysbox', '【期间大事】
' +
logs.map(function (l) { return '· ' + esc(l) + '
'; }).join('')));
/* 期间的大事件 → 世界级叙事 */
(settle.bigEvents || []).forEach(function (e) {
rnd.appendChild(el('div', 'sysbox box-world', N.worldNarrative(G, e)));
});
/* 死亡叙事不可跳过 */
(settle.deaths || []).forEach(function (d) {
rnd.appendChild(el('div', 'sysbox box-death', '' + esc(d) + '
'));
});
/* 期间风闻(禁止闭关期间外界完全无消息) */
if (y >= 3) {
var rm = N.makeRumor(G); G.rumors.unshift(rm);
rnd.appendChild(el('div', 'sysbox', '期间有人来访/传讯,带来一条消息(可信度 ' + rm.pct + '%,来源' + esc(rm.source) + '):' + esc(rm.text) + '
'));
}
rnd.appendChild(el('div', 'story', '「' + esc(N.golden({ goldenCache: G.goldenCache, rng_i: G.turn * 7 + 3 })) + '」
'));
$('#storyWrap').appendChild(rnd);
/* 大劫阶段推进 + 大事件:从统一队列领取 */
var st = G.takeStages();
st.forEach(function (s) { $('#storyWrap').lastChild.appendChild(el('div', 'sysbox box-stage', '【大劫推进·' + esc(s.name) + '】
' + esc(s.desc) + '
')); });
var be = G.takeBigEvents();
be.forEach(function (e) { $('#storyWrap').lastChild.appendChild(el('div', 'sysbox box-world', N.worldNarrative(G, e))); });
renderAll();
if (G.over) { renderChoices([]); showDeath(D.DEATH_FORMS); }
else renderChoices(G.makeOptions());
scrollStory();
autoSave();
}
}]);
}
/* ============================================================
SECTION: menu 天道菜单
============================================================ */
function showMenu() {
var pc = G.pc;
var html = '四大铁律
' +
'1. 绝对不妥协:世界运转遵循严密逻辑,不因你的意愿改变物理法则、战力体系或因果律。拒绝机械降神与强行圆满。
' +
'2. 绝对不遗忘:所有已发生事件、已建立因果、已死亡人物、已消耗资源,永久记录并影响后续推演。
' +
'3. 绝对不套路:严禁模板化叙事、脸谱化人物、套路化奇遇。所有情节基于当前世界状态与你的行为涌现。
' +
'4. 绝对不撤销:已发生之因果不可被任何方式(读档、道具、法术、祈求)撤销。因果只能被转化、被承受,或被更高级别的因果覆盖。
' +
'游玩指南
' +
'· 善用因果网追踪,注意伏笔优先级(天道级 > 因果级 > 个人级 > 日常级)
' +
'· 注意世界 TICK 事件——你不在场时世界照样在转
' +
'· 利用风险收益矩阵决策,注意风闻真假概率
' +
'· 每十回合保存存档码,每五十回合关注大劫阶段
' +
'· 善用修仙百艺降低消耗,珍惜道蕴资源
' +
'· 关注代价状态机递进,避免走到「临界」进而「转化」
' +
'· 接受因果不可撤销,享受死亡与转世
' +
'操作
';
modal('菜单 · 规则与存档', html, [
{
label: '叙事引擎(AI)', cls: 'btn-blue', close: false, act: function () {
closeModal();
setTimeout(openLLMSettings, 30);
}
},
{
label: '切换明暗', cls: 'btn-ghost', close: false, act: function () {
toggleTheme();
}
},
{
label: '游玩说明', cls: 'btn-ghost', close: false, act: function () {
closeModal();
setTimeout(openHelp, 30);
}
},{ label: '关闭', cls: 'btn-ghost' },
{
label: '导出存档码到文件', cls: 'btn-blue', act: function () {
var r = G.makeSaveCode();
if (!r.code) { toast('导出失败', 'err'); return; }
var blob = new Blob([r.code], { type: 'text/plain;charset=utf-8' });
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = '大道无界存档_' + pc.name + '_第' + G.turn + '回合.txt';
document.body.appendChild(a); a.click();
setTimeout(function () { URL.revokeObjectURL(a.href); document.body.removeChild(a); }, 800);
toast('存档码已导出为文件', 'ok');
}
},
{ label: '存入新槽位', cls: 'btn-blue', act: function () { saveToSlot(); } },
{
label: '重开一局', cls: 'btn-gold', act: function () {
modal('重开一局', '当前进度不会丢失(已自动存档)。确定回到启动页开始新的一局吗?
',
[{ label: '取消', cls: 'btn-ghost' }, { label: '重开', cls: 'btn-gold', act: backToStart }]);
}
}
]);
}
/* ---------- 叙事引擎设置 ---------- */
function aiFormVal(id) { var e = document.getElementById(id); return e ? e.value : ''; }
function aiFormChecked(id) { var e = document.getElementById(id); return !!(e && e.checked); }
function aiReadForm() {
return {
enabled: aiFormChecked('aiEnabled'),
mode: aiFormVal('aiMode') || 'direct',
base: aiFormVal('aiBase').trim(),
key: aiFormVal('aiKey').trim(),
proxy: aiFormVal('aiProxy').trim(),
proxyToken: aiFormVal('aiProxyToken').trim(),
model: aiFormVal('aiModel').trim() || 'deepseek-chat',
freq: aiFormVal('aiFreq') || 'major',
temperature: Math.max(0, Math.min(2, parseFloat(aiFormVal('aiTemp')) || 0.95))
};
}
function aiSyncModeBoxes() {
var m = aiFormVal('aiMode');
var d = document.getElementById('aiDirectBox'), p = document.getElementById('aiProxyBox');
if (d) d.style.display = (m === 'direct') ? '' : 'none';
if (p) p.style.display = (m === 'proxy') ? '' : 'none';
}
function openLLMSettings() {
if (!window.LLM) { toast('叙事外接层未加载', 'err'); return; }
var c = LLM.cfg();
var html = '' +
'
LLM 只负责把本地引擎已经算定的结果写成叙事,不参与任何数值判定;失败会自动回退本地推演。
' +
'
' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
直连方式下密钥只存在本机 localStorage,任何拿到这台机器的人都能读到;共用电脑请用代理方式。
' +
'
' +
'
';
modal('叙事引擎 · LLM', html, [
{
label: '测试连接', cls: 'btn-blue', close: false, act: async function () {
var msg = document.getElementById('aiTestMsg');
LLM.save(aiReadForm());
if (msg) msg.textContent = '连接中…';
var r = await LLM.test();
if (msg) msg.textContent = r.ok ? ('连接成功,模型回复:' + String(r.text).slice(0, 30)) : ('失败:' + (r.error || '未知错误'));
toast(r.ok ? '叙事引擎连接成功' : ('连接失败:' + (r.error || '')), r.ok ? 'ok' : 'err');
}
},
{
label: '保存', cls: 'btn-gold', close: false, act: function () {
var v = aiReadForm();
if (v.enabled && v.mode === 'direct' && (!v.base || !v.key)) { toast('直连方式需要 API 地址与 Key', 'err'); return; }
if (v.enabled && v.mode === 'proxy' && !v.proxy) { toast('代理方式需要代理地址', 'err'); return; }
LLM.save(v);
closeModal();
toast('已保存,下一回合生效', 'ok');
if (G) renderTop();
}
},
{ label: '关闭', cls: 'btn-ghost' }
]);
setTimeout(function () {
var sel = document.getElementById('aiMode');
if (sel) sel.addEventListener('change', aiSyncModeBoxes);
aiSyncModeBoxes();
}, 0);
}
/* ---------- 新手教程 / 游玩说明 ---------- */
var TUT_STEPS = [
{
t: '这是什么游戏',
b: '本地推演的修仙沙盒:没有固定剧情,也没有标准答案。你选什么,世界就按它自己的逻辑回什么。唯一不可逆的是结果——死了不能读档复活。'
},
{
t: '开局先读第一章',
b: '每局开头是一章小说。顶部三行告诉你「你是谁、你在哪、你要做什么」,正文会点出眼前最要紧的那件事。人物道碑默认折叠在下面,想核对数值再展开。'
},
{
t: '每回合怎么玩',
b: '读一段叙事,下面给出 4 条行动 + 1 条自由输入。每条都标了风险、收益、代价。想同时做几件事,在「组合」框里输入编号(比如 13)再点并行执行。'
},
{
t: '界面地图',
b: '上:回合 / 大劫 / 气血 / 灵石。左:人物道碑(手机上点底部「道碑」)。右:风闻、因果、人物、世界动态、天地志、坊市、天道点、存档(手机上点「更多」)。'
},
{
t: '「此刻」栏',
b: '抉择区上方一直显示:你在哪、你的长期目的、身上多少灵石,以及最要紧的那件事还剩几个回合到期。拿不准要干嘛时,先看这一栏。'
},
{
t: '代价与死亡',
b: '精力、道伤、道心污染、天道监察、寿元都会累积,不是摆设。气血归零或寿元耗尽就是死;死后只能选一种转化形态,不能读档复活。'
},
{
t: '想用 AI 写文',
b: '右上「天道」→「叙事引擎(AI)」。不配也能玩,AI 只影响文笔,不影响任何判定。'
}
];
function openTutorial(startAt) {
var i = startAt || 0;
function draw() {
var s = TUT_STEPS[i];
var body = '新手教程 · 第 ' + (i + 1) + ' / ' + TUT_STEPS.length + ' 步
' +
'
' + esc(s.t) + '
' + esc(s.b) + '
';
var foot = [];
if (i > 0) foot.push({ label: '上一步', cls: 'btn-ghost', close: false, act: function () { i--; draw(); } });
else foot.push({ label: '关闭', cls: 'btn-ghost' });
if (i < TUT_STEPS.length - 1) foot.push({ label: '下一步', cls: 'btn-blue', close: false, act: function () { i++; draw(); } });
else foot.push({ label: '开始修仙', cls: 'btn-gold' });
modal('新手教程', body, foot);
}
draw();
}
function openHelp() {
var html = '' +
'
一句话:你做什么,天道就按它的逻辑回什么;没有正确答案,只有后果。
' +
'
一、一回合会发生什么
' +
'
1)你读一段叙事;2)从下面的行动里选一个(或自己写);3)本地引擎判定结果、结算因果与世界变化;' +
'4)世界在你不在场时也照常推进(世界 TICK);5)回到第 1 步。
' +
'
每十回合会推送一条「大事件预告」并更新物价;每五十回合推进一次大劫阶段。
' +
'
二、界面位置
' +
'
顶部:回合、大劫阶段、气血、灵石,以及右侧的岁月如梭 / 存档 / 天道。
' +
'
左侧(手机:底部「道碑」):人物道碑——境界、寿元、六司、道途、道伤、生平。
' +
'
右侧(手机:底部「更多」):风闻(真假待辨)、因果、天道点、结算、人物、世界动态、天地志、百艺道途、坊市、存档。
' +
'
抉择区上方:「此刻」栏——你在哪、目的、灵石、最要紧的事。
' +
'
三、关键数值
' +
'
气血:归零即死。精力:每回合消耗,低于 15 不能行动,靠静坐恢复。
' +
'
道蕴:修炼积累,突破时消耗。灵石:通用货币,交易与远行都要用。
' +
'
道心污染:五阶,看到不该看的、用禁忌技艺都会涨;高了会扭曲判定。
' +
'
天道监察 L0–L5:触碰禁忌会升级,L4 以上有天道化身。
' +
'
道伤:肉身 / 神魂 / 因果 / 道基四类五级,直接影响战力与突破。
' +
'
寿元:年龄到上限就死,闭关与岁月如梭都会真实变老。
' +
'
天道点:用道伤、污染、寿元、名声换;可在「天道点」面板抵消天劫、降监察、洗因果、续命。
' +
'
声誉:影响物价与 NPC 初始态度。
' +
'
四、四种开档
' +
'
全随机:最快,全部交给天道。半随机:先选身份类型,三选一。
' +
'
关键词:给 1–3 个词,天道在因果网里找锚点。自定义:自己写人设,需通过四维校验。
' +
'
五、怎么变强
' +
'
闭关修炼涨修为与道蕴;打磨百艺(炼丹、炼器、布阵、符箓…)换资源;行善行恶都会塑道途,' +
'道途到第三境会凝出道号,写进万古道碑。越阶挑战要有代价支撑,硬来会触发天道修正。
' +
'
六、死亡与转世
' +
'
气血归零、寿元耗尽都会死。死后选择转化形态(转世 / 化鬼 / 成傀 / 入道 / 归墟),' +
'部分因果、记忆碎片或道伤可以带进下一世,但要付出代价。死亡不可撤销。
' +
'
七、存档
' +
'
每回合自动存档。「存档」面板可导出/导入存档码;「岁月如梭」可以主动跳过一段时间,' +
'但外界会真实变老、势力会洗牌、伏笔会到期。
' +
'
八、常见问题
' +
'
为什么总是失败?选项标了风险,高风险不一定该死,但需要战力、因果或环境支撑。
' +
'
为什么选项只有四五条?其余路径靠「自由输入」——直接写你想做的事。
' +
'
找不到某条因果了?去「因果」面板看状态,或「天道点」面板用代价换线索。
' +
'
AI 叙事怎么开?右上「天道」→「叙事引擎(AI)」,按提示填地址与密钥。
' +
'
';
modal('游玩说明', html, [{ label: '知道了', cls: 'btn-gold' }]);
}
function bindHelpButtons() {
var t = document.getElementById('btnTutorial');
if (t) t.addEventListener('click', function () { openTutorial(0); });
var h = document.getElementById('btnHelp');
if (h) h.addEventListener('click', function () { openHelp(); });
}
/* ---------- 明暗主题 ---------- */
var THEME_KEY = 'ddwj_theme_v1';
function applyTheme(t) {
document.documentElement.setAttribute('data-theme', t);
try { localStorage.setItem(THEME_KEY, t); } catch (e) {}
var b = document.getElementById('btnTheme');
if (b) { b.textContent = t === 'light' ? '亮' : '暗'; b.title = t === 'light' ? '切换为暗色' : '切换为亮色'; }
var sb = document.getElementById('btnThemeStart');
if (sb) sb.textContent = t === 'light' ? '切到暗色' : '切到亮色';
}
function toggleTheme() {
applyTheme(document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light');
}
(function initTheme() {
var t = 'dark';
try { t = localStorage.getItem(THEME_KEY) || 'dark'; } catch (e) {}
applyTheme(t);
document.addEventListener('click', function (e) {
var id = e.target && e.target.id;
if (id === 'btnTheme' || id === 'btnThemeStart') toggleTheme();
});
})();
/* ---------- 数值与文案纠错:所有数量从数据实算 ---------- */
(function fixCounts() {
var facts = document.getElementById('startFacts');
if (facts) facts.textContent = D.START_WORLDS.length + ' 初始世界 · ' + D.UPPER_REALMS.length + ' 上界 · 1 位面 · ' + D.SYSTEMS.length + ' 道途';
var stats = document.getElementById('startStats');
if (stats) {
stats.innerHTML = '四大铁律:绝对不妥协 · 绝对不遗忘 · 绝对不套路 · 绝对不撤销
' +
'初始世界 ' + D.START_WORLDS.length + ' 座 · 上界 ' + D.UPPER_REALMS.length + ' 座 · 位面 1 处 · 出身 ' +
(D.ORIGINS.length - 1) + ' 种 · 灵根 ' + (D.LINGGEN.length - 1) + ' 类 · 天赋 ' + D.TALENTS.length +
' 项 · 命途缺陷 ' + D.FLAWS.length + ' 种 · 因果线 ' + D.KARMA_POOL.length + ' 条 · 世界状态 ' + D.WORLD_STATES.length + ' 项
' +
'每局候选均由混沌种子动态抽取,可「换一批」;负面选择返还天道点,游戏期亦可以道伤、污染、寿元、名声换取天道点。
' +
'全部推演在本机浏览器内完成,无需联网、无需接口,永不中断生成。';
}
if (typeof BASE_STEPS !== 'undefined') {
BASE_STEPS[0].lead = '本局的世界候选由混沌种子从 ' + D.START_WORLDS.length + ' 座初始世界中抽取 5 座,每次开档不同,可「换一批」。世界的法则、灵气与危险度决定开局叠加哪些世界状态。';
BASE_STEPS[2].lead = '本局出身候选从 ' + (D.ORIGINS.length - 1) + ' 种中抽 12 种,可换一批。出身带来初始六维、物品、百艺、声誉与因果线;负面出身返还天道点。';
BASE_STEPS[3].lead = '本局灵根候选从 ' + (D.LINGGEN.length - 1) + ' 类中抽 14 类,可换一批。神品灵根极贵且会带来额外注视;负面灵根(如九阴绝脉)返还天道点。';
BASE_STEPS[4].lead = '本局天赋候选从 ' + D.TALENTS.length + ' 项中抽 12 项正面天赋,可换一批。正面天赋消耗天道点。';
BASE_STEPS[5].lead = '本局缺陷候选从 ' + D.FLAWS.length + ' 种中抽 8 种,另有 6 项负面天赋,可换一批。每一项缺陷都会返还天道点,且会在游戏过程中周期性真实发作。';
}
})();
bindHelpButtons();
function kv2(k, v) { return '' + esc(k) + '' + esc(v == null ? '—' : v) + '
'; }
function openIdentity() {
if (!G || !G.opening) return;
var id = G.opening.identity, pc = G.pc, sys = D.systemOf(pc);
var rows = [
['身份', id.status, D.explainIdentity('status', id.status)],
['出身', id.birth, D.explainIdentity('birth', id.birth)],
['标签', id.tag, D.explainIdentity('tag', id.tag)],
['处境', id.plight, D.explainIdentity('plight', id.plight)]
];
var html = '' + rows.map(function (r) {
return '
' + esc(r[0]) + '
' +
'
' + esc(r[1]) + '' + esc(r[2]) + '
';
}).join('') +
'
道途
' + esc(sys.name + ' · ' + D.realmNameOf(pc, pc.realmIdx)) + '' + esc(sys.desc) + '
' +
'
执念
' + esc(pc.goal.name) + '' + esc(pc.goal.desc) + '
' +
'
';
modal('身份说明', html, [{ label: '知道了', cls: 'btn-gold' }]);
}
function openNpc(id) {
var n = (G.npcs || []).filter(function (x) { return x.id === id; })[0];
if (!n) return;
var atts = D.ATTITUDES.map(function (a) { return [a, n.att[a] || 0]; }).filter(function (x) { return x[1] >= 5; });
var top = atts.slice().sort(function (a, b) { return b[1] - a[1]; })[0];
var rel = !n.met ? '你们还没正式见过面。' : (top ? '他对你:' + top[0] + '(' + top[1] + ')。' : '他对你没什么明显的态度。');
var html = '' +
'
' + esc(n.fullName) + '' + esc(D.realmNameOf(n, n.realmIdx)) + '' +
(n.isQiyun ? '气运之子' : '') + '
' +
'
' + esc(rel) + (n.met ? ' 羁绊:' + esc(D.BONDS[n.bond || 0]) : '') + '
' +
'
' +
kv2('势力', n.factionName) + kv2('年龄', n.age + ' 岁 / 寿元 ' + Math.round(n.lifeMax || 0)) +
kv2('性格', n.personality) + kv2('目标', n.goal) +
kv2('战力', n.power) + kv2('背叛概率', Math.round((n.betrayalBase || 0) * 100) + '%') +
'
' +
(n.motiveRevealed ? '
隐藏动机:' + esc(n.hiddenMotive) + '
' : '') +
'
对你的态度
' +
(atts.length ? atts.map(function (a) {
return '
' + esc(a[0]) + '' + a[1] + '
';
}).join('') : '
暂时没有明显态度。
') +
((n.memory || []).length ? '
他记得的事
' + n.memory.slice(0, 6).map(function (m) {
return '
· 第 ' + m.turn + ' 回合【' + esc(m.level) + '】' + esc(m.summary) + '
';
}).join('') : '') +
'
';
modal(n.fullName, html, [{ label: '关闭', cls: 'btn-ghost' }]);
}
function openCustomSystem() {
var base = D.systemById((WZ && WZ.systemId) || 'qi');
var src = (WZ && WZ.customSystem) || { name: '自创体系', desc: '', upper: '', ladder: base.ladder.map(function (r) { return [r[0], r[1]]; }) };
var html = '自创体系只改名称与叙事:第 N 境=原体系第 N 境,战力同档,不会额外变强。
' +
'' +
'' + src.ladder.map(function (r, i) {
return '
';
}).join('') + '
' +
'';
modal('自创修炼体系', html, [
{
label: '保存并选用', cls: 'btn-gold', close: false, act: function () {
var name = (document.getElementById('csName').value || '').trim() || '自创体系';
var ladder = [];
for (var i = 0; i < 11; i++) {
var nm = (document.getElementById('csR' + i).value || '').trim() || ('第' + (i + 1) + '境');
ladder.push([nm, '自创体系第 ' + (i + 1) + ' 境(战力对齐气道·' + D.systemById('qi').ladder[i][0] + ')。']);
}
var desc = (document.getElementById('csDesc').value || '').trim() || '你自己写下的一条路。名字是你定的,规矩也是你定的。';
D.saveCustomSystem({ name: name, desc: desc, ladder: ladder, upper: name + '上界' });
WZ.customSystem = { name: name, desc: desc, ladder: ladder, upper: name + '上界' };
WZ.systemId = 'custom'; WZ.systemTouched = true;
closeModal(); renderStep();
toast('已选用自创体系:' + name, 'ok');
}
},
{ label: '取消', cls: 'btn-ghost' }
]);
}
document.addEventListener('click', function (e) {
var t = e.target && e.target.closest ? e.target.closest('[data-gloss]') : null;
if (t) { e.preventDefault(); openIdentity(); }
});
function backToStart() { $('#game').classList.remove('on');
$('#startScreen').style.display = 'flex';
G = null;
}
/* ============================================================
SECTION: render-all
============================================================ */
function renderAll() {
if (!G || !G.pc) return;
if (G.pc.systemId) D.__activeSystem = G.pc.systemId;
renderLeft(); renderTop(); renderTabs(); renderTabBody(); renderMobNav();
}
/* ============================================================
SECTION: bindings
============================================================ */
/* ---------- 启动页:四种开档方式 ---------- */
var startMode = 'fullrandom';
document.querySelectorAll('.mode-row').forEach(function (c) {
c.addEventListener('click', function () {
startMode = c.getAttribute('data-mode');
openWizard(startMode);
});
});
function openWizard(mode) {
WZ = newWizard(mode);
ensureSets();
/* 全随机:一键生成全部配置(预算受控,保证可开档),直接进入预览确认 */
if (mode === 'fullrandom') {
var rr = DYNAMIC.buildFullRandom(WZ.pool);
WZ.world = rr.world || WZ.sets.worlds[Math.floor(WZ.pool.rng() * WZ.sets.worlds.length)];
WZ.systemId = (WZ.world && WZ.world.defaultSystem) || 'qi';
WZ.origin = rr.origin; WZ.linggen = rr.linggen; WZ.tianzi = rr.tianzi;
WZ.talents = (rr.talents || []).slice();
WZ.flaws = (rr.flaws || []).slice();
WZ.six = rr.six;
WZ.goal = D.GOALS[Math.floor(WZ.pool.rng() * D.GOALS.length)];
WZ.daohao = '';
/* 跳到预览步,玩家可直接确认或回退微调 */
WZ.step = buildSteps('fullrandom').length - 1;
}
$('#startScreen').style.display = 'none';
$('#wizard').classList.add('on');
renderStep();
}
$('#btnFull').addEventListener('click', function () { openWizard('fullrandom'); });
var btnCustomEl = document.getElementById('btnCustom');
if (btnCustomEl) btnCustomEl.addEventListener('click', function () { openWizard(startMode); });
$('#btnLoad').addEventListener('click', function () {
var saves = getSaves();
if (!saves.length) {
modal('续前世因缘', '当前浏览器还没有任何存档。
你也可以粘贴一段天道存档码来续玩——在创角页退出后,或开局后点击右侧「存档」标签页即可读取。
',
[{ label: '知道了', cls: 'btn-gold' }]);
return;
}
var html = saves.map(function (s, i) {
return '';
}).join('');
modal('续前世因缘', html + '',
[{ label: '取消', cls: 'btn-ghost' }, {
label: '读取', cls: 'btn-gold', act: function () {
var paste = $('#pasteCode') ? $('#pasteCode').value.trim() : '';
var code = paste || (selSave != null && saves[selSave] ? saves[selSave].code : null);
if (!code) { toast('请选择一个存档或粘贴存档码', 'err'); return; }
var g2 = E.Game.loadFromCode(code);
if (g2 && g2.error) { toast('读取失败:' + g2.error, 'err'); return; }
if (!g2 || !g2.pc) { toast('存档损坏或格式不符', 'err'); return; }
G = g2; curRound = 0; selSave = null;
$('#startScreen').style.display = 'none';
$('#game').classList.add('on');
$('#storyWrap').innerHTML = '';
var rnd = el('div', 'round');
rnd.appendChild(headRow('续前世因缘', '第 ' + G.turn + ' 回合'));
rnd.appendChild(el('div', 'sysbox box-break', '【读档成功】' + esc(G.pc.name) + ',' + esc(D.REALMS[G.pc.realmIdx].name) + '。
' +
'' + esc(G.worldBrief()) + '
' +
'铁律·绝对不撤销:读档不能撤销已发生的因果。你回到的是这个时间点,而不是它之前。
'));
$('#storyWrap').appendChild(rnd);
renderAll(); renderChoices(G.makeOptions());
toast('读档成功', 'ok');
}
}]);
var selSave = null;
setTimeout(function () {
document.querySelectorAll('#mBody .death-form').forEach(function (d) {
d.addEventListener('click', function () {
document.querySelectorAll('#mBody .death-form').forEach(function (x) { x.classList.remove('sel'); });
d.classList.add('sel'); selSave = parseInt(d.getAttribute('data-i'), 10);
});
});
}, 0);
});
$('#btnMontage').addEventListener('click', doMontage);
$('#btnSave').addEventListener('click', function () { saveToSlot(); curTab = 'save'; renderTabs(); renderTabBody(); });
$('#btnMenu').addEventListener('click', showMenu);
/* ---------- 移动端底部导航(5 格:道碑 / 史书 / 风闻 / 因果 / 更多) ---------- */
var MOB_NAV = [
{ k: 'left', label: '道碑' },
{ k: 'story', label: '史书' },
{ k: 'tab:rumor', label: '风闻' },
{ k: 'tab:karma', label: '因果' },
{ k: 'more', label: '更多' }
];
function highlightMob(k) {
var row = $('#mnRow'); if (!row) return;
row.querySelectorAll('button').forEach(function (x) {
x.classList.toggle('on', x.getAttribute('data-nav') === k);
});
}
function renderMobNav() {
var row = $('#mnRow');
if (!row || row.dataset.built === '1') return;
row.dataset.built = '1';
MOB_NAV.forEach(function (it) {
var b = el('button', it.k === 'story' ? 'on' : '', esc(it.label));
b.setAttribute('data-nav', it.k);
row.appendChild(b);
});
row.addEventListener('click', function (e) {
var b = e.target.closest('button[data-nav]');
if (!b) return;
var k = b.getAttribute('data-nav');
if (k === 'more') { openTabSheet(); return; }
highlightMob(k);
if (k.indexOf('tab:') === 0) {
curTab = k.slice(4);
$('#game').setAttribute('data-mob', 'right');
renderTabs(); renderTabBody();
} else {
$('#game').setAttribute('data-mob', k);
}
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
function openTabSheet() {
var html = '' + TABS.map(function (t) {
var unread = t.id === 'rumor' && G ? G.rumors.filter(function (r) { return !r.read; }).length : 0;
return '';
}).join('') + '
';
modal('全部面板', html, [{ label: '关闭', cls: 'btn-ghost' }]);
setTimeout(function () {
document.querySelectorAll('.tab-sheet-item').forEach(function (b) {
b.addEventListener('click', function () {
curTab = b.getAttribute('data-tab');
highlightMob(['rumor', 'karma'].indexOf(curTab) >= 0 ? 'tab:' + curTab : 'more');
$('#game').setAttribute('data-mob', 'right');
renderTabs(); renderTabBody();
closeModal();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
});
}, 0);
}
/* ---------- 回到抉择:手机上史书很长,随时能跳回选项 ---------- */
(function () {
var fab = el('button', '', '↓ 抉择');
fab.id = 'toChoices';
fab.addEventListener('click', function () {
var c = $('#choiceWrap');
if (c) c.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
document.body.appendChild(fab);
var onScroll = function () {
var inGame = $('#game') && $('#game').classList.contains('on');
var isStory = $('#game') && $('#game').getAttribute('data-mob') === 'story';
var cw = $('#choiceWrap');
var cwTop = cw ? cw.getBoundingClientRect().top : 99999;
/* 抉择区还看不见时才提示,看见了就收起,避免挡住选项 */
var show = inGame && isStory && window.innerWidth <= 980 && cwTop > window.innerHeight - 140;
fab.classList.toggle('on', !!show);
};
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
window.__ddwjScrollFab = onScroll;
})();
/* 深链:?play=1 直接全随机开局(方便分享/快速进入) */
if (/[?&]play=1/.test(location.search)) {
setTimeout(function () {
try { openWizard('fullrandom'); startGame(); } catch (e) { /* 启动失败则停留在首页 */ }
}, 80);
}
/* 快捷键:数字键选择选项 */
document.addEventListener('keydown', function (e) {
if (!G || !$('#game').classList.contains('on')) return;
if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') return;
var n = parseInt(e.key, 10);
if (n >= 1 && n <= curOptions.length) {
if (curOptions[n - 1].free) toggleFreeInput();
else doChoice(n - 1, null);
}
});
})();
By Meoo 秒悟