Files
gplan-2/game/presentation/row_pacer.gd
T
DKChan b605022001 feat(迭代27 T5): 波五a 类型闸拉起+存量收口——审计器 0 存量+warning 配置在位
P1-6 类型闸(须 T1-T4 全落后——本波按执行顺序锁殿后):
- tools/audit_type_hints.py 新机器闸(V7): R1 无类型容器注解/R2 有值返回缺返回类型/
  R3 参数缺类型三规则+行级 # type-hint: ignore 显式白名单通道;产品码 core/data/presentation
- 存量收口: 368 处显式 Array/Dictionary 非参数化注解→机械参数化(Array[Variant]/
  Dictionary[Variant, Variant] 零行为面);13 处参数真类型(SaveManagerCore/ConfigDb/
  BattleOrchestrator/MinesweeperRules/LevelRunner/SettingsStore 等)
- 白名单豁免四处: board_generator generate/_attempt/legality/_finalize 的 db 参——
  测量面 _BoardRowStub 冒烟夹具鸭子型注入(typed 参拒 stub)
- project.godot 拉起 gdscript/warnings/untyped_declaration=1(V7 源码断言在 smoke _smoke_type_gate)
- static_scan 新 S9 类型闸规则;顺修历史 cd 错位(9-24 布局迁移后 /.. 落 game/ 致守卫
  恒跳过静默空转——修复后 S1-S4/S9 首次实跑全绿)+S1/S2 增 ADR-0007-exempt 标记豁免通道
  (main _seed_value 引擎熵源=文档化豁免面)

验收: 冒烟 1081/0(+V7×2)+run_all 27 套+static_scan 全绿+probe click+render golden+对拍三变体全同
2026-10-15 01:05:17 +08:00

