golden 换代触发原因(强制记载):棋盘三图换代面实测 changed=0.13%/0.11%/0.10%, 差异像素全落 900×760 裁剪区内(裁剪区外 0),按 README 加严口径判失效;成因定位为 2f3a87e9(魔法道具 HUD)注入 REWARD_BADGE 预读格字形,晚于 2026-10-08 建基,与本批零因果。 呈报后用户裁「重制三张基线」,经 tools/golden_baseline_refresh.sh 换代;复闸 changed 全 0.00%、 菜单 0.00% 未动。README 判定记录同步加注一条。 同批:modal_stack 合并账本改 Array[Control](类型闸 R1);新增/触碰的测试件按 test-conventions §6 补齐类型标注(FakeStage/FakeFx calls: Array[Array]、damage_controller_test 两处 db: ConfigDb), pacing_queue_test 三条 d12 断言补〔wd12〕尾缀。直跑:standee_fx_sweep/damage_controller/ pacing_queue 三套全绿+error_scan 0;audit_type_hints PASS。
80 lines
2.9 KiB
GDScript
80 lines
2.9 KiB
GDScript
## VERTICAL SLICE - NOT FOR PRODUCTION
|
|
## 模态层级状态机:登记序 = 树序 = z 遮挡序,打开显式隐藏其上方可见层、关闭恢复。
|
|
## 层必须先 register 方可 open/close;遮挡由本类显式记账,不得靠树序巧合。
|
|
## 纯逻辑类——Control 层由组合根喂入 register。
|
|
extends RefCounted
|
|
|
|
var _list: Array[Control] = [] # 登记序 = z 遮挡序(低 → 高)
|
|
var _occluded := {} # modal → 被其隐藏的上方层(关闭恢复)
|
|
var _tags := {} # modal → 排除组标签集(互斥域随层登记)
|
|
|
|
# 排除组封闭词表(组合根登记用——新标签=改此处+评审,防调用点拼装回潮)
|
|
const TAG_NON_LOCKING := "non_locking" # 开态不计入棋盘输入锁与角标置灰(调试浮窗默认开×遮挡复活×has_open 组合=棋盘永久锁死)
|
|
const TAG_SCREEN_STATE := "screen_state" # 常驻屏态(关卡地图屏——自身开态不计入本屏角标判据)
|
|
|
|
|
|
func register(m: Control, tags: Array = []) -> void:
|
|
_list.append(m)
|
|
_tags[m] = tags
|
|
|
|
|
|
func open(m: Control) -> void:
|
|
var idx := _list.find(m)
|
|
if idx < 0:
|
|
push_error("ModalStack.open: 未登记层不可打开(register 先行)")
|
|
return
|
|
var hidden: Array[Control] = []
|
|
for i in range(idx + 1, _list.size()):
|
|
var higher: Control = _list[i]
|
|
if is_instance_valid(higher) and higher.visible:
|
|
higher.visible = false
|
|
hidden.append(higher)
|
|
# 遮挡账合并非覆盖:同层重复 open 时上方已隐、hidden 为空,旧账须保留
|
|
# (空表覆盖会让先前被隐层在本层 close 后永不恢复)
|
|
var merged: Array[Control] = []
|
|
for h in _occluded.get(m, []):
|
|
if not merged.has(h):
|
|
merged.append(h)
|
|
for h in hidden:
|
|
if not merged.has(h):
|
|
merged.append(h)
|
|
_occluded[m] = merged
|
|
m.visible = true
|
|
|
|
|
|
func close(m: Control) -> void:
|
|
if _list.find(m) < 0:
|
|
push_error("ModalStack.close: 未登记层不可关闭(register 先行)")
|
|
return
|
|
m.visible = false
|
|
for h in _occluded.get(m, []):
|
|
if is_instance_valid(h):
|
|
h.visible = true
|
|
_occluded.erase(m)
|
|
|
|
|
|
# 开态查询(组合根输入锁唯一判据:登记册上是否有任一层处于开态;
|
|
# 可见性即开态,与 open/close 记账同源——组合根不再手工罗列各层)
|
|
func has_open() -> bool:
|
|
for m in _list:
|
|
if is_instance_valid(m) and m.visible:
|
|
return true
|
|
return false
|
|
|
|
|
|
# 排除组开态查询(模态协议内聚:排除语义随层登记(register tags),
|
|
# 查询按组名集合表达,禁在调用点拼装 has_open_excluding/has_open_excluding_any。
|
|
# 只读查询零副作用——登记序/开合记账语义不变;空标签集=全计(与 has_open 同形)。
|
|
func has_open_excluding_tags(exclude_tags: Array) -> bool:
|
|
for m in _list:
|
|
if is_instance_valid(m) and m.visible:
|
|
var mt: Array[Variant] = _tags.get(m, [])
|
|
var excluded := false
|
|
for t in exclude_tags:
|
|
if mt.has(t):
|
|
excluded = true
|
|
break
|
|
if not excluded:
|
|
return true
|
|
return false
|