// 宇森 · 世界内面板逻辑(图鉴 / 规则 / 论坛 / 公告) // 依赖 assets/game-api.js(提供 API) // ---------- 登录态守卫 ---------- // TEMP: 本地临时默认放行,跳过 Odoo 登录校验;打通后端后删除此分支并恢复下面逻辑 const TEMP_SKIP_AUTH = true; (async () => { if (TEMP_SKIP_AUTH) { const u = document.getElementById('userName'); if (u) u.textContent = '园丁'; return; } try { const me = await API.me(); if (!me.uid) { location.href = 'login.html?next=world.html'; return; } const u = document.getElementById('userName'); if (u) u.textContent = me.name || me.login; } catch (e) { location.href = 'login.html'; } })(); // ---------- 退出 ---------- const logoutBtn = document.getElementById('logoutBtn'); if (logoutBtn) logoutBtn.addEventListener('click', async () => { await API.logout(); location.href = 'index.html'; }); // ---------- 面板控制 ---------- const panel = document.getElementById('dataPanel'); const dpTitle = document.getElementById('dpTitle'); const dpBody = document.getElementById('dpBody'); const dpClose = document.getElementById('dpClose'); if (dpClose) dpClose.addEventListener('click', () => panel.classList.remove('open')); async function openPanel(kind) { if (!panel) return; panel.classList.add('open'); dpBody.innerHTML = '
加载中…
'; try { if (kind === 'codex') { dpTitle.textContent = '图鉴'; renderCodex(await API.codex()); } else if (kind === 'rules') { dpTitle.textContent = '规则'; renderRules(await API.rules()); } else if (kind === 'forum') { dpTitle.textContent = '论坛'; renderForum(await API.threads()); } else if (kind === 'announce') { dpTitle.textContent = '公告'; renderAnnounce(await API.announcements()); } } catch (e) { dpBody.innerHTML = '
加载失败,请稍后重试
'; } } document.querySelectorAll('.world-nav button[data-panel]').forEach(b => { b.addEventListener('click', () => openPanel(b.dataset.panel)); }); // ---------- 程序化矢量缩略图(零 AI 生图,不消耗积分) ---------- 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){x+=``;}else if(v===1){x+=``;}else{x+=``;}x+=``;return x;} function shBuild(r,s){let x=``;x+=``;for(let i=0;i<3;i++)x+=``;return x;} function shElement(r,s){const sides=3+Math.floor(r()*6);let x=``;x+=``;return x;} function shPersona(r,s){let x=``;x+=``;x+=``;return x;} function shTalent(r,s){const sp=4+Math.floor(r()*4);let x=``;x+=``;return x;} function shRelic(r,s){let x=``;x+=``;return x;} function shEquip(sub,s){switch(sub){case 'head':return ``;case 'hand':{let x=``;for(let i=0;i<3;i++)x+=``;return x;}case 'chest':return ``;case 'pants':return ``;case 'shoes':return ``;case 'ring':return ``;case 'neck':return ``;case 'treasure':return ``;default:return ``;}} function shEvent(r,s){let x=``;x+=``;return x;} function shapeFor(cat,sub,r,s){switch(cat){case 'race':return shRace(r,s);case 'build':return shBuild(r,s);case 'element':return shElement(r,s);case 'persona':return shPersona(r,s);case 'talent':return shTalent(r,s);case 'relic':return shRelic(r,s);case 'equip':return shEquip(sub,s);case 'event':return shEvent(r,s);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}`+ ``; } // ---------- 渲染:图鉴(分类 → 子级 → 条目) ---------- function renderCodex(data){ const groups = (data && data.groups) || []; if (!groups.length){ dpBody.innerHTML = '
暂无图鉴数据
'; return; } let html = ''; groups.forEach(g => { html += `

${g.label}

`; (g.subs || []).forEach(sub => { if (sub.name && sub.name !== '其他') html += `
${sub.name}
`; html += '
'; sub.items.forEach(it => { const imgTag = it.image ? `${it.name}` : ''; const fallback = `
${thumbSVG(it)}
`; html += `
`+ `
${imgTag || fallback}
`+ `
${it.name}
`+ `
${it.sub||''}${it.tier||''}
`+ `
${it.desc||''}
`; }); html += '
'; }); html += '
'; }); dpBody.innerHTML = html; // 处理有 image 但加载失败的:替换为程序化 SVG dpBody.querySelectorAll('img.dp-thumb-img').forEach(img => { img.addEventListener('error', () => { const svg = thumbSVG({cat:img.dataset.cat, sub:img.dataset.sub, name:img.dataset.name, tier:img.dataset.tier}); const div = document.createElement('div'); div.className='dp-thumb-svg'; div.innerHTML = svg; img.replaceWith(div); }); }); } // ---------- 渲染:规则(按类别分组) ---------- function renderRules(data){ const groups = (data && data.groups) || []; if (!groups.length){ dpBody.innerHTML = '
暂无规则数据
'; return; } let html = ''; groups.forEach(g => { html += `

${g.label}

`; (g.items || []).forEach(it => { const lvl = (it.level!=null) ? ('Lv.'+it.level) : ''; html += `
${lvl}${it.name}${it.desc||''}
`; }); html += '
'; }); dpBody.innerHTML = html; } // ---------- 渲染:论坛 ---------- function renderForum(threads){ threads = threads || []; if (!threads.length){ dpBody.innerHTML = '
暂无主题
'; return; } let html = '
'; threads.forEach(t => { const replies = (t.replies || []).map(p => `
${p.author_name||'匿名'}${p.create_date}
${p.body_html||''}
` ).join(''); html += `
`+ `
${t.category_label||''}`+ (t.pinned?`置顶`:'')+`${t.name}`+ `${t.author_name||''}
`+ `
${t.body_html||''}
`+ `
${replies}
`; }); html += '
'; dpBody.innerHTML = html; } // ---------- 渲染:公告 ---------- function renderAnnounce(list){ list = list || []; if (!list.length){ dpBody.innerHTML = '
暂无公告
'; return; } let html = '
'; list.forEach(n => { const pri = n.priority==='urgent' ? 'urgent' : (n.priority==='important' ? 'imp' : ''); html += `
${n.date}
`+ `
${n.body_html||''}
`; }); html += '
'; dpBody.innerHTML = html; }