feat: 世界聊天敏感词过滤+菜单整合, 部署脚本绑定 yt_game
This commit is contained in:
+71
-9
@@ -42,7 +42,7 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
|
||||
}
|
||||
#topBar .title .worldName.editable { cursor: pointer; padding: 2px 8px; border-radius: 5px; transition: background .15s; }
|
||||
#topBar .title .worldName.editable:hover { background: rgba(255,255,255,.08); }
|
||||
#topBar .title .worldName-input {
|
||||
.worldName-input {
|
||||
background: transparent; border: 1px solid rgba(124,199,255,.6); color: #cfe1f3;
|
||||
font-size: 15px; font-weight: 600; padding: 2px 6px; border-radius: 5px; width: 180px;
|
||||
font-family: inherit; outline: none;
|
||||
@@ -172,7 +172,7 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
|
||||
|
||||
/* 世界等级经验条(位于 bottomBar 内最左侧,加大加清晰) */
|
||||
#worldExpBarWrap{
|
||||
flex:0 0 auto; width:380px; height:100%;
|
||||
flex:0 0 auto; width:520px; height:100%;
|
||||
display:flex; flex-direction:column; justify-content:center; gap:7px;
|
||||
padding:0 16px 0 0; background:transparent; border:none;
|
||||
}
|
||||
@@ -180,8 +180,9 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
|
||||
.we-lv{font-size:16px; font-weight:800; color:#c4b5fd; text-shadow:0 0 10px rgba(139,92,246,.5);}
|
||||
.we-title{font-size:13px; color:#9fb0c4; max-width:130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
|
||||
.we-exp-txt{margin-left:auto; font-size:13px; font-weight:600; color:#cfe1f3;}
|
||||
.we-bar{width:100%; height:12px; background:rgba(255,255,255,.10); border-radius:6px; overflow:hidden;}
|
||||
.we-fill{height:100%; width:0%; background:linear-gradient(90deg,#8b5cf6,#4af); border-radius:6px; transition:width .3s ease; box-shadow:0 0 12px rgba(139,92,246,.45);}
|
||||
.we-pct{margin-left:2px; font-size:13px; font-weight:700; color:#c4b5fd; min-width:34px; text-align:right;}
|
||||
.we-bar{width:100%; height:14px; background:rgba(255,255,255,.10); border-radius:7px; overflow:hidden;}
|
||||
.we-fill{height:100%; width:0%; background:linear-gradient(90deg,#8b5cf6,#4af); border-radius:7px; transition:width .3s ease; box-shadow:0 0 12px rgba(139,92,246,.45);}
|
||||
|
||||
/* 主沙盘底部功能框架(预留区:放功能按钮 / 素材槽,比微观边框更高) */
|
||||
#bottomBar {
|
||||
@@ -238,6 +239,8 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
|
||||
}
|
||||
#microHeader .backBtn:hover { background: rgba(100,200,255,0.25); }
|
||||
#microHeader .microTitle { margin-left: 16px; font-size: 16px; font-weight: bold; color: #e0e0e0; }
|
||||
#microHeader .microTitle.editable { cursor: pointer; padding: 2px 8px; border-radius: 5px; transition: background .15s; }
|
||||
#microHeader .microTitle.editable:hover { background: rgba(255,255,255,.08); }
|
||||
#microHeader .microSubtitle { display: none; }
|
||||
|
||||
#microBody { margin-top: 96px; flex: 1; display: flex; flex-direction: column; position: relative; overflow: hidden; }
|
||||
@@ -715,7 +718,7 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
|
||||
@media (max-width:420px){
|
||||
#topBar .title .worldName{display:none;}
|
||||
#bottomBar .bb-btn{padding:7px 9px;font-size:11px;}
|
||||
#worldExpBarWrap{width:220px;gap:5px;}
|
||||
#worldExpBarWrap{width:280px;gap:5px;}
|
||||
#worldExpBarWrap .we-title{display:none;}
|
||||
#sceneToolbar{right:8px;gap:5px;padding:6px 7px;top:calc(58px + env(safe-area-inset-top) + 6px);}
|
||||
#sceneToolbar .top-icon{width:34px;height:34px;font-size:16px;}
|
||||
@@ -818,6 +821,7 @@ body { background: #0a0e17; overflow: hidden; font-family: 'Microsoft YaHei', sa
|
||||
<span class="we-lv">世界 Lv.<b id="weLv">--</b></span>
|
||||
<span class="we-title" id="weTitle">--</span>
|
||||
<span class="we-exp-txt" id="weExpTxt">EXP -- / --</span>
|
||||
<span class="we-pct" id="weExpPct">--%</span>
|
||||
</div>
|
||||
<div class="we-bar"><div class="we-fill" id="weExpFill" style="width:0%"></div></div>
|
||||
</div>
|
||||
@@ -1690,6 +1694,7 @@ let ambParticles = [];
|
||||
|
||||
// 微观世界
|
||||
let microState = null;
|
||||
let microNames = {}; // 地块 key("q,r") -> 该微观世界自定义名称(后端持久化,记忆)
|
||||
let microBuildRevealAt = 0; // 建筑显示时间戳:过渡结束 + 200ms 后才浮现
|
||||
// 游戏化系统全局状态(与 Odoo 后端打通)
|
||||
let microChar = null; // 玩家角色:{name,level,exp,gold,inventory,equip}
|
||||
@@ -2972,6 +2977,15 @@ function pickTransitionImage() {
|
||||
return TRANSITION_IMAGES[idx];
|
||||
}
|
||||
|
||||
// ============ 微观世界标题:有自定义名显示「层 · 自定义名」,否则默认「层 · 地形村落」 ============
|
||||
function microTitleText(tile) {
|
||||
const key = tile.q + ',' + tile.r;
|
||||
const custom = microNames[key];
|
||||
const layerLabel = { earth: '🌍 大地', under: '🕳 地下', core: '🔥 地心', sky: '🌤 天空', star: '✦ 星空' }[tile.layer] || '';
|
||||
if (custom) return `${layerLabel} · ${custom}`;
|
||||
return `${layerLabel} · ${TERRAIN[tile.terrain].name}村落`;
|
||||
}
|
||||
|
||||
// ============ 进入微观世界 ============
|
||||
function enterMicroWorld(tile) {
|
||||
if (!tile) return;
|
||||
@@ -3012,8 +3026,7 @@ function enterMicroWorld(tile) {
|
||||
microZoom = 1; microPanX = 0; microPanY = 0;
|
||||
microViewOpen = true; // 暂停主世界重绘
|
||||
microView.classList.add('active'); // 入场动画(缩放+去模糊)在遮罩掩护下播放,揭幕时早已归位
|
||||
const layerLabel = { earth:'🌍 大地', under:'🕳 地下', core:'🔥 地心', sky:'🌤 天空', star:'✦ 星空' }[tile.layer] || '';
|
||||
document.getElementById('microTitle').textContent = `${layerLabel} · ${TERRAIN[tile.terrain].name}村落`;
|
||||
document.getElementById('microTitle').textContent = microTitleText(tile);
|
||||
document.getElementById('microSubtitle').textContent = `坐标(${tile.q}, ${tile.r}) · ${microState.units.length}位居民`;
|
||||
applyMicroAccent(tile.terrain);
|
||||
updateSidebar();
|
||||
@@ -3682,8 +3695,7 @@ function setMicroTile(tile) {
|
||||
? [{ ...BUILDINGS[tile.buildingType], type: tile.buildingType, level: tile.buildingLevel, hp: 80 + tile.buildingLevel * 10 }]
|
||||
: [];
|
||||
microState.resources = calcResources(tile);
|
||||
const layerLabel = { earth: '🌍 大地', under: '🕳 地下', core: '🔥 地心', sky: '🌤 天空', star: '✦ 星空' }[tile.layer] || '';
|
||||
document.getElementById('microTitle').textContent = `${layerLabel} · ${TERRAIN[tile.terrain].name}村落`;
|
||||
document.getElementById('microTitle').textContent = microTitleText(tile);
|
||||
document.getElementById('microSubtitle').textContent = `坐标(${tile.q}, ${tile.r}) · ${microState.units.length}位居民`;
|
||||
updateSidebar();
|
||||
microAutoRoadCells(); // 切地块时重新计算自动道路贴图
|
||||
@@ -5044,6 +5056,7 @@ function applyWorldState(ws) {
|
||||
if (ov.terrain) t.terrain = ov.terrain;
|
||||
if (ov.height != null) { t.height = ov.height; t.tier = heightToTier(ov.height); }
|
||||
if (ov.flags) t.flags = ov.flags;
|
||||
if (ov.micro_name != null) microNames[ov.q + ',' + ov.r] = ov.micro_name; // 恢复微观世界命名记忆
|
||||
}
|
||||
// 微观建造布置:按 'q,r' 恢复(覆盖所有层,进入地块时即显示)
|
||||
for (const ov of ws.tiles) {
|
||||
@@ -5946,6 +5959,51 @@ function setupWorldNameEdit() {
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 微观世界标题:点击进入编辑,回车/失焦保存到后端,Esc 取消 ============
|
||||
function setupMicroNameEdit() {
|
||||
const mt = document.getElementById('microTitle');
|
||||
if (!mt) return;
|
||||
mt.classList.add('editable');
|
||||
mt.title = '点击重命名此微观世界';
|
||||
mt.addEventListener('click', () => {
|
||||
if (!microState || !microState.tile) return; // 仅在微观视图内可编辑
|
||||
if (mt.querySelector('input')) return; // 已在编辑
|
||||
const tile = microState.tile;
|
||||
const key = tile.q + ',' + tile.r;
|
||||
const current = microNames[key] || '';
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.value = current;
|
||||
input.placeholder = '输入此微观世界名称';
|
||||
input.maxLength = 24;
|
||||
input.className = 'worldName-input';
|
||||
input.spellcheck = false;
|
||||
mt.textContent = '';
|
||||
mt.appendChild(input);
|
||||
input.focus();
|
||||
input.select();
|
||||
let done = false;
|
||||
const restore = () => { if (done) return; done = true; mt.textContent = microTitleText(tile); };
|
||||
const commit = async () => {
|
||||
if (done) return;
|
||||
const next = input.value.trim();
|
||||
if (!next || next === current) { restore(); return; }
|
||||
restore(); // 先本地显示,再异步写后端
|
||||
microNames[key] = next;
|
||||
try {
|
||||
const r = await API.renameMicro(tile.q, tile.r, next);
|
||||
if (r && r.ok && r.name) { microNames[key] = r.name; }
|
||||
else console.warn('微观世界重命名后端未响应,已本地保存', r);
|
||||
} catch (e) { console.warn('微观世界重命名失败,已本地保存', e); }
|
||||
};
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') { e.preventDefault(); commit(); }
|
||||
else if (e.key === 'Escape') { e.preventDefault(); restore(); }
|
||||
});
|
||||
input.addEventListener('blur', commit);
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
W = canvas.width = window.innerWidth;
|
||||
H = canvas.height = window.innerHeight - BOTTOM_BAR_H;
|
||||
@@ -5960,6 +6018,8 @@ function init() {
|
||||
setupBuildUI();
|
||||
// 顶栏世界名称点击编辑
|
||||
setupWorldNameEdit();
|
||||
// 微观世界标题点击编辑(按地块独立命名,持久化到后端)
|
||||
setupMicroNameEdit();
|
||||
// 钟表已移走;时间文字由 syncTimeUI 每 100ms 节流更新
|
||||
// 世界切换导航
|
||||
document.querySelectorAll('#worldNav .wn-item').forEach(el => {
|
||||
@@ -6245,6 +6305,8 @@ if (typeof fitWorld === 'function') fitWorld();
|
||||
if (weExpFill) weExpFill.style.width = worldExpPct + '%';
|
||||
const weExpTxt = document.getElementById('weExpTxt');
|
||||
if (weExpTxt) weExpTxt.textContent = 'EXP ' + worldExp + ' / ' + worldExpMax;
|
||||
const weExpPctEl = document.getElementById('weExpPct');
|
||||
if (weExpPctEl) weExpPctEl.textContent = Math.floor(worldExpPct) + '%';
|
||||
// 邮件未读角标(顶部图标栏)
|
||||
const topMailBadge = document.getElementById('topMailBadge');
|
||||
if (topMailBadge) {
|
||||
|
||||
Reference in New Issue
Block a user