fea: 添加地图管理
This commit is contained in:
1 parent
cc2883458c
commit
22157d4baa
7 files changed
+201
-44
No files matched your search
@@ -104,3 +104,39 @@
|
||||
- `pause()` 里按 `G.fromEditor` 改 `#scr-pause [data-act="quit"]` 的 textContent;`UI.init` 的 `quit` 改为 `() => (G.fromEditor ? toEditor() : toLevels())`。
|
||||
- 结算屏(scr-result)的 quit 未动,仍为「关卡选择」——用户只要求暂停界面。
|
||||
- 验证:Node 桩冒烟(`/tmp/ed_pause_quit.mjs`,5 断言:两种来源标签切换、quit 两种跳转、从关卡选择启动自制关复位 fromEditor);`death/map/check/solve/smoke/save/key/dom-smoke/ice` 九套全绿。
|
||||
|
||||
## 编辑器:保存=更新当前地图 / 打开=新图 / 新增「导入地图」按钮(用户要求)
|
||||
- 需求:①点「保存」不再每次新建,而是保存当前地图;②每次打开编辑器都是新地图;③加一个「导入地图」按钮,从以前保存的地图里选。
|
||||
- `js/save.js`:新增 `updateCustom(lv)`(按 id 就地替换,id 不存在则追加 + flush)。`addCustom` 保留未删以兼容旧调用。
|
||||
- `js/editor.js`:
|
||||
- `open()` 改为**每次重置**为空白新图(宽高沿用 `#ed-w/#ed-h` 输入值)并 `this.cur=null`;抽出 `show()` 只重绘不重置。
|
||||
- `save()`:`makeCustomLevel` 后若 `this.cur` 存在则 `lv.id = this.cur`,统一走 `Save.updateCustom` → 首次新建、之后就地更新(id 不变、列表不涨)。
|
||||
- 新增导入:`openImport()`(弹窗 `#ed-importbox` 列 `Save.customs()`,空则提示)、`closeImport()`、`pickImport(id)`(hydrate + 设 cur + 关弹窗)。
|
||||
- `updateSaveState()`:`#ed-savestate` 显示「→ 更新「XX」」/「→ 另存为新图」,在 open/show/save/pickImport/删除/列表点击后刷新。
|
||||
- 「我的地图」列表点击仍是导入(设 cur + hydrate + 更新提示);删除正在编辑的地图时 `cur` 复位为 null。
|
||||
- `index.html`:工具栏加 `data-act="ed-import"` 按钮 + `#ed-savestate` 提示;`#scr-editor` 内加 `#ed-importbox` 弹窗(`.modal`)。
|
||||
- `css/style.css`:`.modal/.modal-panel/.imp-row/.imp-empty/.modal-btns/.save-state` 样式。
|
||||
- 关键坑:`toEditor()`(暂停菜单「返回编辑器」)不能调 `open()`——那会把正在试玩的地图清空。改为 `Editor.faces ? Editor.show() : Editor.open()`。
|
||||
- 验证:Node 桩冒烟(`/tmp/ed_save_import.mjs`,22 断言:open 恒新图/cur=null、首存新建+再存就地更新 id 不变、新图再存新增、导入载入+保存更新原图、删除解除关联、show 保留、savestate 提示切换、空存档导入提示);暂停菜单测试 5 断言仍过;`death/map/check/solve/smoke/save/key/dom-smoke/ice` 九套全绿。
|
||||
|
||||
## 编辑器去掉「我的地图」+ 关卡选择自制页加地图管理(删除)(用户要求)
|
||||
- 需求:①编辑器右侧去掉「我的地图」,只保留「导入地图」;②关卡选择的自制页要有地图管理选项,可删除某个地图。
|
||||
- 编辑器瘦身:`index.html` 删掉 `<h3>我的地图</h3><div id="my-levels">`;`js/editor.js` 删除 `renderMyLevels()` 及其 4 处调用(init/show/save/pickImport)。导入地图弹窗 `#ed-importbox` 成为唯一入口,`updateSaveState()`(保存去向提示)保留。
|
||||
- 自制页管理(`js/ui.js`):
|
||||
- `UI.manage` 状态;`#manage-btn`(放在 `#scr-levels` 的 panel-head,`hidden` 类控制只在「自制」分类显示)。
|
||||
- `setManage(on)` 切管理态(按钮文案 管理↔完成 + `.on` 高亮 + 重绘);`show('scr-levels')` 与切 tab 都会 `setManage(false)` 复位。
|
||||
- `renderLevels()`:管理态且 `builtin===false` 时给卡片加 `.manage` 类 + 追加 `.lv-del`(✕)按钮,且**不给卡片绑 play**(避免误点开始);`delCustom(l)` = confirm → `Save.removeCustom(id)` → `renderLevels()`。
|
||||
- `confirm` 用 `typeof confirm === 'function' &&` 守卫以便 Node 测试。
|
||||
- `js/main.js`:`UI.init` 加 `manage: () => UI.setManage(!UI.manage)`。
|
||||
- `css/style.css`:`.head-btns/.head-btns .btn.on`、`.lv-card.manage`(cursor:default、禁 hover 位移)、`.lv-del`(右上角红色 ✕)。
|
||||
- 验证:Node 桩冒烟(`/tmp/ed_manage.mjs`,18 断言:管理按钮仅自制页可见、管理态渲染 ✕、删除指定图、confirm 取消不删、退出复位、切分类隐藏按钮并复位、管理态点卡不开始);编辑器 22 断言 + 暂停 5 断言仍过;`death/map/check/solve/smoke/save/key/dom-smoke/ice` 九套全绿;`grep renderMyLevels|my-levels` 无残留。
|
||||
|
||||
## 修复:管理按钮在其他分类仍显示 + 文案改「管理地图」(用户要求)
|
||||
- 需求复述:①「管理地图」按钮**只在自制分类显示**,其他分类一律不显示;②按钮文字改成「管理地图」。
|
||||
- **根因**:CSS 里只有 `.screen.hidden` / `#hud.hidden` / `.modal.hidden` / `#touch.hidden` 这类限定选择器,**没有通用 `.hidden{display:none}` 规则**。`js/ui.js` 给 `#manage-btn`(class=`btn tiny hidden`)加 `hidden` 类根本匹配不上任何规则 → 按钮在全部/跳刺/耐久/BOSS/坑向分类都照样显示。JS 逻辑(`classList.toggle('hidden', filter!=='custom')`)一直是对的,纯 CSS 漏规则。
|
||||
- 修复:
|
||||
- `css/style.css`:新增通用 `.hidden{display:none !important}`(放在 `.screen.hidden` 附近)。所有 `hidden` 类使用场景(screens/hud/boss-bar/modal/touch/manage-btn)语义一致,无副作用。
|
||||
- `index.html`:按钮文案 `管理` → `管理地图`。
|
||||
- `js/ui.js`:`renderLevels`/`setManage` 里重复的按钮刷新逻辑收敛为新方法 `updateManageBtn()`(hidden + 文案 + .on 一处维护,防漂移);`setManage(on)` 语义改为「非自制分类的开启请求按退出处理」(按钮不可见时必须自动复位管理态,不能 return 留下隐藏的激活态)。
|
||||
- 教训:**JS 层 class 断言测试测不出 CSS 漏规则**(class 加上了但样式不存在)。已在 `/tmp/ed_manage.mjs` 增加 CSS 回归断言:正则检查 `css/style.css` 存在通用 `.hidden{...display:none...}` 规则。
|
||||
- 验证:`/tmp/ed_manage.mjs` 24 断言全过(含 CSS 回归、setManage 非法请求复位、show 复位、文案切换);其余 3 个编辑器 harness(save_import 22 / pause_quit 5 / noclip_spike 13 / props 14)仍全过;`death/map/check/solve/smoke/save/key/dom-smoke/ice` 九套全绿;预览 8123 返回 200 且按钮文案/规则已生效。
|
||||
@@ -59,6 +59,7 @@ canvas#game{
|
||||
}
|
||||
.screen.hidden{display:none}
|
||||
#hud.hidden,#boss-bar.hidden{display:none}
|
||||
.hidden{display:none !important} /* 通用:任何元素加 hidden 类即隐藏(如自制页「管理地图」按钮) */
|
||||
|
||||
.title-wrap{text-align:center;margin-bottom:1vh}
|
||||
.title-sub{color:var(--dim);letter-spacing:6px;font-size:.75em}
|
||||
@@ -92,6 +93,8 @@ canvas#game{
|
||||
}
|
||||
.panel.narrow{width:min(400px,92%)}
|
||||
.panel-head{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);padding-bottom:6px}
|
||||
.head-btns{display:flex;gap:6px;align-items:center}
|
||||
.head-btns .btn.on{background:var(--red);border-color:var(--red2);color:#fff}
|
||||
.panel h2{font-size:1.3em;letter-spacing:3px}
|
||||
.panel h3{font-size:1em;letter-spacing:2px;color:var(--gold);margin-bottom:5px}
|
||||
|
||||
@@ -117,6 +120,15 @@ canvas#game{
|
||||
.lv-card .st{margin-top:5px;font-size:.78em;color:var(--dim)}
|
||||
.lv-card.cleared{border-color:#2f6b45}
|
||||
.lv-card .chk{position:absolute;right:7px;top:6px;color:var(--green)}
|
||||
/* 自制页管理态:删除按钮 */
|
||||
.lv-card.manage{cursor:default;border-color:var(--red2)}
|
||||
.lv-card.manage:hover{transform:none;border-color:var(--red);background:#241a22}
|
||||
.lv-del{
|
||||
position:absolute;right:6px;top:6px;width:22px;height:22px;padding:0;
|
||||
background:#3a1420;border:1px solid var(--red2);color:var(--red);
|
||||
font-family:inherit;font-size:.8em;line-height:1;cursor:pointer;border-radius:2px;
|
||||
}
|
||||
.lv-del:hover{background:var(--red);color:#fff;border-color:var(--red)}
|
||||
.tag{padding:1px 6px;border:1px solid;font-size:.85em}
|
||||
.t-jump{color:#ff8a5c;border-color:#ff8a5c}
|
||||
.t-endur{color:#4ad0ff;border-color:#4ad0ff}
|
||||
@@ -187,6 +199,25 @@ canvas#ed-canvas{
|
||||
.prop-in{background:#111119;border:1px solid var(--line);color:var(--fg);font-family:inherit;font-size:1em;padding:3px 4px;width:100%}
|
||||
.prop-ctl .prop-in{width:auto;flex:1}
|
||||
|
||||
/* 「保存」去向提示 */
|
||||
.save-state{color:var(--dim);font-size:.78em;white-space:nowrap;align-self:center}
|
||||
.save-state.on{color:var(--gold)}
|
||||
|
||||
/* 导入地图弹窗 */
|
||||
.modal{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;background:rgba(4,4,10,.72);padding:4vh 4vw}
|
||||
.modal.hidden{display:none}
|
||||
.modal-panel{background:var(--bg2);border:2px solid var(--line);width:min(380px,94%);max-height:86vh;display:flex;flex-direction:column;gap:8px;padding:14px}
|
||||
.modal-panel h3{font-size:1.05em;letter-spacing:2px;color:var(--gold)}
|
||||
.modal-tip{color:var(--dim);font-size:.78em;line-height:1.5}
|
||||
#ed-import-list{display:flex;flex-direction:column;gap:4px;overflow-y:auto}
|
||||
.imp-row{background:#1b1b26;border:1px solid var(--line);padding:6px 8px;font-size:.84em;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:6px}
|
||||
.imp-row:hover{border-color:var(--gold)}
|
||||
.imp-row.on{border-color:var(--gold);background:#2b2b3c}
|
||||
.imp-nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.imp-meta{color:var(--dim);font-size:.9em;white-space:nowrap}
|
||||
.imp-empty{color:var(--dim);font-size:.8em;padding:10px 2px}
|
||||
.modal-btns{display:flex;justify-content:flex-end;gap:6px}
|
||||
|
||||
::-webkit-scrollbar{width:8px;height:8px}
|
||||
::-webkit-scrollbar-track{background:#111}
|
||||
::-webkit-scrollbar-thumb{background:#333;}
|
||||
|
||||
+17
-3
@@ -48,7 +48,10 @@
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>关卡选择</h2>
|
||||
<button class="btn tiny" data-act="back">返回</button>
|
||||
<div class="head-btns">
|
||||
<button class="btn tiny hidden" id="manage-btn" data-act="manage">管理地图</button>
|
||||
<button class="btn tiny" data-act="back">返回</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs" id="level-tabs"></div>
|
||||
<div class="level-grid" id="level-grid"></div>
|
||||
@@ -148,8 +151,10 @@
|
||||
<button class="btn tiny" data-act="ed-resize">调整</button>
|
||||
<button class="btn tiny" data-act="ed-fill">铺底</button>
|
||||
<button class="btn tiny" data-act="ed-clear">清空</button>
|
||||
<button class="btn tiny" data-act="ed-import">导入地图</button>
|
||||
<button class="btn tiny" data-act="ed-test">试玩</button>
|
||||
<button class="btn tiny go" data-act="ed-save">保存</button>
|
||||
<span id="ed-savestate" class="save-state">→ 新地图</span>
|
||||
</div>
|
||||
<canvas id="ed-canvas" width="800" height="608"></canvas>
|
||||
<div class="editor-tip">左键放置 / 右键擦除,滚轮水平平移。<b>不会覆盖已有方块</b>——放在空格才生效,要换掉先擦除。选「⌖ 查看属性」再点方块可只查看不修改。<b>刺</b>只有一个,放好后在右侧「属性」面板调<b>方向(上/下/左/右)</b>;传送门 <b>W</b>、提示牌 <b>T</b>、炮台 <b>O</b>、摄像头 <b>C</b>、BOSS <b>B</b> 的属性也都在这里设置。每面至少 1 个 <b>S</b>(出生点) 与 1 个 <b>X</b>(终点)。</div>
|
||||
@@ -157,8 +162,6 @@
|
||||
<div class="editor-side">
|
||||
<h3>属性(选中方块)</h3>
|
||||
<div id="ed-props"><div class="prop-empty">点击画布上的方块,查看并设置它的属性</div></div>
|
||||
<h3>我的地图</h3>
|
||||
<div id="my-levels"></div>
|
||||
<h3>面(多面地图)</h3>
|
||||
<div class="face-btns">
|
||||
<button class="btn tiny" data-act="ed-addface">+ 新增面</button>
|
||||
@@ -168,6 +171,17 @@
|
||||
<div id="ed-faces"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导入地图选择 -->
|
||||
<div id="ed-importbox" class="modal hidden">
|
||||
<div class="modal-panel">
|
||||
<h3>导入地图</h3>
|
||||
<p class="modal-tip">从以前保存过的地图里选一个载入编辑器(会替换当前编辑内容)</p>
|
||||
<div id="ed-import-list"></div>
|
||||
<div class="modal-btns">
|
||||
<button class="btn tiny" data-act="ed-import-cancel">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+57
-37
@@ -65,7 +65,6 @@ export const Editor = {
|
||||
this.bind();
|
||||
$('#ed-w').addEventListener('change', () => this.resize(+$('#ed-w').value, +$('#ed-h').value));
|
||||
$('#ed-h').addEventListener('change', () => this.resize(+$('#ed-w').value, +$('#ed-h').value));
|
||||
this.renderMyLevels();
|
||||
},
|
||||
|
||||
buildPalette() {
|
||||
@@ -133,6 +132,8 @@ export const Editor = {
|
||||
on('ed-clear', () => { if (typeof confirm === 'function' && !confirm('清空当前面?')) return; const f = this.curFace(); f.grid = blank(f.w, f.h); f.doors = []; f.props = propsBlank(f.w, f.h); this.selTile = null; this.draw(); this.renderProps(); });
|
||||
on('ed-test', () => this.test());
|
||||
on('ed-save', () => this.save());
|
||||
on('ed-import', () => this.openImport());
|
||||
on('ed-import-cancel', () => this.closeImport());
|
||||
on('ed-addface', () => this.addFace());
|
||||
on('ed-copyface', () => this.copyFace());
|
||||
on('ed-delface', () => this.delFace());
|
||||
@@ -142,18 +143,33 @@ export const Editor = {
|
||||
curFace() { return this.faces[this.face]; },
|
||||
|
||||
open() {
|
||||
if (!this.faces) {
|
||||
const w = +$('#ed-w').value || 25, h = +$('#ed-h').value || 19;
|
||||
this.faces = [{ name: '面1', w, h, seq: 0, grid: blank(w, h), doors: [], props: propsBlank(w, h) }];
|
||||
this.face = 0;
|
||||
}
|
||||
// 从菜单进入编辑器:始终是新地图(宽高沿用输入框里的值)
|
||||
const w = +$('#ed-w').value || 25, h = +$('#ed-h').value || 19;
|
||||
this.faces = [{ name: '面1', w, h, seq: 0, grid: blank(w, h), doors: [], props: propsBlank(w, h) }];
|
||||
this.face = 0;
|
||||
this.cur = null; // 未关联任何已存地图 → 首次保存为新建
|
||||
this.show();
|
||||
},
|
||||
|
||||
/** 仅重绘当前状态(不从零重置):用于试玩后「返回编辑器」等场景,保留正在编辑的地图 */
|
||||
show() {
|
||||
if (!this.faces) { this.open(); return; }
|
||||
this.selTile = null;
|
||||
$('#ed-w').value = this.curFace().w;
|
||||
$('#ed-h').value = this.curFace().h;
|
||||
this.draw();
|
||||
this.renderFaces();
|
||||
this.renderProps();
|
||||
this.renderMyLevels();
|
||||
this.updateSaveState();
|
||||
},
|
||||
|
||||
/** 「保存」按钮旁的提示:会更新哪张图,还是另存为新图 */
|
||||
updateSaveState() {
|
||||
const el = $('#ed-savestate');
|
||||
if (!el) return;
|
||||
const l = this.cur ? Save.customs().find(o => o.id === this.cur) : null;
|
||||
el.textContent = l ? '→ 更新「' + (l.name || 'MY MAP') + '」' : '→ 另存为新图';
|
||||
el.classList.toggle('on', !!l);
|
||||
},
|
||||
|
||||
resize(w, h) {
|
||||
@@ -408,41 +424,45 @@ export const Editor = {
|
||||
save() {
|
||||
const err = this.validate();
|
||||
if (err) { alert(err); return; }
|
||||
const lv = makeCustomLevel(this.buildData());
|
||||
Save.addCustom(lv);
|
||||
let lv = makeCustomLevel(this.buildData());
|
||||
if (this.cur) lv.id = this.cur; // 保存当前地图:沿用已有 id,就地更新
|
||||
Save.updateCustom(lv);
|
||||
this.cur = lv.id;
|
||||
this.renderMyLevels();
|
||||
this.updateSaveState();
|
||||
this.api.refresh?.();
|
||||
},
|
||||
|
||||
// ---------------- 我的关卡 ----------------
|
||||
renderMyLevels() {
|
||||
const box = $('#my-levels');
|
||||
// ---------------- 导入地图(从已保存的地图里选) ----------------
|
||||
openImport() {
|
||||
const box = $('#ed-importbox');
|
||||
if (!box) return;
|
||||
box.innerHTML = '';
|
||||
const list = Save.customs();
|
||||
if (!list.length) box.innerHTML = '<div style="color:#666;font-size:.8em">还没有保存的关卡</div>';
|
||||
list.forEach((l, i) => {
|
||||
const d = document.createElement('div');
|
||||
const fn = (l.faces || []).length;
|
||||
d.className = 'my-lv' + (l.id === this.cur ? ' on' : '');
|
||||
d.innerHTML = `<span>${i + 1}. ${escapeHtml(l.name)}${fn ? ` <b style="color:#a86bff">[${fn}面]</b>` : ''}</span><span class="x">✕</span>`;
|
||||
d.querySelector('span').onclick = () => {
|
||||
this.cur = l.id;
|
||||
this.hydrate(l);
|
||||
$('#ed-name').value = l.name || '';
|
||||
this.afterFaceSwitch();
|
||||
this.renderMyLevels();
|
||||
};
|
||||
d.querySelector('.x').onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
if (typeof confirm === 'function' && !confirm('删除 ' + l.name + '?')) return;
|
||||
Save.removeCustom(l.id);
|
||||
this.renderMyLevels();
|
||||
this.api.refresh?.();
|
||||
};
|
||||
box.appendChild(d);
|
||||
});
|
||||
const list = $('#ed-import-list');
|
||||
const customs = Save.customs();
|
||||
list.innerHTML = '';
|
||||
if (!customs.length) {
|
||||
list.innerHTML = '<div class="imp-empty">还没有保存过的地图,先「保存」一个吧</div>';
|
||||
} else {
|
||||
customs.forEach(l => {
|
||||
const fn = (l.faces || []).length;
|
||||
const d = document.createElement('div');
|
||||
d.className = 'imp-row' + (l.id === this.cur ? ' on' : '');
|
||||
d.innerHTML = `<span class="imp-nm">${escapeHtml(l.name || 'MY MAP')}</span><span class="imp-meta">${fn ? fn + ' 面' : '单关'}</span>`;
|
||||
d.onclick = () => this.pickImport(l.id);
|
||||
list.appendChild(d);
|
||||
});
|
||||
}
|
||||
box.classList.remove('hidden');
|
||||
},
|
||||
closeImport() { $('#ed-importbox')?.classList.add('hidden'); },
|
||||
pickImport(id) {
|
||||
const l = Save.customs().find(o => o.id === id);
|
||||
if (!l) return;
|
||||
this.cur = l.id;
|
||||
this.hydrate(l);
|
||||
$('#ed-name').value = l.name || '';
|
||||
this.closeImport();
|
||||
this.afterFaceSwitch();
|
||||
this.updateSaveState();
|
||||
},
|
||||
|
||||
// 从存档/数据对象恢复多面 + 每格属性(按扫描顺序回填 messages/turrets/camzones/bosses)
|
||||
|
||||
+8
-1
@@ -169,7 +169,13 @@ function nextLevel() {
|
||||
}
|
||||
function toLevels() { G.state = 'menu'; UI.show('scr-levels'); Touch.hide(); }
|
||||
function toTitle() { G.state = 'menu'; UI.show('scr-title'); Touch.hide(); }
|
||||
function toEditor() { G.state = 'editor'; UI.show('scr-editor'); Touch.hide(); Editor.open(); }
|
||||
function toEditor() {
|
||||
G.state = 'editor';
|
||||
UI.show('scr-editor');
|
||||
Touch.hide();
|
||||
// 试玩后返回:保留正在编辑的地图;从未进过编辑器时才新建
|
||||
if (Editor.faces) Editor.show(); else Editor.open();
|
||||
}
|
||||
function pause() {
|
||||
if (G.state !== 'playing') return;
|
||||
G.state = 'paused';
|
||||
@@ -289,6 +295,7 @@ Audio2.enabled = Save.data.audio !== false;
|
||||
UI.init({
|
||||
play: (lv) => { G.fromEditor = false; startLevel(lv, lv.builtin === false ? -1 : (lv.index ?? -1)); },
|
||||
levels: toLevels,
|
||||
manage: () => UI.setManage(!UI.manage),
|
||||
help: () => UI.show('scr-help'),
|
||||
editor: toEditor,
|
||||
back: toTitle,
|
||||
|
||||
@@ -42,6 +42,15 @@ export const Save = {
|
||||
this.flush();
|
||||
return lv;
|
||||
},
|
||||
/** 更新已保存的自制关卡(按 id 就地替换,不新增);id 不存在则追加 */
|
||||
updateCustom(lv) {
|
||||
this.data.customs = this.data.customs || [];
|
||||
const i = this.data.customs.findIndex(l => l.id === lv.id);
|
||||
if (i >= 0) this.data.customs[i] = lv;
|
||||
else this.data.customs.push(lv);
|
||||
this.flush();
|
||||
return lv;
|
||||
},
|
||||
removeCustom(id) {
|
||||
this.data.customs = (this.data.customs || []).filter(l => l.id !== id);
|
||||
this.flush();
|
||||
|
||||
@@ -14,6 +14,7 @@ const TABS = [
|
||||
export const UI = {
|
||||
H: {}, // handlers
|
||||
filter: 'all',
|
||||
manage: false, // 自制页「管理」态:开启后每张自制地图显示删除按钮
|
||||
cur: 'scr-title',
|
||||
toastTimer: 0,
|
||||
|
||||
@@ -37,6 +38,7 @@ export const UI = {
|
||||
b.textContent = label;
|
||||
b.onclick = () => {
|
||||
this.filter = k;
|
||||
this.setManage(false); // 切换分类时退出管理模式
|
||||
$$('#level-tabs .tab').forEach(t => t.classList.remove('on'));
|
||||
b.classList.add('on');
|
||||
this.renderLevels();
|
||||
@@ -49,7 +51,7 @@ export const UI = {
|
||||
SCREENS.forEach(s => $('#' + s).classList.toggle('hidden', s !== name));
|
||||
this.cur = name;
|
||||
$('#hud').classList.toggle('hidden', name !== null && name !== '');
|
||||
if (name === 'scr-levels') this.renderLevels();
|
||||
if (name === 'scr-levels') { this.setManage(false); this.renderLevels(); }
|
||||
},
|
||||
showGame() {
|
||||
SCREENS.forEach(s => $('#' + s).classList.add('hidden'));
|
||||
@@ -64,6 +66,9 @@ export const UI = {
|
||||
const all = [...LEVELS, ...customs];
|
||||
const list = all.filter(l => this.filter === 'all' ? true : l.type === this.filter);
|
||||
|
||||
// 「管理地图」按钮只在自制分类下显示
|
||||
this.updateManageBtn();
|
||||
|
||||
if (!list.length) {
|
||||
grid.innerHTML = '<div style="color:#666;padding:20px">这个分类下还没有关卡。去「自制关卡」造一个吧。</div>';
|
||||
return;
|
||||
@@ -71,7 +76,7 @@ export const UI = {
|
||||
list.forEach(l => {
|
||||
const rec = Save.get(l.id);
|
||||
const card = document.createElement('div');
|
||||
card.className = 'lv-card' + (rec ? ' cleared' : '');
|
||||
card.className = 'lv-card' + (rec ? ' cleared' : '') + (this.manage && l.builtin === false ? ' manage' : '');
|
||||
const stars = '★'.repeat(l.diff || 1) + '☆'.repeat(Math.max(0, 5 - (l.diff || 1)));
|
||||
card.innerHTML = `
|
||||
<div class="idx">${l.builtin === false ? 'U' + (l.no || '') : String(l.index + 1).padStart(2, '0')}</div>
|
||||
@@ -83,11 +88,46 @@ export const UI = {
|
||||
<div class="st">${rec ? `✔ 死亡 ${rec.deaths} · ${fmtTime(rec.time)}` : '未通关'}</div>
|
||||
${rec ? '<div class="chk">✔</div>' : ''}
|
||||
`;
|
||||
card.onclick = () => this.H.play(l);
|
||||
// 管理态下自制卡不再可进入,只保留删除按钮,避免误点开始
|
||||
if (!(this.manage && l.builtin === false)) card.onclick = () => this.H.play(l);
|
||||
// 管理态:自制地图显示删除按钮
|
||||
if (l.builtin === false && this.manage) {
|
||||
const del = document.createElement('button');
|
||||
del.className = 'lv-del';
|
||||
del.textContent = '✕';
|
||||
del.title = '删除这张地图';
|
||||
del.onclick = (e) => { e.stopPropagation(); this.delCustom(l); };
|
||||
card.appendChild(del);
|
||||
}
|
||||
grid.appendChild(card);
|
||||
});
|
||||
},
|
||||
|
||||
/** 刷新「管理地图」按钮:仅在自制分类可见;管理态文案变「完成」并高亮 */
|
||||
updateManageBtn() {
|
||||
const mb = $('#manage-btn');
|
||||
if (!mb) return;
|
||||
mb.classList.toggle('hidden', this.filter !== 'custom');
|
||||
mb.textContent = this.manage ? '完成' : '管理地图';
|
||||
mb.classList.toggle('on', this.manage);
|
||||
},
|
||||
|
||||
/** 切换自制页的管理态(显示/隐藏删除按钮) */
|
||||
setManage(on) {
|
||||
// 仅自制分类可进入管理态;非自制分类的开启请求按退出处理(按钮不可见,须自动复位)
|
||||
if (on && this.filter !== 'custom') on = false;
|
||||
this.manage = !!on;
|
||||
this.updateManageBtn();
|
||||
this.renderLevels();
|
||||
},
|
||||
|
||||
/** 删除一张自制地图(从关卡选择界面管理) */
|
||||
delCustom(l) {
|
||||
if (typeof confirm === 'function' && !confirm('删除地图「' + (l.name || 'MY MAP') + '」?此操作不可恢复')) return;
|
||||
Save.removeCustom(l.id);
|
||||
this.renderLevels();
|
||||
},
|
||||
|
||||
hud({ name, deaths, ms, apples, totalApples, hasGun, save, totalSaves }) {
|
||||
$('#hud-level').textContent = name || '';
|
||||
$('#hud-deaths').textContent = 'DEATH ' + deaths;
|
||||
|
||||
Reference in new issue
Block a user