154 lines
8.0 KiB
GDScript
Raw 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.
# VERTICAL SLICE - NOT FOR PRODUCTION
# 结算行排播泵(迭代 8 卡⑦;T3 拆件批 2026-10-02 自 main.gd 整体迁出——规范 1.1 单一职责):
# 演出层排播:逻辑先行零回压,行按段分组阶梯延迟进明细带。
# 硬契约:核心层零 await 不破——本队列只做信号的"后置消费",结算顺序/结果/时序逻辑零改动(§3 契约 1);
# 减动效开=延迟归零走同帧直落快路(行呈现不跳过——§3 契约 5);模态开启前强制排空(明细回看/停点
# 覆盖层所见明细带恒完整——排空入口在组合根 _open_modal)。
# 装配纪律:视图句柄与呈现刷新回调由组合根注入(ADR-0001——本类零信号连接,仅做信号后置消费);
# 行消费与卡⑦前逐位一致(_consume_row 原文迁入,语义零改动)。
extends Node
const PACE_SEGMENT_GAP := 0.3 # 段间阶梯延迟(代拍板:白名单 0.2–0.4s 内取值,追认清单呈报)
const PACE_ROW_GAP := 0.22 # 段内间隔(三轮 #3 上调 0.06→0.22——逐条演出可感知,配合数字浮现/状态脉冲)
var _rows: Array[Variant] = [] # 待排播行(step_row 载荷原样入队;__round_line 标记=轮首分隔行)
var _pace_wait := 0.0 # 距下一行排播的剩余秒(_process 消耗)
var _prev_segment := "" # 上一排播行的段型(段间/段内延迟判据)
var _was_pumping := false # 排播活跃边沿(T17——末行消费帧触发结算视觉回挖掘态刷新)
# 组合根注入面(_build_ui 装配——呈现句柄 + 演播完边沿的呈现刷新回调)
var log_view: BattleLogView
var stage: BattleStageView
var board_zone: BoardZoneView
var settlement_fx: SettlementFxView
var board_view: BoardView
var refresh_request := Callable() # () → 组合根 _refresh_presentations(行尾直呼/演播完边沿 call_deferred)
var pump_ended := Callable() # () → 组合根锁期点击重放入口(T17 v3.1——演播完边沿 call_deferred;空回调安全)
# 队列只读面(探针/测试经组合根委托属性读——共享同一 Array 引用,读数即实况)
func pending_rows() -> Array:
return _rows
# 入队(常态路径——减动效关;step_row 载荷原样,零加工)
func enqueue(row: Dictionary) -> void:
_rows.append(row)
# 同帧直落(减动效快路——延迟归零,行呈现不跳过;轮首分隔行减动效分支同用此口)
func consume_now(row: Dictionary) -> void:
_consume_row(row)
# 段间延迟取数面(减动效双态断言用——开=0 归零,行不跳过;常态=段间/段内两档)
func pace_delay_for(prev_seg: String, next_seg: String) -> float:
if UiKit.motion_scale < 1.0:
return 0.0
return PACE_SEGMENT_GAP if prev_seg != next_seg else PACE_ROW_GAP
# 强制排空(模态开启前调用——明细回看/停点覆盖层/终局面所见明细带恒完整)
func flush() -> void:
while not _rows.is_empty():
_consume_row(_rows.pop_front())
_pace_wait = 0.0
_prev_segment = ""
func _process(delta: float) -> void:
# 排播泵(卡⑦——演出层帧驱动;队列空=零开销直返)
if _rows.is_empty():
_pace_wait = 0.0
_prev_segment = ""
if _was_pumping:
# 演播完边沿:结算视觉期由排播队列承载(_is_settling 排播项),队列排空的翻转
# 无核心信号驱动——此处主动刷回挖掘态(去饱和解除/明细带收起/mode=dig)。
_was_pumping = false
refresh_request.call_deferred()
# 锁期点击重放(T17 v3.1——玩家操作零丢失):最后一次锁期点击延迟一帧重入
# _on_board_action(此刻队列已空=锁已解,正常受理;重放入口由组合根注入)。
if pump_ended.is_valid():
pump_ended.call_deferred()
return
_was_pumping = true
_pace_wait -= delta
if _pace_wait > 0.0:
return
var entry: Dictionary[Variant, Variant] = _rows.pop_front()
_consume_row(entry)
var seg := String(entry.get("type", ""))
_pace_wait = 0.0 if UiKit.motion_scale < 1.0 else (PACE_SEGMENT_GAP if seg != _prev_segment else PACE_ROW_GAP)
_prev_segment = seg
# 行消费(排播队列的出队步 / 减动效快路共用):明细带入带+演出链+呈现刷新——与卡⑦前排位逐位一致
func _consume_row(row: Dictionary) -> void:
if row.has("__round_line"):
log_view.finish_settlement_round() # 上轮摘要行(明细带 pattern 3)随轮首分隔行落带
log_view.append_bb("[color=#e0b860]── 第 %d 轮 ──[/color]" % int(row["__round_line"]))
return
log_view.append_row(row)
stage.on_step_row_fx(row)
_play_cross_zone_fx(row) # 跨区因果演出(结算演出链批):开格之因 → 站桩之果
_play_settlement_mark(row) # 三轮 #3——逐条标出:Buff/Debuff 色环脉冲 → 伤害/治疗数字浮现
# 高光演出(战斗结算×3①② 占位):完美排雷横幅 / 标中旗加重弹道
if String(row.get("type", "")) == "highlight" and String(row.get("highlight", "")) != "skill_unlock":
board_zone.fx.play_perfect_sweep()
elif String(row.get("highlight", "")) == "flag_hit":
board_zone.fx.play_flag_hit_projectile(true)
# Boss 半血解锁技提示(迭代14④ T4——D8 呈现面:skill_unlock 行弹一次性技能名 toast,
# 轻量非演出零动画资产;行文本 =「XX 解锁了 YY」,只报技能名不报数值——规格=敌人系统 意图呈现节)
if String(row.get("highlight", "")) == "skill_unlock":
board_zone.toast.show_toast(String(row.get("display_name", "")), "info")
refresh_request.call()
# 逐条结算标出(三轮 #3——用户裁决按结算顺序演出:先 Buff/Debuff 特效、再基础伤害数字;
# 打中谁标谁、主角被打标主角;道具/奖励行逐条冒出)。目标解析失败兜底棋盘中心。
func _play_settlement_mark(row: Dictionary) -> void:
var mark_type := String(row.get("type", ""))
if mark_type != "effect" and mark_type != "damage" and mark_type != "heal":
return
var target := String(row.get("target", ""))
var center: Vector2
if target == "hero":
center = stage.hero_standee_center()
else:
var v = stage.enemy_standee_center(target) if target != "" and target != "all_enemies" else null
center = v if v != null else board_view.get_global_rect().get_center()
match mark_type:
"effect":
var col: Color = Color("7cc47c") if int(row.get("value", 0)) > 0 else Color("c47c7c")
settlement_fx.play_status_pulse(center, col)
"damage":
settlement_fx.play_damage_number(center, "-%d" % int(row.get("value", 0)),
Color("d9402a"), bool(row.get("crit", false)))
"heal":
settlement_fx.play_damage_number(center, "+%d" % int(row.get("value", 0)), Color("7cc47c"), false)
# 跨区因果演出(§Transitions 结算逐行兑现——动画密度终稿归战斗表现样张批):
# 伤害行 → ⚔ 弹道自棋盘飞向目标敌方站桩(crit 金档加重);治疗行 → ✚ 弹道飞向主角站桩;
# 命中反馈(闪白/血条递减/灰化)由站桩侧 on_step_row_fx/set_data 承接
func _play_cross_zone_fx(row: Dictionary) -> void:
var from_v: Vector2 = board_view.get_global_rect().get_center()
match String(row.get("type", "")):
"damage":
var target := String(row.get("target", ""))
var crit := bool(row.get("crit", false))
if target == "" or target == "hero":
# 主角受击弹道(迭代 6 T5 拆「敌→主角零反馈」早退):⚔ 危险红飞向主角站桩;
# crit 只加重字号不加金(金=收益/胜利语义位,不用于敌方对主角输出)
settlement_fx.play_projectile(from_v, stage.hero_standee_center(), "⚔",
UiKit.RED_MINE, crit)
return
var to_v = stage.enemy_standee_center(target)
if to_v != null:
settlement_fx.play_projectile(from_v, to_v, "⚔",
UiKit.GOLD if crit else UiKit.RED_MINE, crit)
"heal":
if String(row.get("target", "")) != "hero" and String(row.get("target", "")) != "":
return
settlement_fx.play_projectile(from_v, stage.hero_standee_center(), "✚", Color("7cc47c"))