merge: 关卡画面深层节点不可点根修——画布随图自适应+图上命中真实输入回归闸(用户目检 bug 反馈)
This commit is contained in:
commit
83c11a13a7
2 files changed
+96
-11
No files matched your search
@@ -46,6 +46,7 @@ var _attr_value: Label
|
|||||||
var _bonus_row: HBoxContainer
|
var _bonus_row: HBoxContainer
|
||||||
var _bonus_value: Label
|
var _bonus_value: Label
|
||||||
var _cand_ids: Array = [] # 本停点候选 id 集(图上命中合法集)
|
var _cand_ids: Array = [] # 本停点候选 id 集(图上命中合法集)
|
||||||
|
var _content_size := Vector2(620, 240) # 图内容包络(open_choices 按图拟合——画布 min 尺寸随它)
|
||||||
var _hover_id := -1 # 悬停预承诺候选(-1 = 无)
|
var _hover_id := -1 # 悬停预承诺候选(-1 = 无)
|
||||||
var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——只在冒险模式开)
|
var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——只在冒险模式开)
|
||||||
var _abandon_armed := false # 二次确认态(首击置位、二击提交、超时/重开回落)
|
var _abandon_armed := false # 二次确认态(首击置位、二击提交、超时/重开回落)
|
||||||
@@ -209,10 +210,31 @@ func open_choices(cands: Array, runner) -> void:
|
|||||||
_cand_ids = []
|
_cand_ids = []
|
||||||
for cand in cands:
|
for cand in cands:
|
||||||
_cand_ids.append(_id_of(cand))
|
_cand_ids.append(_id_of(cand))
|
||||||
|
_fit_canvas(_runner.nodes())
|
||||||
_refresh_status()
|
_refresh_status()
|
||||||
visible = true
|
visible = true
|
||||||
|
|
||||||
|
|
||||||
|
# 画布随图自适应(level-map.md Layout:画布宽随层数、高随同层最大节点数——公式与 _layout 同源;
|
||||||
|
# 2026-09-28 真鼠标点击 bug 根修:固定 620×240 画布下深层节点(L1 Boss 内容 x=802)落在 gui_input
|
||||||
|
# 命中区外——绘制无裁剪看得见、命中收不到事件。单链关卡层深=节点序,必须先拟合再命中。
|
||||||
|
func _fit_canvas(nodes: Array) -> void:
|
||||||
|
if nodes.is_empty():
|
||||||
|
return
|
||||||
|
var layer := {}
|
||||||
|
var slot := {}
|
||||||
|
_assign_layers(nodes, layer, slot)
|
||||||
|
var maxl := 0
|
||||||
|
var maxslot := 0
|
||||||
|
for k in layer.keys():
|
||||||
|
maxl = maxi(maxl, int(layer[k]))
|
||||||
|
for k in slot.keys():
|
||||||
|
maxslot = maxi(maxslot, int(slot[k]))
|
||||||
|
_content_size = Vector2(70 + maxl * 122 + NODE_HALF.x + 8.0, 52 + maxslot * 58 + NODE_HALF.y + 8.0)
|
||||||
|
_content_size = _content_size.max(Vector2(620, 240)) # 下限 = 旧定值画布(小图观感不变)
|
||||||
|
_canvas.custom_minimum_size = _content_size
|
||||||
|
|
||||||
|
|
||||||
# 状态栏刷新(命令式纪律——组合根打开本屏/兑付确认回执后重查;零推送信号订阅)
|
# 状态栏刷新(命令式纪律——组合根打开本屏/兑付确认回执后重查;零推送信号订阅)
|
||||||
func _refresh_status() -> void:
|
func _refresh_status() -> void:
|
||||||
if _runner == null:
|
if _runner == null:
|
||||||
@@ -249,13 +271,13 @@ func _on_canvas_input(ev: InputEvent) -> void:
|
|||||||
if _runner == null or not visible:
|
if _runner == null or not visible:
|
||||||
return
|
return
|
||||||
if ev is InputEventMouseMotion:
|
if ev is InputEventMouseMotion:
|
||||||
var nid := _candidate_at(_canvas.get_local_mouse_position())
|
var nid := _candidate_at(ev.position) # gui_input 事件坐标 = 接收控件局部坐标(push_input 同成立)
|
||||||
if nid != _hover_id:
|
if nid != _hover_id:
|
||||||
_hover_id = nid
|
_hover_id = nid
|
||||||
_canvas.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND if nid >= 0 else Control.CURSOR_ARROW
|
_canvas.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND if nid >= 0 else Control.CURSOR_ARROW
|
||||||
_canvas.queue_redraw()
|
_canvas.queue_redraw()
|
||||||
elif ev is InputEventMouseButton and ev.button_index == MOUSE_BUTTON_LEFT and ev.pressed:
|
elif ev is InputEventMouseButton and ev.button_index == MOUSE_BUTTON_LEFT and ev.pressed:
|
||||||
var nid2 := _candidate_at(_canvas.get_local_mouse_position())
|
var nid2 := _candidate_at(ev.position)
|
||||||
if nid2 >= 0:
|
if nid2 >= 0:
|
||||||
_submit_choice(nid2)
|
_submit_choice(nid2)
|
||||||
|
|
||||||
@@ -349,9 +371,18 @@ func _draw_graph(canvas: Control) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _layout(nodes: Array, canvas: Control) -> Dictionary:
|
func _layout(nodes: Array, canvas: Control) -> Dictionary:
|
||||||
# 分层:layer = 入口 BFS 深度;同层 slot 垂直排布
|
|
||||||
var layer := {}
|
var layer := {}
|
||||||
var slot := {}
|
var slot := {}
|
||||||
|
_assign_layers(nodes, layer, slot)
|
||||||
|
var pos := {}
|
||||||
|
var origin := (canvas.size - _content_size) / 2.0
|
||||||
|
for node in nodes:
|
||||||
|
pos[node] = origin + Vector2(70 + int(layer[node]) * 122, 52 + int(slot[node]) * 58)
|
||||||
|
return pos
|
||||||
|
|
||||||
|
|
||||||
|
# 分层(layout/fit 共享件):layer = 入口 BFS 深度;同层 slot 垂直排布;未达节点兜底排末层
|
||||||
|
func _assign_layers(nodes: Array, layer: Dictionary, slot: Dictionary) -> void:
|
||||||
var layer_counts := {}
|
var layer_counts := {}
|
||||||
var entry := _entry(nodes)
|
var entry := _entry(nodes)
|
||||||
var queue: Array = []
|
var queue: Array = []
|
||||||
@@ -369,7 +400,6 @@ func _layout(nodes: Array, canvas: Control) -> Dictionary:
|
|||||||
continue
|
continue
|
||||||
layer[target] = l + 1
|
layer[target] = l + 1
|
||||||
queue.append(target)
|
queue.append(target)
|
||||||
# 未达节点(理论不可达——校验已拦)兜底排末层
|
|
||||||
var max_layer := 0
|
var max_layer := 0
|
||||||
for k in layer.keys():
|
for k in layer.keys():
|
||||||
max_layer = maxi(max_layer, int(layer[k]))
|
max_layer = maxi(max_layer, int(layer[k]))
|
||||||
@@ -378,11 +408,6 @@ func _layout(nodes: Array, canvas: Control) -> Dictionary:
|
|||||||
layer[node] = max_layer + 1
|
layer[node] = max_layer + 1
|
||||||
slot[node] = int(layer_counts.get(layer[node], 0))
|
slot[node] = int(layer_counts.get(layer[node], 0))
|
||||||
layer_counts[layer[node]] = int(layer_counts.get(layer[node], 0)) + 1
|
layer_counts[layer[node]] = int(layer_counts.get(layer[node], 0)) + 1
|
||||||
var pos := {}
|
|
||||||
var origin := (canvas.size - Vector2(620, 240)) / 2.0
|
|
||||||
for node in nodes:
|
|
||||||
pos[node] = origin + Vector2(70 + int(layer[node]) * 122, 52 + int(slot[node]) * 58)
|
|
||||||
return pos
|
|
||||||
|
|
||||||
|
|
||||||
func _entry(nodes: Array) -> Dictionary:
|
func _entry(nodes: Array) -> Dictionary:
|
||||||
|
|||||||
@@ -27,10 +27,70 @@ func _ready() -> void:
|
|||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
main._start_adventure_from_menu()
|
main._start_adventure_from_menu()
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
|
# ---- ⑫ 图上命中真实输入(关卡画面实装批 2026-09-28 回归闸):入口停点候选节点中心
|
||||||
|
# push_input 真实鼠标路径 motion+click → 悬停预承诺置位 → route_chosen 到达组合根。
|
||||||
|
# 由来=真机 bug:深层节点(L1 Boss 内容 x=802)在固定画布命中区外,press_candidate 直调
|
||||||
|
# 命中函数探针全绿、真鼠标点不中——本 case 封住输入管线,⑬封住几何拟合。
|
||||||
if main.route_overlay.visible:
|
if main.route_overlay.visible:
|
||||||
main.route_overlay.press_candidate(0) # 入口选路停点 → 首战
|
var ro = main.route_overlay
|
||||||
|
var cands: Array = main.runner.next_candidate_ids()
|
||||||
|
var nds: Array = main.runner.nodes()
|
||||||
|
var tgt := {}
|
||||||
|
for n in nds:
|
||||||
|
if main.runner.node_id_of(n) == int(cands[0]):
|
||||||
|
tgt = n
|
||||||
|
var npos: Vector2 = ro._layout(nds, ro._canvas)[tgt]
|
||||||
|
var gpos: Vector2 = ro._canvas.get_global_position() + npos
|
||||||
|
var evm := InputEventMouseMotion.new()
|
||||||
|
evm.position = gpos
|
||||||
|
evm.global_position = gpos
|
||||||
|
main.get_viewport().push_input(evm, true)
|
||||||
|
await get_tree().process_frame
|
||||||
|
if int(ro._hover_id) != int(cands[0]):
|
||||||
|
_pre_fail("图上悬停预承诺未置位(hover=%s 候选=%s)" % [str(ro._hover_id), str(cands[0])])
|
||||||
|
var evc := InputEventMouseButton.new()
|
||||||
|
evc.button_index = MOUSE_BUTTON_LEFT
|
||||||
|
evc.pressed = true
|
||||||
|
evc.position = gpos
|
||||||
|
evc.global_position = gpos
|
||||||
|
main.get_viewport().push_input(evc, true)
|
||||||
|
await get_tree().process_frame
|
||||||
|
await get_tree().process_frame
|
||||||
|
if main.route_overlay.visible or main.runner.awaiting_choice():
|
||||||
|
_pre_fail("图上真实点击未提交选路(overlay 可见=%s 选路态=%s)" % [str(main.route_overlay.visible), str(main.runner.awaiting_choice())])
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
|
# ---- ⑬ 画布随图拟合(同 bug 的几何面回归):复刻分层公式独立推导最深节点内容 x,
|
||||||
|
# 断言画布 min 尺寸覆盖之(open_choices 每 stop 拟合——L1 单链 Boss x=70+6*122=802 曾越界)
|
||||||
|
var ro2 = main.route_overlay
|
||||||
|
var nds2: Array = main.runner.nodes()
|
||||||
|
var layer2 := {}
|
||||||
|
var inbound2 := {}
|
||||||
|
for n2 in nds2:
|
||||||
|
for tid2 in main.runner.node_out_ids(n2):
|
||||||
|
inbound2[tid2] = true
|
||||||
|
var queue2: Array = []
|
||||||
|
for n2 in nds2:
|
||||||
|
if not inbound2.has(main.runner.node_id_of(n2)):
|
||||||
|
queue2.append(n2)
|
||||||
|
layer2[n2] = 0
|
||||||
|
while not queue2.is_empty():
|
||||||
|
var cur2: Dictionary = queue2.pop_front()
|
||||||
|
for tid2 in main.runner.node_out_ids(cur2):
|
||||||
|
var tg2 := {}
|
||||||
|
for n2 in nds2:
|
||||||
|
if main.runner.node_id_of(n2) == tid2:
|
||||||
|
tg2 = n2
|
||||||
|
if tg2.is_empty() or layer2.has(tg2):
|
||||||
|
continue
|
||||||
|
layer2[tg2] = int(layer2[cur2]) + 1
|
||||||
|
queue2.append(tg2)
|
||||||
|
var maxl2 := 0
|
||||||
|
for k2 in layer2.keys():
|
||||||
|
maxl2 = maxi(maxl2, int(layer2[k2]))
|
||||||
|
var need_x2: float = 70.0 + maxl2 * 122.0 + 26.0 + 8.0
|
||||||
|
if float(ro2._canvas.custom_minimum_size.x) < need_x2 - 0.5:
|
||||||
|
_pre_fail("画布拟合不足(min.x=%.0f < 最深内容 %.0f——深层节点不可点回归)" % [ro2._canvas.custom_minimum_size.x, need_x2])
|
||||||
var bv = main.board_view
|
var bv = main.board_view
|
||||||
var ms = main.settlement.minesweeper if main.settlement != null else null
|
var ms = main.settlement.minesweeper if main.settlement != null else null
|
||||||
if ms == null or bv._cells.is_empty():
|
if ms == null or bv._cells.is_empty():
|
||||||
@@ -234,7 +294,7 @@ func _ready() -> void:
|
|||||||
bv._hero_tex_idle = tex_saved
|
bv._hero_tex_idle = tex_saved
|
||||||
bv.refresh()
|
bv.refresh()
|
||||||
if _problems.is_empty():
|
if _problems.is_empty():
|
||||||
print("[ui_input_probe] PASS — 悬停金框/禁入信号/路径预览/flag_place/chord/dig/英雄落位/影子构成/朝向/缩格重排/缺位回退 十一断言全绿(真实 push_input 路径)")
|
print("[ui_input_probe] PASS — 悬停金框/禁入信号/路径预览/flag_place/chord/dig/英雄落位/影子构成/朝向/缩格重排/缺位回退 十三断言全绿(真实 push_input 路径——⑫图上命中⑬画布拟合 2026-09-28 增)")
|
||||||
get_tree().quit(0)
|
get_tree().quit(0)
|
||||||
else:
|
else:
|
||||||
for p in _problems:
|
for p in _problems:
|
||||||
|
|||||||
Reference in new issue
Block a user