109 lines
4.7 KiB
HTML
109 lines
4.7 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>关卡地图预览 · IWBT C 同人</title>
|
||
<style>
|
||
:root { --bg:#0e1018; --panel:#171b26; --line:#2a3040; --fg:#dfe3ee; --dim:#8b93a7; }
|
||
* { box-sizing: border-box; }
|
||
body { margin:0; background:var(--bg); color:var(--fg); font:14px/1.6 ui-sans-serif,system-ui,"PingFang SC","Microsoft YaHei",sans-serif; }
|
||
header { padding:14px 18px; border-bottom:1px solid var(--line); display:flex; gap:14px; align-items:center; flex-wrap:wrap; }
|
||
h1 { font-size:15px; font-weight:500; margin:0 14px 0 0; }
|
||
select, button { background:var(--panel); color:var(--fg); border:1px solid var(--line); border-radius:6px; padding:5px 10px; font:inherit; }
|
||
.stat { color:var(--dim); font-size:13px; }
|
||
.stat b { color:var(--fg); font-weight:500; }
|
||
main { padding:14px 18px 30px; overflow:auto; }
|
||
canvas { display:block; background:var(--panel); border:1px solid var(--line); border-radius:6px; image-rendering:pixelated; }
|
||
.legend { margin-top:12px; display:flex; gap:18px; flex-wrap:wrap; color:var(--dim); font-size:13px; }
|
||
.legend i { display:inline-block; width:12px; height:12px; border-radius:2px; vertical-align:-2px; margin-right:5px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>关卡地图预览</h1>
|
||
<select id="sel"></select>
|
||
<span class="stat" id="stat"></span>
|
||
</header>
|
||
<main><canvas id="cv"></canvas></main>
|
||
<div class="legend">
|
||
<span><i style="background:#3a4056"></i>实心块</span>
|
||
<span><i style="background:#5b7fa6"></i>冰面</span>
|
||
<span><i style="background:#6b5b8a"></i>单向台</span>
|
||
<span><i style="background:#2d5a7a"></i>水</span>
|
||
<span><i style="background:#e04b6a"></i>四向刺</span>
|
||
<span><i style="background:#8b3ad0"></i>即死块</span>
|
||
<span><i style="background:#3ad07a"></i>出生点</span>
|
||
<span><i style="background:#4bc8ff"></i>存档点</span>
|
||
<span><i style="background:#ffd24b"></i>终点 / 苹果</span>
|
||
<span><i style="background:#c48bff"></i>手枪·跳台·炮台·BOSS·掉落刺</span>
|
||
</div>
|
||
|
||
<script type="module">
|
||
import { LEVELS } from './js/levels/index.js';
|
||
|
||
const COLORS = {
|
||
'#': '#3a4056', '@': '#3a4056', '=': '#3a4056', 'M': '#4a4f63',
|
||
'%': '#5b7fa6', '-': '#6b5b8a', '~': '#2d5a7a', '*': '#8b3ad0', 'V': '#4a4f63',
|
||
'^': '#e04b6a', 'v': '#e04b6a', '<': '#e04b6a', '>': '#e04b6a',
|
||
S: '#3ad07a', G: '#4bc8ff', X: '#ffd24b', F: '#ffd24b',
|
||
P: '#c48bff', J: '#c48bff', O: '#c48bff', B: '#c48bff', D: '#c48bff',
|
||
W: '#c48bff', T: '#8b93a7', C: '#8b93a7', '!': '#8b93a7',
|
||
};
|
||
|
||
const sel = document.getElementById('sel');
|
||
const stat = document.getElementById('stat');
|
||
const cv = document.getElementById('cv');
|
||
const ctx = cv.getContext('2d');
|
||
|
||
LEVELS.forEach((l, i) => {
|
||
const o = document.createElement('option');
|
||
o.value = i;
|
||
o.textContent = `${l.id} ${l.name} ${l.cn || ''}`;
|
||
sel.appendChild(o);
|
||
});
|
||
|
||
function draw(d) {
|
||
const rows = d.grid, h = rows.length, w = d.width;
|
||
const s = Math.max(4, Math.min(16, Math.floor(Math.min(1500 / w, 820 / h))));
|
||
cv.width = w * s; cv.height = h * s;
|
||
cv.style.width = cv.width + 'px';
|
||
cv.style.height = cv.height + 'px';
|
||
ctx.clearRect(0, 0, cv.width, cv.height);
|
||
let spike = 0;
|
||
for (let y = 0; y < h; y++) {
|
||
const line = rows[y] || '';
|
||
for (let x = 0; x < w; x++) {
|
||
const ch = line[x] || '.';
|
||
if (ch === '.') continue;
|
||
if ('^v<>'.includes(ch)) spike++;
|
||
const c = COLORS[ch];
|
||
if (!c) continue;
|
||
ctx.fillStyle = c;
|
||
if ('^v<>'.includes(ch)) { // 刺画成三角形,方向和判定一致
|
||
ctx.beginPath();
|
||
const px = x * s, py = y * s;
|
||
if (ch === '^') { ctx.moveTo(px + s / 2, py + 2); ctx.lineTo(px + s - 2, py + s - 1); ctx.lineTo(px + 2, py + s - 1); }
|
||
if (ch === 'v') { ctx.moveTo(px + 2, py + 1); ctx.lineTo(px + s - 2, py + 1); ctx.lineTo(px + s / 2, py + s - 2); }
|
||
if (ch === '<') { ctx.moveTo(px + 1, py + s / 2); ctx.lineTo(px + s - 1, py + 2); ctx.lineTo(px + s - 1, py + s - 2); }
|
||
if (ch === '>') { ctx.moveTo(px + 1, py + 2); ctx.lineTo(px + s - 1, py + 1); ctx.lineTo(px + 1, py + s - 2); }
|
||
ctx.closePath(); ctx.fill();
|
||
} else if ('SGXFPJODBW'.includes(ch)) {
|
||
ctx.beginPath();
|
||
ctx.arc(x * s + s / 2, y * s + s / 2, s * 0.3, 0, 7);
|
||
ctx.fill();
|
||
} else {
|
||
ctx.fillRect(x * s, y * s, s, s);
|
||
}
|
||
}
|
||
}
|
||
const total = w * h;
|
||
const saves = rows.join('').split('').filter(c => c === 'G').length;
|
||
stat.innerHTML = `<b>${d.name}</b> ${d.cn || ''} · ${w}×${h} 格 · 类型 ${d.type} · 难度 ${d.diff} · 刺 ${spike} 格(${(spike / total * 100).toFixed(1)}%)· 存档点 ${saves}`;
|
||
}
|
||
|
||
sel.addEventListener('change', () => draw(LEVELS[+sel.value]));
|
||
draw(LEVELS[0]);
|
||
</script>
|
||
</body>
|
||
</html>
|