Files

142 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-10-23 工作日志
## 首页移除「清除存档」按钮(用户要求)
- 需求:把首页(scr-title)的「清除存档」按钮删掉。
- 改动:仅删 `index.html:41` 的 `<button class="btn small" data-act="wipe">清除存档</button>` 一行。`js/main.js` 里的 `wipe` handler(确认后 `Save.wipe()`)已无按钮触发,成为未接线死代码,保留无害(用户只要求删按钮,未要求删逻辑)。
- 验证:`grep 清除存档/data-act="wipe"` 零残留;`tools/` 无引用;`death/map/check/solve/smoke/save/key/dom-smoke/ice` 九套回归全绿;预览 8123 返回 200。
## 主界面新增「设置」功能(用户要求)
- 需求:主界面加「设置」入口;可自定义用户名;可拖动调整手机操作按钮的位置与大小;点「确定」保存、有「恢复默认」按钮。
- 新增 `js/settings.js`:`BUTTONS`(7 个手柄键 left/right/jump/shoot/down/pause/suicide,各带默认 `cx,cy,size` 百分比+像素),`meta()/place()/apply()/showUsername()`,`Settings.init/enter/confirm/reset`,`wire()` 给预览按钮绑「拖动移动 + 右下角圆点缩放」。工作副本 `work` 未点确定不落盘,点「返回」即丢弃。
- 存档:`js/save.js` 的 `settings:{username,touch}` 走 `Save.flush()`(localStorage `iwbtc_fan_v1`)。
- 接线:`index.html` 标题菜单加「设置」按钮 + `#title-user` + `scr-settings` 屏(用户名 input + `#touch-preview` 拖拽预览 + 确定/恢复默认);`js/main.js` 加 `settings/set-ok/set-reset` action 并 `Settings.init()`;`js/touch.js` `show()` 里 `Settings.apply()` 让自定义布局在游戏内生效。
- CSS:`.tc-btn` 改为 `position:absolute;transform:translate(-50%,-50%)`(active 变形同步保留 translate);新增 `.tc-rsz` 缩放手柄、`.touch-preview`、`.set-*`、`#title-user` 样式。
- 验证:新增 `tools/settings-test.mjs`(默认布局/apply 定位/enter+confirm 保存用户名并写 localStorage/reset 回出厂且不自动落盘,15 断言全过);dom-smoke(main 初始化成功)、check/smoke/map/save/key/solve/ice 全绿;预览服务返回 200 且含 `scr-settings`。
## 修复:拖动设置里的手柄按钮时直接跳到右下角
- 根因:`js/settings.js` 原 `wire()` 用「指针(clientX) − 基准偏移(off/cx0)」做**绝对坐标映射**定位;一旦 `wrap.getBoundingClientRect()` 取值与渲染偏差(预览百分比定位 + translate(-50%,-50%) 场景下易偏),按下首帧就把 cx/cy 算到极值 → 弹到右下角。
- 修复:改为**相对位移累加**——按下只记录 `lastX/lastY` 与预览 `rect`;`pointermove` 把「相对上一帧的像素位移 ÷ rect 宽高 ×100」累加到当前 cx/cy(clamp 3~97%),缩放则按「当前指针到中心距离 ÷ 按下时距离」缩放 `size`(24~160px)。第一帧必然停在正确位置,不再跳角。还顺手加了 `e.stopPropagation` + try/catch 包裹 `setPointerCapture`。
- 回归:`tools/settings-test.mjs` 新增「模拟拖动/缩放」4 条——派发真实 pointerdown/move/up 事件断言:cx 按位移增量变化、逆向可精确归位、缩放放大尺寸且不改位置。settings-test 共 21 断言全过,dom-smoke 初始化正常。
## 修复:设置界面打开后一片空白
- 根因:`js/ui.js` 的 `SCREENS` 数组没加 `'scr-settings'`,`UI.show('scr-settings')` 会把所有已知屏 hidden、却从不取消设置屏的 hidden——只剩画布标题背景(用户截图即此症状)。
- 修复:SCREENS 加入 `scr-settings`(一行)。教训:**新增 screen 必须同步 SCREENS 数组**。
- 回归:`tools/settings-test.mjs` 增加「SCREENS 含 scr-settings / 标题有设置入口」两条源码断言,防复发;settings-test 与 dom-smoke 全绿。
## 设置界面:确定返回上一页 + PC/移动端分设
- 需求:① 点「确定」后返回进入设置前的界面(上一页);② 电脑端设置「键盘映射」,移动端设置「手机操作按钮」(按设备二选一显示)。
- `js/input.js`:新增 `DEFAULT_BIND`(出厂 BIND 快照)、`export defaultBindings()`(深拷贝)、`export applyBindings(map)`(用 `{action:[code]}` 覆盖生效键位)。
- `js/settings.js`:
- 新增 `KEY_ACTIONS`(left/right/up/down/jump/shoot/suicide/restart/pause 共 9 项可重绑)、`keyLabel(code)`(←/→/Shift/空格等可读名)。
- `pcMode = !isTouch`(由 `Settings.init(useTouch)` 传入);`enter()` 末尾按 `pcMode` 切换 `#set-keys-row`/`#set-touch-row` 的 hidden:PC 显键盘映射、移动端显手柄按钮。
- `renderKeys()` 渲染每行「动作名 + 按键 chip(点×删除)+ +添加」;`wireKeys()` 点击委托 + `document` keydown 捕获下一次按键写入 `work.keys`(Esc 取消),同一动作支持多键。
- `confirm()` 保存用户名/touch/keys 并 `applyBindings(s.keys)`;`reset()` 把 keys 也回出厂;`makeDefaults()`/`meta()` 保证 `keys` 字段齐全。
- `wireKeys()` 加 `keysWired` 幂等保护(重复 init 不会重复绑监听)。
- `js/main.js`:模块级 `let settingsFrom='scr-title'`;`settings` action 里 `settingsFrom = UI.cur` 后 `Settings.enter()`;`set-ok` 改为 `Settings.confirm(); UI.show(settingsFrom)`(确定后回上一页);`Settings.init(useTouch)` 传入设备检测结果。
- `index.html`:设置屏新增 `#set-keys`(键盘映射容器)+ 两行 `#set-keys-row`/`#set-touch-row` 供切换显隐。
- CSS:`.set-keys/.krow/.kact/.kkeys/.kchip/.krm/.kadd/.klisten` 等键盘映射样式(`blink` 动画提示监听中)。
- 验证:`tools/settings-test.mjs` 扩到 30 断言(input API / PC·移动端面板显隐切换 / 点击 chip→keydown 重绑 / 添加 / 删除 / 确定落盘 / reset 回出厂 / main 里 set-ok 返回上一页源码断言)全过;dom-smoke、check、smoke、map、save、key、ice、solve 全绿;预览 8126 返回 200 且含 set-keys/set-keys-row/set-touch-row。
## 设置·键盘重绑加冲突检测(用户要求)
- 需求:电脑端在设置里改键时,要能「检测冲突」。
- `js/settings.js`:
- 新增 `let conflict = null`(warning 状态)与 `conflictInfo(code, act, idx)`:遍历 `KEY_ACTIONS` 的 `work.keys`,若 `code` 已被**其它**动作(或同动作不同槽)占用则返回该动作标签;并额外检查不可重绑的系统动作 `BIND.menu`(如 KeyQ→「菜单」),避免把键绑到系统功能上。
- `onCaptureKey()`:捕获到非 Esc 的键后先查冲突;若冲突则**拒绝写入**、置 `conflict={code,label,act}` 并 `renderKeys()`(停留在监听态,等换键或 Esc);无冲突才写入并清 `conflict`。
- `renderKeys()`:面板顶部加 `.kwarb` 红色横幅「「X」已被【Y】占用,请换一个键(Esc 取消)」;监听行 tip 在冲突时变红显示「该键冲突,请换一个键」。
- `enter()/reset()` 重置 `conflict=null`;`Settings` 暴露 `_conflict()/_listening()` 供测试/调试。
- `js/input.js`:`const BIND` → `export const BIND`(供冲突检测引用 `menu` 键)。
- CSS:`.kwarb`(红底警示横幅)、`.klisten.bad`(红字警告)。
- 验证:`tools/settings-test.mjs` 扩到 36 断言,新增「冲突:jump 不会绑到被左移占用的 KeyA / 记录到被左移占用 / 顶部警告横幅 / 改用空闲键 KeyK 成功 / 同动作重复键不重复写入 / Esc 退出无残留 / KeyQ 被系统菜单占用」全过;dom-smoke、key、smoke、check、map、save、ice、solve 全绿。
## 刺改为「单三角形 + 碰撞与形状完全对应」(用户纠正)
- 用户纠正:不要 3 个小齿刺,要**单个三角形占一格**,且**碰撞必须和三角形形状完全对应**(不是矩形近似)。
- `js/sprites.js` `drawSpike`:由「3 个小三角」改为**一个占满整格的三角**(尖端朝 dir,底边贴相反侧),并沿底边画一条暗色基座(正好落在三角底边,不扩大判定)。`TILE=32`,故每刺格 32×32。
- `js/level.js`:删除 `spikeBox`(旧「收窄矩形近似」),新增 `spikeTri(t,x,y)`(顶点与 `drawSpike` 完全一致)与 `triRectOverlap`(分离轴定理,凸三角×AABB 充要条件)。`deadlyRect` 改用 `spikeTri`+`triRectOverlap`(网格刺 + dropspike 都用真三角;dropspike 用 `SPIKE_L` 与其 `drawDropSpike` 的 dir=2 一致)。
- 关键性质:底宽尖窄——底部两角现在**会致死**(旧矩形近似把它们当安全),尖顶外侧安全,与真实三角形状一致。
- 一致性利好:game 的 `player.js`、`tools/lib-reach.mjs`(求解器)、`save-diag.mjs`、`bot.mjs` 全部直接调用 `L.deadlyRect`,改碰撞后自然统一,无需另改求解器模型。
- 验证:新增 `tools/spike-test.mjs`(16 断言:spikeTri 四向顶点 / triRectOverlap 内-外-AABB / deadlyRect 集成上刺尖顶安全·底中致死·底左角致死·空地安全 / 与旧矩形近似在底左角结论相反的精确性对比)全过;`solve.mjs` 仍「所有关卡终点与存档点均可达」;editor-wysiwyg、dom-smoke、check、smoke、map、save、key、ice、settings 九套全绿。
## 编辑器所见即所得(WYSIWYG)+ 刺显示真实三角形
- 需求:编辑器里所有方块要显示游戏内真实样子(原来用「彩色块 + 字母/箭头」表示);刺要显示成三角形(游戏里 `drawSpike` 本来就是三角,编辑器之前却画成 `↑↓←→` 箭头字符)。
- `js/editor.js`:
- `import * as S from './sprites.js'`;新增 `drawTileReal(ctx, ch, px, py, props, t)` 把每个字符分派到 `S.drawX`(`#/@/=/M`→drawBlock 各 variant、`%/~/-\/V/^/*/D/X/G/P/F/J/W/T/O/C` 各对应函数,`^` 按 `props.dir` 转 0/1/2/3 画三角,`S` 出生点画小人 `drawKid`);`B`/`!` 等无独立美术的保留「色块+字母」兜底 `drawMarker`。
- `draw()` 重写:每格调用 `drawTileReal` 替代「填色块+画字母」;保留淡网格线、选中高亮;传送门额外叠加 `W{id}` 与连接状态(编辑器元数据)。
- `buildPalette()`:`?` 工具用 ⌖ 字形,其余每个方块用 `drawTileReal` 渲染成 32×32 缩略 canvas(`class="pal-cv"`),调色板即真实预览。
- `PALETTE` 改 `export const`(便于测试);删除不再使用的 `SPIKE_ARROW`。
- CSS:`.pal-cv{width:26px;height:26px;...image-rendering:pixelated}`。
- 测试:新增 `tools/editor-wysiwyg-test.mjs`(init 渲染全部调色板方块无异常 / 刺以三角形图元绘制 lineTo=6 / 方块真实纹理 fillRect / 23 种方块在画布绘制不抛异常);dom-smoke(init 跑 buildPalette 覆盖全部 25 字符分发)、check/smoke/map/save/key/ice/solve 全绿。
## 设置·键盘重绑面板移除「下蹲」(用户要求)
- 需求:把「按键设置」里的下蹲(down)项去掉。
- `js/settings.js`:`KEY_ACTIONS` 删除 `['down','下(下蹲)']`(由 9 项变 8 项)。仅影响键盘重绑面板与冲突检测范围;游戏内 `down` 逻辑(`player.js` 下蹲/下穿)与移动端触屏 `down` 按钮(`BUTTONS`/`touch.js`/`index.html` 的 `data-touch="down"`)保持不变——用户只要求移除「按键设置」里的项。
- 测试:`tools/settings-test.mjs` 的跳跃行索引 4→3(down 移除后 jump 成为第 4 项),36 断言仍全过;dom-smoke、key、check、solve 全绿。
## 刺外观对齐参考图(冰蓝白渐变大三角)
- 用户反馈「这也不是三角形呀」并给参考图:经典 I wanna 的淡蓝白渐变单三角。此前刺是灰色 `#c8c8d8` + 深色基座条,观感不像三角。
- `js/sprites.js` `drawSpike` 重写外观:顶点不变(仍与 level.js `spikeTri` 完全一致,碰撞不动),改为冰蓝白线性渐变(尖端 `#f4f9fd` → 底边 `#a8bccf`,沿刺方向渐变)+ 淡蓝描边 `rgba(150,175,200,.9)` + 尖端附近白色高光片;删除深色基座条。
- 删除已无引用的 `tri()` 助手;`C.spike/spikeDark` 更新为淡蓝(`#cfdde9`/`#a8bccf`)。
- 新增 `spike-preview.html` 预览页:用真实 drawSpike/drawBlock 渲染地面刺/天花板刺/贴墙刺,供用户对照参考图。
- 测试:spike-test(16 断言)、editor-wysiwyg、dom-smoke、solve(23 关可达)及全部回归绿。
- 修复:上一轮 drawSpike 的「尖端高光」坐标计算错误(ax + (bx+cx)/2 把顶点当标量相加,多边形飞出格子),游戏中刺后面拉出灰色长条。已整体删除高光,只留冰蓝白渐变三角 + 淡蓝描边(与参考图一致)。全部回归绿。
## 冰面提速 + 删除首页操作说明 + 物理手感下调(用户要求三连)
- **冰面最大速度阈值调大**:`js/player.js` 新增 `PHY.ICE_TOP: 1.6`(原内联 `HSPD*1.35`),冰面/滑行惯性速度上限 = HSPD×1.6;`ICE_MAX` 局部变量改由 `PHY.ICE_TOP` 计算。ice-test 注释数字同步为 4.32。
- **删除首页操作说明**:`index.html` 去掉 scr-title 的「操作说明」按钮和整个 `#scr-help` 屏;`js/main.js` 删 `help` action;`js/ui.js` 的 `SCREENS` 删 `'scr-help'`;`css/style.css` 删 `.help-*` 规则。
- **物理手感下调(用户:「kid移动速度减慢点,跳跃重力小一点」)**:`js/player.js` 与 `tools/lib-reach.mjs` 同步 GRAV 0.4→0.34、HSPD 3→2.7。扫描确认仅 j1/j9 终点不可达,按「调手感+重调关卡」方案修图:
- j1 回廊:`carpet(m, 81, R-11, 15, [0, 8])` —— x89 加缺口。原因:新物理下从 x88 跳台落进 x90 时落点偏右(fx≈2895),满跳第 3 帧就蹭 x91 的「>」刺尖;有 x89 就能走进 x90 从左侧起跳越过对刺门。
- j9 L0:`carpet(m, 5, 27, 116, [54])` —— x59 加缺口。原因:矮洞天花板(row 24, x60-75)挡住 x57→x61 四格跳的横向漂移,玩家撞顶后下坠砸原点刺;拆成 57→59→61 两个 2 格跳(短跳 apex≈43px,碰不到洞顶)。
- j9 L1:`carpet(m, 9, 13, 108, [27])` —— x36 加缺口。原因:hgap(36,13) 的通道列 36 被 carpet 填了 '^',整门是 3 连刺,从右往左回跳被 x37 的「<」刺别住;补缺口后恢复两段式穿越。
- 全部回归绿:solve(23 关终点+存档+苹果全可达)、check/map/smoke/dom-smoke/save/key/spike/settings/editor-wysiwyg/ice/death/jumpdist/save-diag/bot 共 15 项。
- 教训(已入 MEMORY.md):① 4 格缺口跳在新物理下能跳但起拍位必须够靠左,跳台落点偏右是隐形杀手;② 实心天花板会挡高跳的横向漂移;③ hgap 通道列必须本身为空,carpet 缺口没对齐就会留一根 '^' 把门变成 3 连刺。
## 编辑器:去掉「查看属性」工具,改为任意工具点击即看属性(用户要求)
- `js/editor.js`:`PALETTE` 删除 `['?', '查看属性', ...]`(调色板 25→24 项,全是真实方块);`buildPalette()` 删掉 '?' 的放大镜字形特例;`act()` 删掉 `sel === '?'` 分支——现在所有点击都先 `selTile = {tx,ty}` 再 `renderProps()`,配合原有「放置不覆盖」规则,拿任何非橡皮方块点击已有格子都只查看不覆盖;空格子则放置新块并显示其属性;橡皮擦与右键保持擦除语义。
- 新增回归 `tools/editor-pick-test.mjs`(18 断言):假 DOM 的 addEventListener 真实收句柄,可模拟画布 mousedown;验证 PALETTE/UI 无 '?'、拿 ^/#/% 点 C/T 只查看不覆盖且面板渲染对应字段、拿 O 点空格放置并显属性、橡皮/右键擦除、空态引导文案。注意 renderProps 的字段是 appendChild 子行,断言要拼 `box.innerHTML + children.map(c=>c.innerHTML)`。
- `tools/editor-wysiwyg-test.mjs` 清理 '?' 死过滤。
- 全套 16 项回归绿(solve 23 关可达不受影响,本次只改编辑器交互)。
## 重力再降一档 + 去掉跳跃时 kid 身体晃动(用户要求)
- **GRAV 0.34 → 0.33**(`js/player.js` PHY + `tools/lib-reach.mjs` 同步)。"稍微调低一点点":单跳 apex≈109px、滞空更久。扫参发现 0.32 断 j2、0.33 断 j6(非单调——6px 状态桶对齐决定窄窗存不存在),选最小步长 0.33。
- **j6 修图**:`m.set(44, 15, '.')` 清掉 hgap(43,15) 的 '<' 刺。根因:该对刺门原本只靠「tile45.3(fx≥1448)满跳→tile41.3」这条 2px 宽起拍窗通过;'<' 刺尖会别住起跳(fx<1448 第 3 帧即死),0.33 下 hop 落点整体左移 2px 窗口够不着。清掉后留 ^(43)+>(42) 双刺门,整格起拍窗 + 左右两个方向都稳。修后 j6 状态数 535→934。
- **去掉跳跃晃动**:删 `player.js` 起跳/二段的 `squash=1.28/1.22` 拉伸;`sprites.js` 的 `drawKid` 改 `const sq = air ? 1 : p.squash`——空中恒定原始体型,落地挤压(0.72)残余也不得带入空中。落地挤压保留(在地面,不属"跳跃时")。
- 教训(已入 MEMORY.md):① 对刺门的侧刺('<'/'>')会把起跳/落点窗切成 2px 级窄窗,求解器 6px 状态桶的对齐就能翻转整关可解性——改物理后遇到"某一跳全灭",先查是不是侧刺别住起拍;② 追这类问题用逐帧 trace(复刻 sim.jump 主循环打印每帧 x/y/vy + deadlyRect),比手算几何可靠得多。
- 16 项回归全绿;solve 23 关终点+存档+苹果全可达。
## JUMP 8.5→8.0 降低跳跃高度(用户:「跳跃的高度太高了,调低点」)
- `js/player.js` PHY + `tools/lib-reach.mjs` 同步:JUMP 8.5→8.0。单跳 apex 109.5→97px、水平 139→131px。只降 JUMP 不动 GRAV——保留用户之前要的「小重力飘」手感。
- 扫参定位只破坏 j8(存档#4/#5+终点不可达)。逐帧 trace 实证:j8 x83-84 台(row18)→x88-89 台(row16) 这一跳的航线正对 vgap(86,14);`spikeTri` 的 '^' 顶点在格顶 y=480(无收紧),JUMP 8.0 单跳 apex 盒底 479——只差 1px,顶点 (2768,480) 在起跳后 t=21~25 共 4 帧落在玩家盒内,SAT 铁死。JUMP 8.5 时 apex 盒底 466 从容擦过。
- 修图(j8 两处,其余 22 关零改动):
- `vgap(m, 86, R-8)` → `m.set(86, R-9, 'v')`:去掉 x86 地刺 '^'、保留压顶刺 'v'。试过「x88-89 台加宽到 x87-89」无效(上升段照样死,落点加宽治标不治本)。
- x87 加 '-' 台(配合上面修完后的落点 2802 能上台)。
- 修后 j8 状态数 307→469,23 关终点+存档+苹果全可达。
- 16 项回归全绿。教训已入 MEMORY.md:① apex 盒底与航线 '^' 顶点差 1px 时顶点会连续 4 帧在盒内,改 JUMP/GRAV 后遇到「某一跳全灭」先算 apex 盒底对比航线刺顶点;② 逐帧 trace 复刻 sim.jump 主循环是定位这类问题的唯一可靠手段(手算 SAT 截面连错三次)。
## JUMP 再降一档 + 下落分手重力 + 零形变(用户三连要求)+ 求解器加固(本次会话主体)
用户原话:「跳跃的高度太高了,调低点」→「跳跃高度再调低一点,下落时的重力加速度再明显点,而且kid落地时候依然有拉伸效果,还是不行」。
**物理落地**(`js/player.js` + `tools/lib-reach.mjs` 同步):
- JUMP 8.0→7.5:单跳 apex 97→85px、水平 131→118.8px(jumpdist 实测)。
- 新增 `GRAV_FALL: 0.55`:上升 0.33 轻、下落 0.55 重(分手重力)。落地前一帧竖直速度 8.9px/帧。
- **squash 形变全部删除**:`player.js` 去掉 squash 字段/起跳赋值/落地挤压逻辑;`sprites.js` 的 `drawKid` 按原始 11×21 画;`editor.js` 预览桩同步删字段。用户两次强调落地拉伸也不要。
**求解器三个加固**(`tools/lib-reach.mjs`,solve/reach-diag 默认值同步):
- RELSET [0,4,8,14] → [0,4,5,6,8,14]:vgap 夹缝需要"第 5/6 帧松键"的短跳(apex 32~43px 窗口)。实证:rel=4 t=7 盒右沿蹭地刺左坡 2.6px;rel=6 t=11 盒顶擦垂刺尖 0.6px;只有 rel=5 两边都躲开(j2 x16);j7 顶层 x52 夹缝则要 rel=6。
- **fy 规范化(canon)**:落点常比地面高零点几 px(1535.5 vs 1536),与精确落点同 3px 桶——先占桶者赢,走链被错位几 px 的跳链顶掉。加 rel=6 后 j2 状态数从 1572 暴跌到 80 就是这么来的。push() 里按 canStand 门控吸附到 32 倍数。
- 状态桶 3px(上轮已改)。
**关卡重调**(`js/levels/jump.js`,核心矛盾:JUMP 7.5 + GRAV_FALL 0.55 下 3 连刺=96px 的起拍窗只有 0~1 帧,实测不可玩):
- **carpet 缺口间距 4→3**(3 连刺→2 连刺=64px,射程余量 13→55px):一行改动全局生效。
- j2 五处显式 gaps(井口/左端出口的 3 连刺必须拆,walk 限制使起拍位比所需位窄 1~2px):
- L0:`carpet(m, 5, 47, 50, [1, 18])` 保持(x25-27 已被间距 3 拆成 2 连刺)。
- L2:`[1, 11, 31]` = x12 拆井口 3 连刺;x22/x42 补 hgap 夹缝通道列(原来被 carpet 填 '^',整门变 3 连刺且左侧落点是 4 格跳 128px 超射程)。
- L3:`[0]` = x10 拆左端 3 连刺(左行跳落点盒右沿差 0.8px 蹭 x10 左坡;L1 同位置能过是因 vgap(12) 清空了 x10-14,L3 没有)。
- L4:`[1, 41]` = x12 拆井口 3 连刺;x52 补 hgap 夹缝通道列。
- 修后 j2 状态数 80→2421(10/10 存档+终点),23 关全可达。
**验证**:solve 23 关终点+存档+苹果全可达;check/map/smoke/dom-smoke/save/key/spike/settings/editor-wysiwyg/editor-pick/ice/death/jumpdist/save-diag/bot 共 15 项全绿。
## GRAV 0.33→0.29 上升再飘一档(用户:「跳跃的重力参数再飘点」)
- `js/player.js` PHY + `tools/lib-reach.mjs` 同步:GRAV 0.33→0.29(仅上升段,GRAV_FALL 0.55 不变)。单跳 apex 85→91px、水平 118.8→122px。
- 本轮零修图:23 关全可达(状态数与上轮持平微增)。原因:上轮已把 carpet 缺口间距改 3 格(2 连刺=64px),射程余量充裕,上升更飘不触任何窄窗。
- 16 项回归全绿。至此手感链:移速 2.7 / 上升重力 0.29 / 下落重力 0.55 / JUMP 7.5。