96 lines
5.1 KiB
JavaScript
96 lines
5.1 KiB
JavaScript
// 二段跳时机回归(tools/dj-test.mjs)
|
||
// 历史 bug 链:
|
||
// ① 全额抵消(旧 `vy = max(vy,0) - DJUMP`):满力一段到顶点后晚几帧接二段,二段塌到接近 0(用户报"二段非常短")
|
||
// ② 部分抵消(DJ_INERTIA 0.5→0.35):仍有"好沉+高度短"——起跳初速被下落速度从 6.7 吃到 3~4,
|
||
// 起跳那一下没劲才是"沉"的根源,与上升重力无关
|
||
// ③ 现行(DJ_INERTIA = 0,同 IWBT 原版):任何时机接二段都满力起跳,初速恒 -DJUMP
|
||
// 本测试用真实引擎测量:不同时机接二段时,二段自身的提升高度(djRise)。
|
||
// 通过标准:
|
||
// · 任意时机接二段都 ≈ 满力(≈60px ≈ 1.9 格)——INERTIA=0 语义,曲线应基本持平
|
||
// · 满力一段 ≈ 84px(≈2.6 格);顶点接二段总高 ≈ 4.5 格
|
||
const handlers = {};
|
||
const addL = (t, fn) => { (handlers[t] = handlers[t] || []).push(fn); };
|
||
globalThis.addEventListener = addL;
|
||
globalThis.window = { addEventListener: addL };
|
||
const key = (t, c) => (handlers[t] || []).forEach(fn => fn({ code: c, preventDefault() { }, repeat: false }));
|
||
|
||
const { Level } = await import('../js/level.js');
|
||
const { Player } = await import('../js/player.js');
|
||
const { Input } = await import('../js/input.js');
|
||
|
||
// 与 jumpdist.mjs 同一块平地:row17 是实心平台
|
||
const flat = {
|
||
id: 'test', name: 'T', type: 'jump', width: 44,
|
||
grid: (() => {
|
||
const rows = [];
|
||
rows.push('#'.repeat(44));
|
||
for (let i = 0; i < 16; i++) rows.push('#' + '.'.repeat(42) + '#');
|
||
rows.push('#' + '#'.repeat(42) + '#');
|
||
rows.push('#'.repeat(44));
|
||
return rows;
|
||
})(),
|
||
};
|
||
|
||
// 满力一段跳(起跳后一直按住);第 djFrame 帧松键、djFrame+1 帧接二段(之后保持按住)
|
||
// 返回:firstRise 一段顶点提升 / djRise 二段自身提升(接二段位置→二段后最高点)/ total 二段顶点离平台高度
|
||
function measure(djFrame) {
|
||
const L = new Level(flat);
|
||
const P = new Player(L);
|
||
P.x = 8 * 32; P.y = 17 * 32 - P.h; // 站在 row17 实心平台上
|
||
P.vx = 0; P.vy = 0; P.grounded = true; P.jumps = 0;
|
||
const y0 = P.y;
|
||
key('keydown', 'ShiftLeft'); // 起跳并按住 = 满力一段
|
||
let apex1 = y0, apex2 = y0, djDone = false, vyAtDj = null, yAtDj = y0;
|
||
for (let i = 0; i < 240; i++) {
|
||
const f = i + 1;
|
||
if (f === djFrame) key('keyup', 'ShiftLeft');
|
||
if (f === djFrame + 1) { key('keydown', 'ShiftLeft'); vyAtDj = P.vy; yAtDj = P.y; }
|
||
L.update(1, P, null);
|
||
P.update(1);
|
||
Input.endFrame();
|
||
if (djFrame && f === djFrame + 1 && P.jumps === 2) djDone = true;
|
||
if (djDone) apex2 = Math.min(apex2, P.y);
|
||
else apex1 = Math.min(apex1, P.y);
|
||
if (i > 2 && P.grounded) break;
|
||
if (P.dead) break;
|
||
}
|
||
key('keyup', 'ShiftLeft');
|
||
Input.endFrame();
|
||
return { firstRise: y0 - apex1, djRise: djDone ? yAtDj - apex2 : 0, total: y0 - apex2, vyAtDj, djDone };
|
||
}
|
||
|
||
// 满力一段顶点≈第 27 帧;落地≈第 50 帧。取 27~46 帧覆盖"顶点 → 落地前"整个二段窗口
|
||
const FRAMES = [27, 30, 34, 38, 42, 46];
|
||
const T = 32;
|
||
let fail = 0;
|
||
const rows = [];
|
||
console.log('满力一段跳后,不同时机接二段:二段自身提升高度(真实引擎实测)');
|
||
console.log(' 时机 接二段时vy 一段顶点 二段自身提升 二段顶点离平台');
|
||
for (const f of FRAMES) {
|
||
const r = measure(f);
|
||
rows.push({ f, ...r });
|
||
const dj = r.djDone
|
||
? `${r.djRise.toFixed(1).padStart(6)}px = ${(r.djRise / T).toFixed(2)} 格`
|
||
: ' —— 已落地,二段窗口已过 ——';
|
||
console.log(` 第${String(f).padStart(2)}帧 ${(r.vyAtDj ?? 0).toFixed(2).padStart(5)} ${r.firstRise.toFixed(1).padStart(6)}px ${dj} ${(r.total / T).toFixed(2)} 格`);
|
||
}
|
||
|
||
const at = f => rows.find(r => r.f === f);
|
||
const need = (cond, msg) => { if (!cond) { fail++; console.log(' ❌ ' + msg); } };
|
||
|
||
for (const r of rows) need(r.djDone, `第 ${r.f} 帧应还在二段窗口内(未落地)`);
|
||
const apex = at(27);
|
||
need(apex.firstRise > 79 && apex.firstRise < 90, `满力一段高度应 ≈84px(2.6格),实测 ${apex.firstRise.toFixed(1)}`);
|
||
need(apex.djRise > 52 && apex.djRise < 68, `顶点接二段自身提升应 ≈59px(1.85格),实测 ${apex.djRise.toFixed(1)}`);
|
||
need(apex.total > 138 && apex.total < 150, `顶点接二段总高应 ≈4.5格,实测 ${(apex.total / T).toFixed(2)}`);
|
||
// INERTIA=0:任何时机接二段都满力起跳,曲线应基本持平(与顶点差异 ≤3px)
|
||
for (const r of rows) {
|
||
if (!r.f) continue;
|
||
need(r.djRise >= 55, `第 ${r.f} 帧接二段自身提升应 ≈满力(≥55px),实测 ${r.djRise.toFixed(1)}`);
|
||
need(Math.abs(r.djRise - apex.djRise) <= 3, `第 ${r.f} 帧与顶点接二段差异应 ≤3px(INERTIA=0 曲线持平),实测差 ${Math.abs(r.djRise - apex.djRise).toFixed(1)}`);
|
||
}
|
||
need(at(46).djRise >= 55, `即使摔得快二段也不应衰减(INERTIA=0),实测 ${at(46).djRise.toFixed(1)}`);
|
||
|
||
if (fail) { console.log(`\n❌ dj-test 失败 ${fail} 项`); process.exit(1); }
|
||
console.log('\n✅ dj-test 通过:任意时机接二段均满力起跳(INERTIA=0),顶点总高 ≈4.5 格');
|