// 经典针刺阵型库(IWBT 风格):所有函数基于 Grid,坐标单位 = 格 import { Grid } from './kit.js'; // 上下对刺夹缝(横向穿越 1 格高的缝):缝在行 y。 // 自动清理两侧地毯刺作起跳/落点(否则有效跳距翻倍,无解)。 export function vgap(m, x, y) { m.set(x, y - 1, 'v'); m.set(x, y + 1, '^'); m.set(x - 1, y + 1, '.'); m.set(x + 1, y + 1, '.'); m.set(x - 2, y + 1, '.'); m.set(x + 2, y + 1, '.'); } // 左右对刺夹缝(竖向穿越 1 格宽的缝):缝在列 x,自动清理两侧落点。 export function hgap(m, x, y) { m.set(x - 1, y, '>'); m.set(x + 1, y, '<'); m.set(x - 2, y, '.'); m.set(x + 2, y, '.'); } // 钻石阵:中心空格 + 四向刺(经典 diamond jump) // 注意:矩形判定盒下,完整钻石的横向穿越窗口只有几像素—— // 作为"跳穿门"请用 vgap/hgap;diamond 只用作翻越/绕行的障碍或装饰。 export function diamond(m, x, y) { vgap(m, x, y); hgap(m, x, y); } // 一排刺,gaps 为留空偏移(相对 x0) export function spikeRun(m, x0, y, n, ch = '^', gaps = []) { for (let i = 0; i < n; i++) if (!gaps.includes(i)) m.set(x0 + i, y, ch); } // 悬浮块,顶面铺刺(不可站)/ 或纯块 export function block(m, x, y, w = 1, h = 1, ch = '#') { m.rect(x, y, w, h, ch); } // 悬浮块 + 顶刺 export function spikedBlock(m, x, y, w = 1) { m.rect(x, y, w, 1, '#'); spikeRun(m, x, y - 1, w, '^'); } // 天花板垂刺 export function ceil(m, x0, x1, y) { m.hline(y, x0, x1, 'v'); } // 墙刺竖段 export function wall(m, x, y0, y1, ch) { m.vline(x, y0, y1, ch); } // 角刺块:w×h 实心块,某个外角带 L 形双刺(经典 corner) // dir: 'tl' 左上角 / 'tr' 右上 / 'bl' 左下 / 'br' 右下 export function corner(m, x, y, w, h, dir) { m.rect(x, y, w, h, '#'); if (dir === 'tl') { m.set(x, y - 1, '^'); m.set(x - 1, y, '>'); } if (dir === 'tr') { m.set(x + w - 1, y - 1, '^'); m.set(x + w, y, '<'); } if (dir === 'bl') { m.set(x, y + h, 'v'); m.set(x - 1, y + h - 1, '>'); } if (dir === 'br') { m.set(x + w - 1, y + h, 'v'); m.set(x + w, y + h - 1, '<'); } } // 之字踏石竖井(可解性已用 tools/solve.mjs 验证) // xL..xR 为井内列;topRow = 上层地板行,botRow = 下层地板行(差须为偶数且 ≥4) // floorSide: 'l' 上层地板在井的左侧(井在右侧)/ 'r' 反之 // 规则: // 1) 踏石每级升 2 格(单跳 90px > 64px),左右交替,宽度 w 使两级有 2 格重叠 // —— overlap 让"上升后横移"只需 1 格左右,避免横向距离超过滞空距离。 // 2) 靠地板一侧的井壁从 topRow+1 起到 botRow-4,底部留 3 格入口。 // 3) 顶台位于 topRow 且与上层地板紧邻(同高相接),不存在"洞口被顶台封死"。 export function well(m, xL, xR, topRow, botRow, floorSide = 'l', opts = {}) { const w = opts.w || 5; let i = 0; for (let r = topRow; r <= botRow - 2; r += 2, i++) { const onLeft = (i % 2 === 0) === (floorSide === 'l'); m.rect(onLeft ? xL : xR - w + 1, r, w, 1, '#'); } const floorX = floorSide === 'l' ? xL - 1 : xR + 1; const backX = floorSide === 'l' ? xR + 1 : xL - 1; m.vline(backX, topRow, botRow, '#'); m.vline(floorX, topRow + 1, botRow - 4, '#'); return m; } // 竖井:在 (x0..x1) 之间开竖直通道,交替放小平台,两侧可布墙刺 // plats: [{y, side:'l'|'r', w}],spikeEvery: 每隔几行在对面墙上放刺 export function shaft(m, x0, x1, yTop, yBot, plats, opts = {}) { for (const p of plats) { const px = p.side === 'l' ? x0 : x1 - p.w + 1; m.rect(px, p.y, p.w, 1, '#'); if (p.spike) spikeRun(m, px, p.y - 1, p.w, '^', p.spikeGaps || []); } if (opts.wallSpikes) { const { side = 'r', every = 3, from = yTop + 1, to = yBot - 1 } = opts.wallSpikes; for (let y = from; y <= to; y += every) { if (plats.some(p => Math.abs(p.y - y) <= 1)) continue; m.set(side === 'r' ? x1 : x0, y, side === 'r' ? '<' : '>'); } } } export { Grid };