Files
gplan-2/game/presentation/modal_stack.gd
T
DKChan 0529e1691c fix(iter37①): T2 呈现/组合根缺陷修——蓄力档分档·庆祝句柄·互斥四口·模态 fail-fast·站桩按 id 解析
- d2 蓄力档 `_intent_charge` 布尔分档(颜色相等判据退役,深浅金同值不再误判);d4 庆祝浮动 tween 句柄 kill 纪律(close 必杀)
- d5/d6/d7/d8 互斥四口:调试钮判据并入 `_sync_sim_button` 单点、组合根两守卫补模拟态/冒险态、`is_running()` 运行态查询面
- d9 ModalStack open/close fail-fast+遮挡账合并非覆盖(重复 open 不再吞旧账);d12 target_id 产发(damage_controller 四处)+站桩两口 id 先名兜底;d17 await 后实例守卫;d18 参数类型注解
- 断言:standee_fx_sweep_test(新增,18 项)/modal_flow_test V6-5(5 项 wd9,V6-1~4 原样同绿)/damage_controller_test target_id/pacing_queue_test 排播 target_id/system_test wd6+wd7
- 代拍板:d18 卡片字面 `lumas_out: Array[Variant] = null` 在 Godot 4.7 解析报错(typed array 不可默认 null),改用 `Variant = null`——唯一合法注法,零行为变化,随代拍板追认单留痕
2026-10-25 03:49:05 +08:00

77 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 = _occluded.get(m, []).duplicate()
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