Files
gplan-2/game/presentation/modal_flow.gd
T
DKChan 8b9e55673f refactor(迭代27 T4): 波四 ModalFlow 抽取+互斥域 tags 化+双 context——对拍全同
P1-5/P2-7 组合根可测化+模态协议内聚(场景拆分后重估结论=抽取面收敛为模态编排域):
- modal_flow.gd 新纯逻辑类: 依赖仅 ModalStack+查询 Callable(审计处方)——开合编排
  (挂起兜底/排播排空/遮挡记账/开合同步)/棋盘输入锁谓词(三例外注入)/双形制角标谓词;
  V6 modal_flow_test 19 断言直接 new() 进 run_all(26→27 套)——测试抓出并修正
  挂起兜底 divergent return 一处(原 main 语义=先结挂起后继续开本层不短路)
- ModalStack 互斥域随层登记: register(m, tags) + TAG_NON_LOCKING/TAG_SCREEN_STATE
  封闭词表 + has_open_excluding_tags 组查询;原 has_open_excluding/_any 调用点拼装退役
  (main 五处判据迁移);main._modals=flow.stack(ui_struct _list 登记序断言缝保留)
- P1-5 控制器构造收拢 context: curio 16 参→CurioFlowContext/camp 13 参→CampFlowContext
  (字段逐一同名迁移,控制器可独立实例化)

验收: 对拍三变体全同;冒烟 1079/0+run_all 27 套+probe click+render golden 全绿
2026-10-15 00:31:36 +08:00

90 lines
4.7 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.
class_name ModalFlow
extends RefCounted
# 模态编排纯逻辑类(迭代 27 T4——审计 P1-5/P2-7:组合根可测化抽取 + 模态互斥域内聚)。
# 依赖仅 ModalStack + 查询 Callable(审计处方原话)——零节点构建、零信号连接、零核心触碰
#(ADR-0001 同构:连接归组合根,本类只收注入)。可直接 new() 单测(V6)。
# 职责三件:
# ① 登记与开合编排(遮挡记账单口 / T17 演播后排窗挂起——原 main._open_modal 系逻辑随迁)
# ② 棋盘输入锁谓词(互斥域随层登记——原 main._input_locked 排除式拼装消亡)
# ③ 角标置灰谓词(状态/调试两屏同款守卫判据)
# 排除语义随层登记(ModalStack.register tags):debug_overlay=["non_locking"]、
# level_screen=["screen_state"],其余层零标签——原调用点排除式拼装面消亡(P2-7 修法)。
const ModalStackScript := preload("res://presentation/modal_stack.gd") # 非全局类(main 同款 preload 引用)
var stack = ModalStackScript.new()
# ---- 组合根注入面(查询 Callable 组——依赖仅 ModalStack + 查询 Callable 纪律载体) ----
var start_visible_get: Callable # () -> bool——开始层可见(非模态全屏态例外一)
var inter_visible_get: Callable # () -> bool——过场层可见(例外二)
var settlement_get: Callable # () -> Variant——当前战斗编排器(null=战斗缺位例外三)
var rows_empty_get: Callable # () -> bool——排播队列空(演播后排窗判据)
var flush_rows: Callable # 开层前排空排播(卡⑦——明细带恒完整)
var on_open_sync: Callable # 开侧同步钩子(状态角标+浮窗当局数据)
var on_close_sync: Callable # 关侧同步钩子
var replay_click: Callable # 演播完边沿无挂起时的锁期点击重放(main._replay_locked_click)
var _deferred: Control = null # 演播后开窗挂起(T17——排播放完再开)
func register(m: Control, tags: Array = []) -> void:
stack.register(m, tags)
func open(m: Control) -> void:
# 挂起兜底(T17 演播后排窗):其他路径(如回看)在播期排空队列会使演播完边沿不再触发——
# 此处先结挂起 modal 防永不兑现(m==挂起对象时跳过=本调用自身)。
if _deferred != null and m != _deferred:
var dm: Control = _deferred
_deferred = null
open(dm) # 原语义:先结挂起后继续开本层(不 return——两拍都走)
flush_rows.call() # 卡⑦——任何覆盖层开启前排空排播队列(明细带恒完整,回看/停点面不缺行)
stack.open(m)
on_open_sync.call()
func close(m: Control) -> void:
stack.close(m)
on_close_sync.call()
# 演播后排窗(T17 用户令「伤害过程演完才切结算窗口」):排播队列在播=挂起,演播完边沿
# (pump_ended 派发)再开——旧路径前置 flush 会把击杀轮的伤害/死亡演出同帧倒掉。
# 队列空=立即开(行为同旧路径)。
func open_after_playback(m: Control) -> void:
if rows_empty_get.call():
open(m)
return
_deferred = m
m.visible = false # 挂起含视觉(open_* 已置 visible=true——不压掉则窗在排播期照会上屏,延迟形同虚设)
# 演播完边沿派发(row_pacer.pump_ended 注入口):先开挂起 modal(胜利窗接管呈现),
# 后锁期点击重放——modal 开启后输入锁吞掉重放点击(战斗已收场,重放无意义)。
func pump_ended() -> void:
if _deferred != null:
var m: Control = _deferred
_deferred = null
open(m)
return
replay_click.call()
# 棋盘输入锁谓词(原 main._input_locked 随迁):模态互斥域(non_locking 层开态不计——迭代 18
# 卡⑦ T2 防线)+ 三例外(开始层/过场层非模态全屏态 + 战斗缺位棋盘输入无载体)。
func locks_input() -> bool:
return stack.has_open_excluding_tags([ModalStackScript.TAG_NON_LOCKING]) \
or bool(start_visible_get.call()) or bool(inter_visible_get.call()) or settlement_get.call() == null
# 角标置灰谓词·战斗屏形态(状态/调试两角标同判据):登记册上 non_locking 层外的任一层开着。
# (地图屏此态恒关——屏级互斥,与旧排除式 [debug] 逐位同值)
func badge_greyed_battle() -> bool:
return stack.has_open_excluding_tags([ModalStackScript.TAG_NON_LOCKING])
# 角标置灰谓词·地图屏形态(地图屏自身 screen_state 不计 + 调试浮窗 non_locking 不计——
# 与旧排除式 [level_screen, debug] 逐位同值)。
func badge_greyed_map() -> bool:
return stack.has_open_excluding_tags([ModalStackScript.TAG_NON_LOCKING, ModalStackScript.TAG_SCREEN_STATE])