- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
117 lines
3.4 KiB
GDScript
117 lines
3.4 KiB
GDScript
# 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()
|