fix: 装备属性面板避让逻辑 + 多项功能更新

- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
This commit is contained in:
shen
2026-09-21 16:38:59 -07:00
parent 1522a8a1a1
commit c93894313a
5498 changed files with 4558004 additions and 1442 deletions
+116
View File
@@ -0,0 +1,116 @@
# fly_dynamic_collision_test —— 40250 FlyingInstance 动态 Actor 碰撞回归。
#
# 参考 FlyingInstance.cpp::Update
# collision sphere = max(move_distance * 2, configured radius)
# hitonanothermonster -> HittedObjectSet 去重 -> OnExplodingAtAnotherTarget
# pierce 后继续飞行,耗尽才爆炸。
extends SceneTree
const FlyObject = preload("res://fly_object.gd")
var _failures := 0
func _ck(condition: bool, message: String) -> void:
if condition:
return
_failures += 1
printerr("FAIL: ", message)
func _init() -> void:
await _run()
quit(1 if _failures else 0)
func _run() -> void:
await process_frame
_test_two_actors_and_pierce()
_test_same_actor_is_deduplicated()
if _failures == 0:
print("PASS: fly_dynamic_collision_test")
func _new_manager() -> Array:
var mount := Node3D.new()
get_root().add_child(mount)
var manager := FlyObject.new()
manager.manual_step = true
mount.add_child(manager)
manager.setup(mount)
return [manager, mount]
func _test_two_actors_and_pierce() -> void:
var fixture := _new_manager()
var manager: FlyObject = fixture[0]
var mount: Node3D = fixture[1]
var first := Node3D.new()
first.position = Vector3(0.0, 1.0, 5.0)
get_root().add_child(first)
var second := Node3D.new()
second.position = Vector3(0.0, 1.0, 10.0)
get_root().add_child(second)
manager.set_actor_provider(func() -> Array:
return [
{"vid": 200, "node": first, "center": first.position, "radius": 0.35},
{"vid": 300, "node": second, "center": second.position, "radius": 0.35},
]
)
var events: Array[int] = []
var data := FlyObject.FlyData.new()
data.init_vel = 10.0
data.flat_range = 30.0
data.bomb_range = 0.05
data.collision_sphere_radius = 0.05
data.hit_on_another_monster = true
data.pierce_count = 1
var inst = manager.spawn(Vector3(0.0, 1.0, 0.0), Vector3(0.0, 1.0, 30.0), false, data)
inst.handler = func(event: String, _pos: Vector3, vid: int) -> void:
if event == "at_another":
events.append(vid)
for _i in 100:
if manager.active_count() == 0:
break
manager.step(0.1)
_ck(events == [200, 300], "pierce projectile hits first and second Actor exactly once")
_ck(manager.active_count() == 0, "non-piercing second Actor hit removes projectile")
manager.clear_for_map_change()
first.free()
second.free()
manager.free()
mount.free()
func _test_same_actor_is_deduplicated() -> void:
var fixture := _new_manager()
var manager: FlyObject = fixture[0]
var mount: Node3D = fixture[1]
var target := Node3D.new()
target.position = Vector3(0.0, 1.0, 5.0)
get_root().add_child(target)
manager.set_actor_provider(func() -> Array:
return [{"vid": 400, "node": target, "center": target.position, "radius": 0.35}]
)
var events: Array[int] = []
var data := FlyObject.FlyData.new()
data.init_vel = 10.0
data.flat_range = 12.0
data.bomb_range = 0.05
data.collision_sphere_radius = 0.05
data.hit_on_another_monster = true
data.pierce_count = 2
var inst = manager.spawn(Vector3(0.0, 1.0, 0.0), Vector3(0.0, 1.0, 30.0), false, data)
inst.handler = func(event: String, _pos: Vector3, vid: int) -> void:
if event == "at_another":
events.append(vid)
for _i in 100:
if manager.active_count() == 0:
break
manager.step(0.1)
_ck(events == [400], "same dynamic Actor is blocked by HittedObjectSet")
_ck(manager.active_count() == 0, "deduplicated projectile eventually expires")
manager.clear_for_map_change()
target.free()
manager.free()
mount.free()