# Migration Decisions ## 2026-07-06 第八轮:视差语义定案(远景天幕,夕阳恒在画面内) 策划澄清视差意图:背景是**远景天幕**,夕阳要时刻在画面内——即背景应 大比例**跟随相机**(远景几乎不动的错觉),而非此前理解的"背景比世界慢 一点"。此前 0.92/0.8 的参数把背景几乎钉在世界上,方向整个反了,这才是 连续几轮"看不出视差"的真因。 1. **参数**:`PARALLAX_SCROLL_SCALE := 0.2`(背景仅以 0.2 倍速随世界滚动, 80% 跟随相机);`PARALLAX_ANCHOR_X := 1734.5`(让夕阳——原画 x≈760、 世界 x≈1656——在出生点镜头可见中心 1340.8 恰好居中)。 2. **验算+截图验证**:相机可见中心全程 [1340.8, 2753.2],夕阳屏幕位置从 正中缓移到左 282px,始终在画面内(含光晕);两端背景覆盖充足 (极左窗⊂[581.5,2882.5]、极右窗⊂[1711.5,4012.5])。窗口化实测四点 截图确认:出生点夕阳居中、中段/封门处缓移仍在画面中央区域,未来 相位(红色天幕)同样成立;世界物件大幅滚动 vs 天幕缓动的对比即 视差观感本体。 3. **已知取舍加剧说明**:天幕语义下步道砖(烙在原画里)随镜头大幅跟随 (奔跑时脚下滑动 176px/s)——单张烙死图的固有代价;美术把"近景步道" 拆成独立 1:1 层后即可消除,其余层保持现参数。 4. 可视化验证配方(复用):manage_scene=false + 手动挂 main.tscn + start_game(),Title 态直接 _set_state 不行(树暂停 + 标题屏不透明底色)。 ## 2026-07-06 第七轮修复(玩家与敌人重叠:冲刺穿人收尾排斥) 策划确认:冲刺穿人机制保留,其余时间玩家不得与小怪/Boss 重叠。 1. **调查结论**:身体碰撞矩阵完整且实测有效(玩家 layer32/mask65,敌人 layer64/mask33,平移互撞精确挡在半宽和 19px)。重叠的真实来源是 dash_through 的收尾策略:落点嵌在敌人体内时,恢复碰撞的逻辑"干等分离" (07-04 定案,防止嵌入恢复被反推回入口侧)——嵌着就无限期保持幽灵态, 玩家可长期站在敌人身体里;期间敌人 AI 还会照常向玩家中心走位,把重叠 "续命"。叠加事实:Godot 的 move_and_slide 对已重叠的双方永不主动分离 (safe_margin 0.001),所以一旦嵌入只能靠显式排斥。 2. **修复**:MotionExecutor 记录冲刺方向;`_try_restore_collision` 嵌入时 不再干等,每物理帧沿冲刺方向 `move_and_collide` 推 4px(240px/s,快于 小怪追击 120px/s),顶到边界墙推不动则反向从入口侧退出;推清后恢复 碰撞。幽灵掩码只剩 world 位,排斥不可能进入地形。"从远侧穿出"的 07-04 手感语义保留;cancel 的非物理帧即时恢复路径不变(有测试钉)。 3. **不动的部分**:敌人互相不碰撞(minion/boss mask 无 bit64,成群穿插是 现状设计);小怪尸体在死亡动画期间仍挡路(存量怪癖);Boss 战前 combat_enabled=false 非实体是有意豁免(test_level1_melee_contact 钉)。 4. 契约测试 test_dash_overlap_separation:嵌入落点 45 帧内推出+恢复碰撞+ 从远侧穿出+间距 ≥ 半宽和;干净落点行为与旧版一致。 ## 2026-07-06 第六轮修复(Boss→玩家击退 / 视差加强重启) **语义澄清**:策划三轮反馈的"Boss 击退"实指 **Boss 攻击玩家时对玩家的击退** (第三~五轮修的"玩家击退 Boss"方向保留不回退)。 1. **Boss→玩家击退不可见的三个根因**:①Boss 近战沿用默认基准 120,普通 连段倍率仅 0.4/0.5 → 滑行 2~3px(重击也只 20~37px);②敌方弹丸基准击退 恒为 (0,0)(actors_container 只给敌弹注 speed),Boss 未来相位只用远程 → 整相位击退数学上为零(.tres 里的 0.3/0.5 倍率是死数值);③相机 1:1 跟随 玩家——玩家被击退时自己在屏幕上不动,只有背景平移,小位移完全无感。 2. **修复**:boss.tscn DamageEmitter 覆写 `base_knockback = (360, 304.056)` (初速 ×3 = 距离 ×9):连段 20/32px、重击 190/341px;敌方弹丸生成时注入 `base_knockback = (360, 0)`(不击飞):Boss 射击 11~32px、女巫 8~15px, 强弱势归零规则照常生效。Boss 恒强势(is_strong_in_current_time_phase 恒 true),玩家 Hitstun 0.4s 内不能起手/移动,滑行不受自身动作干扰。 调参 knob:boss.tscn 的 base_knockback 一处 + actors_container.gd 注入值。 3. **视差加强重启**:0.92 时代偏移峰值仅 ±56px、实战镜头段 ±20px,肉眼难辨 (策划"看不出视差"两轮反馈的真因;上一轮误判为"脚下打滑"而回退)。现 `PARALLAX_SCROLL_SCALE := 0.8`,峰值 ±141px、实战段 ±60px。已知取舍: 步道砖烙在原画里会随镜头轻微滑动(0.8 下奔跑时约 44px/s),策划若不接受 只能等美术拆层(无伤视差的唯一解,接线半小时)。 ## 2026-07-06 第五轮修复(击退被反向突进吃掉 / 视差回退) 1. **Boss 实战零击退的真凶:自己的下一拍动作**。裸场景击退管线全通过,但 真实 stage 里 Boss 被击退 3 帧后就照常起手攻击(attack 态还冻结摩擦衰减), 随后动作自带的突进位移接管 velocity 以 -375px/s 冲回玩家——实测净位移 **-24px**(比不击退还近)。修复:**趔趄门禁**——击退残速期 `can_start_enemy_action` 返回 false(EnemyActionDriver 的 BT/谱面双路径 都走此钩子),滑行完整播完 AI 才恢复;小怪加同款钩子。Boss 受击链强制 撤退豁免:`_trigger_hit_chain_escape` 先 `clear_knockback_stray()`(新增 motor 公开方法)再起手 boss_retreat_dash,否则 4 连击逃脱被门禁拦死。 真实 stage 探针验证:位移 -24px → **+132px**(≈理论 129.6)。 2. **单层微视差回退**。可行走步道砖烙在关卡原画里,整图 0.92 倍速 = 角色脚下地砖打滑(策划反馈"视差有问题"即此)。逐像素核查后确认横向 分层缝也不可行:油灯底座 y495-520、栅栏柱脚 y485-495、锚徽 y495-555、 步道面 y495-550 相互咬合,任何水平缝都会把显眼物件剪成两截随视差错位; 左右边界石墙纵贯全高更无法归层。结论:**单张烙死的图做不了无伤视差**, 已回退到背景钉死,契约改为"背景不得随相机移动"。真视差待美术按 "天空/远景海船/中景码头道具/近景步道"拆层出带透明通道的 PNG(宽度均 ≥2301+视差余量),接线约半小时(每层 Sprite2D + 相机可见中心 × 系数, 勿用 Parallax2D——历史上因 CanvasModulate/wipe 兼容性移除过)。 ## 2026-07-05 第四轮修复(Boss 击退加码 ×9 / 小怪击退修复 / 单层微视差) 1. **Boss 击退距离 ×3 → ×9**:首轮 ×3(滑行 43px 级)试玩仍嫌短,策划要求再 ×3。`boss.knockback_distance_taken_mult()` 3.0→9.0(初速 ×3)。斩波 Lv1 击退 Boss ≈130px、下砸 ≈634px;嫌多嫌少改这一个返回值即可。 2. **小怪击退修复(结构 bug,与 Boss 同源)**:minion.tscn 挂着 MovementMotor 但 minion.gd 零引用——击退初速写入后下一帧就被 AI 接管/清零,且 motor 的 摩擦衰减和假高度积分从不执行。修复:新增 movement_motor 成员; handle_air_time 无条件委托 motor(boss 同款);handle_movement 在相位切换 hold **之后**加击退残速早退(换相位定身优先)。顺带修掉隐藏 bug:垂直击退 曾令小怪 ground_state 永久卡 Airborne,从此 allowed_ground_states 拒绝一切 出招——现在假高度正常积分、落地复位 Grounded。小怪不加距离倍率(基准 15~70px 滑行),策划试玩后如需加码再走 knockback_distance_taken_mult 钩子。 3. **关卡背景单层微视差**:ground_background.gd 新增 _process——ArtLayer.x = 基准 + (相机可见中心 − 2047) × 0.08,即背景水平速度为相机的 0.92 倍 (PARALLAX_SCROLL_SCALE)。锚点 2047 = GroundAnchor = battle_center = 贴图中心;极限位移 ±56.5px,已验算两端不露边(顺带修掉了原有的极右 16.5px 潜在露边)。用 get_screen_center_position()(含 limit 夹取)而非 camera.global_position,玩家贴墙时背景不跟滑;无相机(headless/菜单) 保持原位。手写每帧位移而非 Parallax2D:历史上 Parallax2D 因 CanvasModulate/wipe 兼容性已被移除过(见下方历史条目)。真·多层视差 (天空/远景/近景分速度)等美术拆层素材到位后再升级。 4. **修复存量 flake**:test_rhythm_feedback_and_move_list 的特殊拍断言依赖 "启动后 0.46s 内跑到检查点"(RhythmManager 墙钟自启动,_upcoming_beat_index 走实时钟),启动稍慢即越过第 1 拍。按测试纪律钉停时钟(stop_manager), 走确定性 _last_beat_index 兜底。6 连跑验证。 5. 契约测试 test_round4_fixes 钉住小怪水平滑行/残速自清/垂直击退落地恢复、 视差公式与无相机兜底;Boss ×9 更新在 test_round3_fixes。 ## 2026-07-05 第三轮修复(标题背景重组 / 判定字样 1s 消失 / Boss 击退 ×3) 1. **标题画面重组**:背景弃用烙有 Logo 的整图(start_background_past/future), 改用无 Logo 的干净关卡原画 `assets/art/ground/past/past.png` 与 `future/future.png`(与关卡内背景同资产),Logo 由独立悬浮 TextureRect (`TITLE_LOGO_RECT` = 390,140,616×300,MOUSE_FILTER_IGNORE)叠加。背景轮换 周期改为策划口径"2 秒为单位渐入切换":`TITLE_BG_HOLD_SECONDS` 2.8→2.0、 `TITLE_BG_FADE_SECONDS` 1.6→0.8。渐变期间 Logo/菜单固定,顶部重叠感消除。 难度选择页同步换干净未来原画 + 悬浮 Logo。双背景节点名与 `_title_bg_tween` 变量契约保持不变(test_title_music_and_background 无需改动)。 2. **判定字样 1 秒自动消失**:rhythm_track 新增 `JUDGEMENT_VISIBLE_SECONDS=1.0` 倒计时;每次 `_show_judgement_art`(普通判定/锚点 held/TIME SHIFT 三条路径) 重置计时("顶替"语义),到点把 JudgementArt/JudgementLabel 一并隐藏。倒计时 期间不动 modulate/text/scale,与脉冲动画及判定色互不干扰。 3. **Boss 击退距离 ×3**:调查发现 Boss(水平)击退位移实际恒为 0——boss.gd `handle_movement()` 每物理帧无条件 `velocity.x = 0.0`,击退初速活不过一帧 (headless 实测 61 帧位移 0.00px)。修复:MovementMotor 暴露 `has_knockback_stray()`,Boss 在击退残速期跳过清零、交给 480px/s² 摩擦自然 衰减(与玩家同路径,不用被 test_boss_integration 禁止的本地计时器 hack)。 距离 ×3 通过受击方鸭子钩子 `knockback_distance_taken_mult()=3.0` 实现, CombatManager 按 距离∝初速² 换算为初速 ×√3;只放大水平滑行,击飞不变。 实测 120 初速滑行 14.4px→43.3px。小怪存在同类清零问题(minion.gd AI 每帧 接管 velocity.x),策划未提,本轮未动。 4. 契约测试 test_round3_fixes 钉住以上全部行为(背景资产路径、2s/0.8s 常量、 悬浮 Logo 几何与鼠标穿透、判定 1s 消失+顶替重置、Boss 击退倍率与残速存活)。 ## 2026-07-05 第二轮需求落地(剑雨放大 / 敌弹降速 / 菜单组件化 / 结算重做 / 受击朝向修复) 依据策划当日九条答复的最终口径: 1. **剑雨放大一倍(伤害不变)**:6 个 jian_yu .tres 的 `range` 140→280(三级判定 仍相同);落剑视觉 strike_count `1+level`→`2*(1+level)`(4/6/8 把)、spread `90+40*level`→`120+40*level`(Lv3 最远落点 40+240=280 恰与新判定对齐,顺带修复 旧版"视觉铺 250px 但判定只有 140px"的空气剑);BLADE_RAIN_TILE_SPECS 四条目 scale 0.5→1.0(raindrop 跟随)。damage_mult/base_cost 一字未动。 test_blade_rain_alignment 的 Lv3 数量断言 4→8。 2. **敌弹降速 80%(斩波不变)**:actors_container.gd 新增 `ENEMY_PROJECTILE_SPEED := 416.0`(520×0.8),非玩家阵营弹丸生成时覆写 speed; 玩家弹保持 player_projectile.gd 默认 520。销毁按累计飞行距离,降速不缩射程。 3. **受击朝向修复(策划定案:保持受击前朝向)**:双根因双修—— ① combat_manager._apply_knockback 按受击者与 result["from"](emitter 位置)的 x 差把水平击退方向化为"背离攻击者"(dx=0 或缺 from 保留旧 +X);result dict 里的 knockback 数值保持 resolver 原值,纯数值契约测试不受影响。 ② movement_motor 新增 `_knockback_stray_active` 标记:apply_knockback 置位、 残速衰减到 0 或自主移动接管时清除;set_heading 在标记存活或 life_state != Alive 时不改写 heading。玩家/敌人同语义(受击不转身)。 4. **菜单组件化(废弃整图+透明热区)**:新素材套件入库 assets/ui/menu2/ (面板 panel_tall + 按钮底两态 button_base_normal/hover + 文字贴图 13 张 + Logo title_logo),中文字体 assets/fonts/fzzdhjw.ttf。game_flow_manager 全部 界面改为"面板 + StyleBoxTexture 双态按钮 + 文字贴图/字体"拼装:标题(Logo+ 三键+当前难度页脚)、难度选择(简单/普通/困难+开始/回标题+页脚,选中=高亮橙 框)、暂停/确认框/胜负 splash(NinePatch 面板;确认框用 确认/取消 贴图)、 结算(整面板+游戏结算标题字)。按钮保持 Button 类型 + 旧点击矩形兼容坐标测试; 过去/未来场景背景轮换保留。素材里的"结束游戏"按策划答复弃用。 5. **结算重做**:统计标签中文化(最大连击/完美/良好/勉强/失误/锚点成功/相位 切换,fzzdhjw 字体渲染);RANK 只留等级("RANK S",去掉 "(83/100)" 分数), 失败仍不显示 RANK。胜负停留时间 ×3:VICTORY_SCREEN_DELAY 1.2→3.6s、 DEFEAT_SCREEN_DELAY 1.6→4.8s;保留两段式(胜负 splash → 查看结算 → 结算)。 6. **胜负合成音效(策划定案:合成音,不引音频文件)**:SfxManager 新增 &"victory"(C5-E5-G5-C6 上行琶音 1.2s)与 &"defeat"(G4-Eb4-C4-G3 分立下行 1.3s,与 &"death" 滑音区分);订阅 EventBus.flow_state_changed,进入 Victory/Defeat 态时播放(voice 继承 PROCESS_MODE_ALWAYS,暂停树可发声); 玩家死亡瞬间的 &"death" 保留叠加。 7. 新契约测试 test_round2_changes(剑雨数值、敌/我弹速、击退方向、朝向门禁、 延迟常量、结算中文与 RANK 格式、难度页脚联动)。 8. 顺带修复的存量测试问题:①四个对难度敏感的测试(startup_fix / time_phase_adapter / ui_animation_regression / v2_core_contracts)此前隐性依赖 menu 测试残留在 settings.cfg 的 easy 难度,现已显式钉 easy;②三处过时断言改到现口径——相位强势 倍率实为 ×1.4(资源 07-04 校准)、谱面预告标记已有意停用(钉住"不生成")、combo HUD 脉冲断言改抓命名槽位 Slot1;③test_stage4_movement_physics 的固定秒数等待 会被 ANCHOR_ACTIVE 起手拉伸打败(偶发 flake),改为物理帧轮询。 ## 2026-07-05 new1/new2/new3 需求落地(判定门控 / 左侧出生 / 怪物与 Boss 行为) 依据 docs/new1-3.md 与策划当日答疑的最终口径(new1 原文的"零代价无效输入" 已被策划改为"MISS+锁定"混合方案): 1. **判定门控(new1 定案)**:RhythmManager 新增 `gate_judged_input` / `is_input_locked`(`input_lockout_seconds` 0.5、`consumed_repeat_grace` 1)。 规则:每个节奏点只能被一次有效输入消耗;同拍第二次按键按双击容错忽略; 第三次(或超出 BAD 窗的空按)判 MISS——保留原惩罚(无动作、清连击、四拍 槽写 ∅)并触发 0.5s 输入锁定,锁定期间按键完全无效(不判定、不留痕、不 刷新锁定)。纯判定接口 judge/get_rating_for_time 保持无副作用;门控只作 用于**实时判定**的输入(_ensure_judged 打 `live` 标记),测试/AI 的预置 判定原样绕过。接入点:ActionController.submit_intent 主路径 + 两个蓄力施放路径。锁定横跨时间锚点拍 = 锚点必破(相位切换),设计确认。 test_player_combo_input 顺带修复:每次按键前把时钟钉到新拍心,替代旧的 墙钟判定(~20% flake 消除)。新测试 test_input_gate。 2. **出生点左移(new2)**:stage.tscn 玩家 (2047,560)→(1180,560),相机初始 (1180,395);开场需向右推进约 1000px 才遇预置近战怪(2200 不变,策划确认 该段路用于熟悉操作)。LevelDirector 的战斗中心/增援锚点不变。标题固定+ 背景轮播本就已实现,未改动。基线测试(x1_visual_baseline / rhythm_ui_layout / boss_integration / level1_melee_contact / minion_phase_system)中依赖旧出生点的断言改为按门线/停步距离取位。 3. **伤害体系(new3 定案)**:保留"基础攻击 × 相位倍率"。近战小怪基础攻击 翻倍 25→50(LevelDirector 按 MinionBehavior.role 配置 emitter.damage, 远程保持 25);Boss 近战招式倍率翻倍(combo 0.8/0.9→1.6/1.8;重击保守 放大 combo_3 1.4→2.2、lunging_stab 2.0→3.0,未按字面 ×2 以免单跳 120), Boss 远程倍率下调 shoot 0.6→0.4(30×0.4=12 恰合 new3 推荐)、2way 0.7→0.45。 4. **距离参数(new3)**:近战怪警戒 420→360、停步 110→120、出手距离 150→120、攻击判定 range 58~74→130(前方 reach≈140);远程怪危险距离 150→220、后撤 190→220、后撤完毕按拍停顿 1 拍;**射程上限压到视距内** (攻击距离 430→450 ≤ 相机半宽 460——绝不屏幕外开火,new3 的 720 被策划 否决)。Boss melee_distance 135→180、ranged 260→360、dash 360→460。 5. **远程怪受击脱离(策划定案,取代 new3 §5.5 的单次受击)**:连续受击 4 次 (2s 链窗)触发闪烁无敌 0.4s + 跳跃形式脱离(escape_speed_scale 3.2; minion.gd 的 approach_direction 钳制放宽到 ±4);无敌走 FrameCollisionDriver.damage_receiver_enabled(驱动器每帧重申矩阵,直接改 Area2D 会被覆盖);3s 冷却防连续触发;贴墙时沿用穿过玩家身位的反向跳。 6. **Boss 霸体(策划定案,取代 new3 §6.5 韧性系统——明确不做韧性)**: CombatResolver 新增受击方 `shrugs_off_hit(action)` 钩子;Boss 对 base_cost≤0 的攻击恒霸体(伤害照常、打断与击退取消),耗能技能可压制。 所有耗能技能补上击退:dash_slash 1.5、combo_finisher 2.0、ground_smash 2.2(保留原 y1.8 击飞)、zhan_bo 1.0/1.3/1.6、jian_yu 0.8/y0.6;玩家弹丸 在 ActorsContainer 注入 base_knockback(120,304)(原为零)。保留"被连打 4 次后撤逃脱"作为防贴脸兜底。resolve_knockback 顺带修复为跟随伤害判定 通道(good 0.85),对齐 test_combat_manager_resolvers 既有契约。 7. **Boss 相位行为(new3 §6.4/6.7)**:远程相位被近身(≤240px)优先 boss_retreat_dash 拉开(后撤无伤害、8 拍冷却);相位切换后停顿 1 拍再 决策(以总线最后拍号为基准,兼容测试合成拍号)。stage.tscn 的 Boss stationary=true 保持——那是战前木桩状态,BossRoomGate 反锁时已会释放。 新测试 test_new3_enemy_rules(霸体规则/按职责基础攻击/受击脱离)。 ## 2026-07-04 playtest pass 2 (dash pierce / anchor presentation / radial phase wipe) 1. **Dash-through actually pierces now.** Two compounding defects: (a) action displacement speed was computed over the full action length while the motion only runs from Active start to action end, so every action under-travelled by the startup fraction — the 260px dash realized ~195px and frequently ENDED inside the boss; (b) the collision mask snapped back the moment the action finished, so ending overlapped meant depenetration shoved the player back out the entry side ("did not pierce"). Fixes: `_action_motion_speed` divides by (action_beats - startup_beats); MotionExecutor ghosts BOTH directions during dash_through (strips player_body|enemy_body bits from mask AND layer so neither side's move_and_slide recovery can shove anyone), and restores collision deferred — each physics frame it shape-queries the ghosted body layers and only snaps back once clear of overlap. Outside physics frames (headless tests) restore is immediate, keeping the existing test_stage4_movement_physics contract green. 2. **Time anchor presentation follows the upgrade semantics.** The mirrored red pair converging every anchor beat read as "recolored beat balls", and at high streak tiers (interval 1) it doubled screen elements. Now: the beat movers THEMSELVES turn anchor-red when the beat they converge on is an anchor (screen element count never exceeds the rhythm grid). Follow-up: the extra "herald" ball that parked above the center emblem was judged redundant in playtest (the recolored movers + the HUD "ANCHOR Nb" countdown carry the information) and was removed entirely — time anchors add zero elements to the track. Held/broken feedback unchanged. 3. **Phase switch is a player-centered radial wipe.** Scene tint now lives solely on the CanvasModulate (stronger: past warm 1.0/0.94/0.82, future cool 0.55/0.72/1.0; the color-mapped ground reads as the other map). On time_phase_changed the world gets the NEW tint instantly and a fullscreen blend_mul shader multiplies everything OUTSIDE an expanding circle (centered on the player, radius 0 → screen diagonal in 0.5s) by old_tint/new_tint — the old scene visibly dissolves outward from the player, with a bright rim at the wavefront. Works symmetrically in both switch directions; the wipe center follows the player each frame. ## 2026-07-04 action feel pass (mirroring / air slam / dash derive / charge gauge / dummy boss) Fixes driven by playtest feedback against the player_tagged author sheets: 1. **Blade wave cast mirroring.** zhan_bo has no displacement and no left/right tag, so the body never turned while the projectile aimed at the nearest boss. `_play_action_animation` now faces projectile actions (`hit_type == projectile` or `projectile` tag) toward `projectile_direction()`; the whole Visual (body + FX overlay) mirrors with heading as usual. 2. **Air combo is a ground slam.** The author's 12-Aerial Combo sheets are byte-identical to 13-Plunging Strike (PREP / SWING START / FALLING [LOOP] / LAND + FX2/3/4): the move is a forward slam, not an in-air slash. atk_air gained the falling loop segment + FX3 hold; both atk_air and plunge_start now arm `_pending_air_slam_land` at active start — the falling pose holds until touchdown, then a shared `air_slam_land` animation (land.png 6f + FX4) plays. FX overlay offsets follow the author's bottom-right anchor rule `offset = (64 - cell_w, -cell_h)` → FX4 (135x166) = (-71, -166). air_attack_l/r data: move_mult_y 0.2 → -1.6, active 0.5 / recovery 0.5. 3. **[A][sp] derive beats the A-charge.** With hold_threshold at 0.25 beats (0.125s @120bpm), beat-cadenced [A] then [SP] with A still held had already entered Charging, so SP was shelved and the A release cast blade rain. New rule in ActionController.submit_intent: a pressed SP while an on_release charge hold (A/D) is armed or charging breaks the charge and falls through to normal resolution — the four-slot window still holds [A], so [A][SP] resolves the dash (dash_through pierces the boss and hits while passing). S-charge (on_secondary_key) is untouched. 4. **Blade rain composite.** Falling blades now fall for exactly BLADE_RAIN_DROP_FALL_TIME (0.24s @1900px/s) and vanish at ground contact as the landing tile starts (its first frames carry the streak + impact); tiles are bottom-anchored to the ground line via per-sheet cell_height, uniform 0.5 scale, spread grows with charge level (2/3/4 strikes). 5. **Charge gauge = cast level.** ChargeComponent's private 1.1s ramp had nothing to do with the beat-quantized cast level in ActionController. ActionController now exposes `charge_state()` (MAX_CHARGE_LEVEL 3, progress_units 0..max-1) as the single source; the gauge renders level-space (ready == top level), pulses the overlay on level-up, and the HUD shows "CHARGE LV n / 3" with per-level fill colors. Legacy 1.1s ramp remains only as a standalone fallback. 6. **Dummy boss for testing.** Boss export `stationary` (attacks but never displaces: no BT dash/retreat — replaced with ranged pressure — no action motion, no hit-chain escape). Enabled on the Stage instance only; boss.tscn keeps mobile defaults so behavior-tree contract tests still exercise retreat/escape (the point-blank test explicitly un-sets it). ## 2026-07-04 time anchor / dual time phase feature pass Implemented the full AnchorV1.0 chapter 10-12/17/20 feature set following the incremental plan in `Fighting_Rthythm_game/docs/时间锚点与双相位架构修改方案.md` (all names below use its reserved terms: `time_anchor` / `time_phase` / `attack_buff` / `streak`): - **New autoloads** (registered after CombatManager): `TimePhaseManager` (sole writer of `current_time_phase`, broadcasts `time_phase_changed`) and `TimeAnchorSystem` (schedules chart-explicit + periodic anchors, resolves held/broken from `judgement_made` facts only, routes `force_time_phase`). Periodic candidates are computed lazily (origin + current streak tier) and lock when they enter the 2-beat lead window; explicit chart anchors win same-beat collisions and restart the periodic count. - **EventBus** gained `time_phase_changed` / `time_anchor_scheduled` / `time_anchor_resolved` / `streak_changed` / `attack_buff_changed`. `judgement_made(quality, offset_ms, beat_index)` was already extended during migration (7.1-A), so everything here is pure increment. - **EffectContainer** two increments only: stack-aware aggregation for add modifiers (`value x stacks`; all pre-existing resources are max_stacks 1, audited by test_effect_container) and `set_effect_stacks` (plus a small `effect_stacks` reader). Resolvers/CombatManager/RhythmManager untouched. - **Player** gained `StreakCounter` (+1 on skill_executed, zero on miss / chart_reset) and `AttackBuffComponent` (stack storage IS the `effect_time_anchor_attack_buff.tres` Effect instance: +10% damage_mult per stack, cap 20, halved floor on every actual phase switch, zeroed on chart_reset). Damage rides the existing buffs multiplier slot in `resolve_damage` — the design formula 1 + 0.10 x stacks emerges from the stack-aware aggregation. - **Boss** gained `TimePhaseAdapter` with the PastStrong profile (`resources/time_phase/profile_past_strong_enemy.tres` — past: dmg x1.2 / taken x0.8, future: x0.7 / x1.3 via `effect_tp_*` resources; the FutureStrong mirror profile exists for future enemies). Damage-taken multipliers ride DefenseModifier entries; both phases share all collision. - **Chart layer**: `ChartEvent.time_phase_mask` (+`matches_time_phase`), `ChartTrack.time_phase_mask` (authoring default pushed onto `both` events at load), `BeatChart.initial_time_phase`, ChartRunner trigger-time final ruling (skipped events are marked triggered and never re-dispatched) and initial-phase application on ready/reset. stage9_boss_duel gained a time_anchor track (beats 8 / 40). - **Dual music layers**: `AudioLayerController` (scenes/audio/) runs two always-synced players seeded from `RhythmManager.song_position`, crossfades volumes on phase switch (0.35s), drift-corrects the *presentation* players only (loop-wrap aware), and mutes the clock stream. Placeholder assets: both layers use ev_past1.mp3; the future layer goes through a runtime low-pass bus so the switch is audible until a real Future loop lands. - **Presentation placeholders per request**: the second map is ground.png color-mapped (Stage tints the ground sprite + a CanvasModulate, warm past / cool future, 0.35s tween + white flash under the UI layer); the time anchor ball is yellow_ball.png color-mapped crimson in RhythmTrack (mirrored pair converging on the center, grows near the hit point, held → green lock flash "ANCHOR HELD", broken → magenta "TIME SHIFT!"). - **HUD**: TimePhaseHud (phase name + next-anchor countdown, buff stacks /20 with percent, streak) and DebugPanel lines for time phase state and armed/last-resolved anchors. - **Tooling/tests**: `tools/time_anchor_chart_tool.gd` (validate / generate / simulate) and seven new SceneTree tests (`test_time_phase_manager`, `test_time_anchor_resolution`, `test_time_anchor_scheduling`, `test_streak_counter`, `test_attack_buff`, `test_time_phase_adapter`, `test_chart_time_phase_mask`). No Godot binary is available on this machine, so the suite is authored but not yet executed locally. Open tuning items deferred (doc chapter 12): anchor-specific window, streak clear on hurt, on_break punishment effects, conditional anchors (v1.5). ## 2026-07-03 body collision audit Phase 0 keeps the base physics layer names from the migration manual: `world`, `player_hurtbox`, `enemy_hurtbox`, `player_hitbox`, and `enemy_hitbox`. The source project still uses root body collision to let the player and boss block each other: the player body is on layer 2 with mask 5, while the boss body is on layer 4 with mask 3. To preserve that feel without mixing body blocking into hurtbox or hitbox layers, the target project reserves layer 6 as `player_body` and layer 7 as `enemy_body`. ## 2026-07-03 phase 4 character asset migration Phase 4 copies character art only from the curated `tmp/new` source folders: `player_tagged` is migrated to `res://assets/art/characters/player/`, and `boss_tagged` is migrated to `res://assets/art/characters/boss/`. File names are normalized to ASCII snake_case before animation paths are authored. The player package README is preserved as `credits_blair_ceradsky.txt`; the package is CC-BY 4.0 and must credit Blair Ceradsky / @MookaTheCaveman. The boss folder still has no license file in the source material, so boss art is a development asset until the license is identified and added. The old jump-height test is superseded by a no-free-movement/no-jump-input regression: player movement in this migration comes from ActionData-driven rhythm actions only. ## 2026-07-03 post-migration bug-fix pass A full audit against `AnchorV1.0.md`, the migration plan/manual, and the character/boss design doc fixed the following migration defects (tests updated in the same pass): 1. Energy scale: player max energy was 10 while skills cost 15-50, so no skill could ever be paid for. Player/EnergyComponent max is now 100 and the energy bar fills proportionally. 2. Melee reach: all player melee ActionData had `range = 0`, leaving a 48px centered hitbox that body-blocking made unreachable. Every melee action now carries a range. 3. Projectile teams: the shared projectile scene was hard-wired to enemy-hitbox layer / player-hurtbox mask, so the player's blade wave hit the player and never the boss. `projectile_requested` now carries a context Dictionary (team, action, judgement, range); ActorsContainer assigns collision layers per team and injects the ActionData and judgement so damage multipliers resolve. Player gained muzzle spawn methods; the boss muzzle height was lowered to 55 so bullets actually cross the player hurtbox. 4. Super armor: HealthComponent entered Hitstun on every damaging hit regardless of `interrupts`; it now respects the defense resolution (death still always applies). 5. Boss behavior tree: retreat no longer has top priority at melee range (combo first, 2-way shot punish when melee is on cooldown, retreat only after both), the ranged cycle no longer bypasses the 2-way cooldown, and beyond dash range the boss closes in instead of shooting forever. Hit-chain escape now needs 4 hits, has a 4s cooldown and only fires while grounded. Chart events take priority: an upcoming chart event postpones BT decisions and a triggering chart event overrides a BT action. 6. Boss presentation: MotionExecutor is cancelled on action end (no more sliding across the arena), animation speed_scale is aligned to the action's beat length, and the boss no longer turns to track the player mid-action. 7. Charge levels: level was measured from the initial key press (always level 3); it is now measured from time spent in the Charging phase, quantized by `charge_level_beats` (1 beat per level). Holding A/D during a chain-cancelled combo step now arms the charge gate too. 8. Combo HUD: enemy ComboWindows no longer broadcast to the EventBus, so the player's four-slot HUD is not wiped whenever the boss is interrupted. 9. Direction semantics: displacement direction now follows the left/right action tags (mirror pairs) and falls back to the current heading for forward actions (W launcher no longer forces the player to face right); plunging strike now actually drives the fake-height fall (negative move_mult_y maps to height_speed while airborne). 10. Player presentation: hit-stun and death now play (manual sheet animations from 14_hit_stun / 15_death), the plunge has its falling loop, and the charge FX overlay uses the real charging_up_overlay sheet instead of leftover textures. 11. Rhythm track UI reads BPM from RhythmManager (was hard-coded 80 vs the actual 120). 12. Block: parry reduction is graded by the block judgement (perfect 100% / good 80% / bad 60%) and only applies to attacks from the front, per AnchorV1.0 section 9.4; blocked hits no longer interrupt. block_start active window widened to 0.5 beats (total 0.8 beats = 0.4s at 120 BPM, matching the design sheet). ## 2026-07-03 playtest follow-up fixes (bullets / collision / latency / air lock) Root causes found during the first playtest and their fixes: 1. **Stuck in the air after W then S.** MotionExecutor converted `move_mult_y` into a screen-space body velocity; when landing flipped the presentation state to LAND the attack-state cleanup never ran, so the plunge left `velocity.y = -220` on the CharacterBody and the body ascended forever (vertical motion belongs to the fake height system, the body must stay on the ground plane). MotionExecutor is now horizontal-only, MovementMotor zeroes `velocity.y` on landing and decays stray horizontal knockback velocity (KNOCKBACK_FRICTION 480) whenever no attack owns it. 2. **Bullets looked wrong.** effect_sheet.png is a 6x2 grid of 32px cells (verified by alpha scan); the projectile sliced it as 5x2 (38.4px, non-integer) so every frame was cut through the middle. The projectile now uses 6x2, loops the four flight frames at 16 fps, expires by travel range (default 460px, zhan_bo levels 380/460/540) instead of a 0.3s animation timer, and flies at 520px/s instead of 1100 so it is readable. 3. **Collision boxes did not match the drawn bodies.** The new art draws both characters with their feet ~40px above the node root (player idle rows 24-88 of 128; boss rows 16-47 with a -67 sprite offset). All body/hurtbox/hitbox shapes still sat at the old feet-at-root heights, i.e. ~35px below the visible bodies. Player shapes moved to y=-72, boss shapes to y=-70, projectile lanes to -70 on both sides. Player waves now also clear enemy bullets (mask includes enemy_hitbox; enemy_projectiles are freed on contact) implementing the design's projectile duel. 4. **Blade wave fired away from the boss.** The player heading defaults to left and only directional attacks turn it; zhan_bo now aims at the nearest live member of the "bosses" group with heading as fallback. 5. **Latency feel.** anchor_grid halved (0.5 -> 0.25 beats) so on-beat presses reach Active with no artificial stretch; the four-slot clear display no longer swallows the next input (it flushes and consumes immediately); agile input flushing enabled in project.godot; player action displacement now follows the S6 rule (move_mult 1.0 = 100px over the action) instead of a flat 220px/s lunge. test_boss_integration projectile assertions were rewritten for the new contract (6x2 grid, flight loop, range expiry, torso-lane spawn heights, moved hurtboxes). ## 2026-07-03 player animation / FX audit vs player_tagged source pack Audited all 17 tagged action folders against the runtime setup. Root problem: only the three AnimationPlayer clips (atk_ground_1/2, rising_slash) had FX tracks; every action running through the manual sheet-animation path had no FX channel at all, and looping cast animations were being stomped by idle because attack_time_left used the natural clip length (0.1s for the 2-frame blade rain loop). - Manual animation system now supports per-segment fps and a parallel FX timeline driving Visual/FxOverlay. All overlay offsets were computed by aligning the author's embedded frame-0 idle reference against the idle sheet (method reproduces the two hand-calibrated offsets exactly): 01/02/03/05 slash (-147,-191), 04 dash (-98,-128), 07 rising (-111,-156), 08 prep ring (-64,-305), 11 cast (-115,-172), 12/13 FX2/FX3 (-107,-198). - FX wired: atk_ground_3 + combo_finisher slash overlays, dash_slash speed-line overlay (frame 1 is the author's intentional blank), atk_air FX2, plunge FX2 + FX3 falling hold, blade_wave cast overlay, blade_rain launch ring (prep overlay frames 17-19). - Charge visuals split per entry: A/D charge plays the MAGIC CAST prep body animation (hold last frame) with the sword-ring overlay forming (frames 1-16, hold); S charge keeps idle stance with CHARGING UP overlay at the author's 50fps. - Mode C: ContactFxSpawner script added (was a script-less placeholder node) — spawns the per-attack ENEMY CONTACT bursts on the target for melee and the projectile contact burst for blade waves. Mode D: ground_smash chains three SHOCKWAVE tiles along the ground; jian_yu spawns raindrops plus 2-4 RAIN LANDING TILE pillars scaled by charge level. One-shot world FX use scenes/combat/one_shot_fx.gd. - Blade wave projectiles now use the author's PROJECTILE sheet (5 frames, 33fps loop); enemy bullets keep the effect_sheet placeholder. - Smoothness: attack presentation window extended to the action's beat length (idle no longer cuts off casts), same-name animations restart on chain (player and boss), and starting any new action clears stale FX overlay state. Idle now plays all 8 frames (test updated to [0..7]). ## 2026-07-04 full UI reskin (ui_and_ground asset pack) + harbor stage backdrop The placeholder rhythm-track art (rod/blue_ball stitching, center.png emblem, yellow_ball movers) was replaced wholesale by the authored pack in ui_and_ground/, and the stage gained a three-layer harbor backdrop. Map A (past phase) uses the purple/gold set, map B (future phase) the red "d" set; the world itself still renders map B via the existing CanvasModulate color mapping + radial wipe. - RhythmTrack re-skins entirely from `TRACK_SKINS[time_phase]` (rhythm_track.gd): background b/bd (single stretched piece — no more rod stitching, the line sits centered in the frame), long axis line/lined, center emblem anchor01/anchor01d, movers star/stard, special (time anchor) beats swap the mover texture to sanchor/sanchord — still zero extra track elements. - Hit feedback per the art spec: a judged normal beat flips the emblem to the own-phase anchor02 variant and fires the own-phase c01 flash; a held special beat flips the emblem to the CROSS-phase anchor02 variant with the own-phase c02 flash (A: anchor02d + c02, B: anchor02 + c02d). The emblem reverts to idle after 0.45s. A latch keeps the follow-up judgement readout from overwriting anchor feedback (held resolutions arrive nested inside the same judgement_made dispatch; deadline breaks pass an empty judgement dict and don't latch). - Beat ticks now pulse the flash at 0.35 peak alpha so full brightness reads as "you hit something"; test_ui_animation_regression asserts visibility > 0.2. - Stage backdrop: long.png (sunset sea, Parallax2D scroll 0.12, scaled 1.4378 to exactly fill the 982px-tall view) and mid.png (lighthouse/ship/crane cluster, scroll 0.4, scaled 0.92, bases tucked 17px below the sea horizon at y≈383) render behind the ground scene; both are plain-canvas Parallax2D so the CanvasModulate phase tint and the radial wipe cover them too. - ground.png (stone dock with chain railing + end pillars) replaces the old strip at uniform scale 1.7979: the walk surface in the art (texture y 570) lands exactly on the gameplay ground plane y=366 and the art spans precisely boundary-to-boundary (-21.5..4115.5, pillars at the walls). A dark StreetUnderfill polygon backfills below the art so the bottom of the frame never shows void, and Camera2D got limit_left/right (-22/4116) so the view can no longer slide past the artwork at the arena edges. ## 2026-07-04 (later) disaster recovery + WYSIWYG authored view + panel HUD + per-frame collision matrices Context: at ~05:11 an incomplete Mac zip (Baidu sync had not finished uploading scenes/assets — the *.baiduyun.uploading.cfg markers) was extracted over the tree and the merge wiped scenes/, scripts/, tools/, most of resources/ and assets/. The repo had zero commits, so nothing was recoverable from git. The tree was rebuilt from four sources: (1) the three session transcripts (every file ever read/written, replayed Write+Edit chains, most files verified byte-exact against the surviving .git/index SHA-1s), (2) the old project (assets, addon, byte-identical UI scenes, the tagged author packs under assets/art/characters/tmp/new/), (3) ui_and_ground/ + playbossui/ art packs, (4) the three newer test files carried by the zip (test_rhythm_ui_layout, test_boss_integration, test_stage4_player_scene), which numerically encode the newer authored-view state and were treated as the binding spec. A survivor backup was kept at ../Rthythm_archor_game_survivor_backup_0542. THIS commit history now exists precisely so this can never happen again. WYSIWYG (editor == game), per the newer tests: - Stage exports use_authored_camera_view (default true): the editor-authored Camera2D pose IS the runtime view — position (2229.63, 97.38), zoom 0.82, no player follow, no zoom overwrite. false restores the legacy follow rig. - MainUI moved out of CanvasLayer into the world canvas: Main/UI is a Control positioned at the camera world-rect origin (1527.191, -297.742) at scale 1/0.82, sized 1152x648 — the editor shows UI over world exactly as rendered. - Backgrounds are plain Node2D containers (names LongParallax/MidParallax kept for test paths): long.png sunset at (791.6, -335) x1.25, mid.png regions as moored ship (1230, -250) and lighthouse (2650, -303); CanvasModulate is authored in stage.tscn so the phase tint previews in the editor. - Ground sprite crops to region Rect2(0, 90, 2301, 593), offset (-1150.5, -480) — walk line stays texture y570 = world y366. Actor grounding is authored: Visual position (0, 32) with player.gd visual_ground_offset 32. - Player Visual scale ±2, boss Visual scale ±4 (flip preserves magnitude); all scaled sprites use TEXTURE_FILTER_LINEAR. Panel HUD (playbossui pack): StatusBars at (20.74, 35.64) wraps playerui.png (x1.5) with HealthBar/EnergyBar/ChargeBar overlaying the baked wells (player_red_ui/player_blue_ui fills); BossStatus right edge 1123.2 wraps boss_ui.png with BossHealthBar (boss_red_ui fill) + DREAD HARBINGER name; ComboWindow centered at y 278.64; TimePhaseHud and DebugPanel ship hidden. Per-frame collision/damage matrices (frame_collision_driver.gd, on player and boss): every physics tick the driver re-derives all three matrices from the frame actually displayed — - body: base layer/mask re-asserted, composed with MotionExecutor ghost bits; - damage-receive: DamageReceiver rect follows the opaque pixel bounds of the current frame (per-sheet bounds cached from texture alpha), margins via hurt_margin, mirrored automatically through the Visual transform; - damage-send: DamageEmitter only monitors during ACTIVE-phase frames whose native-forward extent reaches HIT_REACH_GATE (0.72) of the sheet's max — the hit window is the weapon-extended frames, not the whole active phase; the shape is rebuilt per frame from pose reach vs action range. DamageEmitter gained per-swing dedup (_already_hit_ids) since per-frame windows can re-trigger area_entered within one swing. New headless test: tests/test_frame_collision_driver.gd (synthetic 4-frame sheet: pose-following hurtbox, reach-gated hitbox, mirror flip, matrix re-assertion, swing dedup). Verification pass (9 static-analysis agents over all 39 tests + cross-refs) confirmed the rebuild and surfaced fixes that were applied: - grounding: player CharacterSprite/FxOverlay moved to node position (0, 40) (visible feet at world 398, camera-frame ratio 0.88; FX stays glued since both sprites shift together and all test-pinned offsets are untouched); boss.gd gained the set_sprite_height_position override so the authored Visual (0, 32) survives physics ticks — boss visible feet land at 398 too. - driver: forward reach is now facing-derived (boss art is native RIGHT- facing, player native LEFT — the mirror sign of basis_x times heading picks the correct native side); a late-ACTIVE fallback window guarantees a plain range hit if no frame of a swing passes the reach gate (art timing can no longer whiff a beat-anchored swing); frame bounds use per-frame get_region().get_used_rect() (C++-side, no first-swing hitch). - test isolation: chart_runner and TimeAnchorSystem resolve bus/manager/clock via reverse root-child scan, and only the real TimeAnchorSystem autoload self-drives from the global clock (test instances are inert). - player.gd no longer contains get_parent() (architecture test contract). Known pre-existing flake (left as-is, documented): test_player_combo_input judges through the RhythmManager wall clock headless, so ~20% of beat phase lands in the miss window and the run can abort; other suites pin judgements. The `libraries/ =` AnimationPlayer serialization was flagged and verified VALID (per-library subpath form, matches the editor-saved old project), and hash-verified load_steps miscounts in old .tres files were intentionally kept (historical bytes, self-healing on editor save). ## 2026-07-04 final Level 1 and menu UI migration baseline The playable baseline was restored to the `rthythm_archor_game_player` reference for camera, HUD canvas, actor/background scale, and authored stage proportions. Level migration is scoped to Level 1 flow, spawn logic, gate state, and combat sequencing; it must not resize the player, minions, Boss, background, HUD, or camera. - `GameFlowManager` now follows the `Rthythm_archor_game` menu implementation byte-for-byte and uses the authored menu textures under `assets/ui/menu`. Difficulty options apply immediately on click, the difficulty page exposes the reference start/back commands, and no current-value label is drawn over the command area. - Stage runtime returns to the player reference view: `UILayer/UI` remains a CanvasLayer-hosted HUD, the camera starts at `(2047, 395)` with zoom `(1.25, 1.25)`, and camera follow is retained. - Root ground anchors for player, the initial minion, and Boss stay at `y = 560`; the visible sprite feet line is verified at `y = 520` for all three actors. Boss uses `visual_ground_offset = -40` and the first active minion uses `visual_ground_offset = -32` so the runtime walk/dash frame lines up with the same visible baseline without changing actor scale. ## 2026-07-05 charge release facing, blade rain ground line, projectile hit circles Three combat-feel fixes reported from play: the S-charge release auto-aimed at the nearest enemy (waves went right whenever a target stood to the right), the blade rain landing FX rendered 40px below where the player visibly stands, and enemy bullets carried a hit circle far larger than the drawn bolt. - Player `projectile_direction` now returns the current facing only (left heading releases left); the nearest-enemy auto-aim helper was removed along with the projectile-turn branch in `_play_action_animation` (a release no longer flips the player toward a target behind them). Regression: `tests/test_zhan_bo_release_facing.gd`. - World-FX ground anchoring: actor origins sit on `y = 560` but the visible feet line is `y = 520` (40px transparent margin under the authored player frames; minions/boss match via `visual_ground_offset`). Blade rain raindrops and landing tiles now anchor to `Player.VISIBLE_GROUND_LINE_OFFSET = -40` so the lowest visible pixel of every landing tile shares the player's visible ground line instead of sinking into the pier. `tests/test_blade_rain_alignment.gd` asserts against the constant. - Projectile collision must stay inside the visible art: `VISUAL_PROFILES` gained `collision_radius` (bullet 9.0 — the effect_sheet bolt is ~32x18px at scale 2; wave keeps 32.0, inside its ~66px smallest frame) and `_apply_visual_profile` duplicates the scene's shared `CircleShape2D` before resizing so bullet/wave instances never overwrite each other. Layer audit found emitter/receiver/projectile team masks already correct (player projectile also keeps enemy_hitbox in its mask on purpose — wave clears enemy bullets). Melee hitboxes untouched: jian_yu keeps its 140px range, which stays narrower than the FX spread at every level. - Known stale test: `test_stage4_player_scene.gd` expects the old 2x-scaled player Visual with a `(0, 32)` editor feet preview; it fails against the current 1x `player.tscn` regardless of these changes (verified by reverting).