fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
# motion_event_warp_test —— 40250 MotionEventType 9 / ProcessMotionEventWarp.
|
||||
# The reference moves the actor to target - normalize(target-actor)*270cm,
|
||||
# rejects the move on a blocked map cell, always turns toward the target, and
|
||||
# calls OnWarp -> SendCharacterStatePacket(FUNC_WAIT).
|
||||
extends SceneTree
|
||||
|
||||
const AssetRoot = preload("res://asset_root.gd")
|
||||
const GameScene = preload("res://game_scene.gd")
|
||||
const NetPlay = preload("res://net_play.gd")
|
||||
|
||||
class WarpWorld extends Node:
|
||||
var blocked := false
|
||||
var probes: Array[Vector2] = []
|
||||
|
||||
func is_blocked(x: float, z: float) -> bool:
|
||||
probes.append(Vector2(x, z))
|
||||
return blocked
|
||||
|
||||
class WarpNetPlay extends Node:
|
||||
var states: Array = []
|
||||
|
||||
func _send_state(func_id: int, arg: int, position: Vector3) -> void:
|
||||
states.append([func_id, arg, position])
|
||||
|
||||
var failures := 0
|
||||
|
||||
func check(ok: bool, label: String) -> void:
|
||||
if not ok:
|
||||
failures += 1
|
||||
printerr("FAIL: " + label)
|
||||
|
||||
func _init() -> void:
|
||||
call_deferred("run")
|
||||
|
||||
func run() -> void:
|
||||
var game := GameScene.new()
|
||||
var world := WarpWorld.new()
|
||||
var net_play := WarpNetPlay.new()
|
||||
var actor := Node3D.new()
|
||||
var target := Node3D.new()
|
||||
get_root().add_child(game)
|
||||
get_root().add_child(world)
|
||||
get_root().add_child(net_play)
|
||||
get_root().add_child(actor)
|
||||
get_root().add_child(target)
|
||||
game.player = actor
|
||||
game.world = world
|
||||
game.net_play = net_play
|
||||
actor.global_position = Vector3(0.0, 1.0, 0.0)
|
||||
target.global_position = Vector3(10.0, 3.0, 0.0)
|
||||
game._motion_effect_target = weakref(target)
|
||||
|
||||
# The native parser must expose a real type-9 event from the reference asset.
|
||||
if ClassDB.class_exists("Metin2AnimPlayer"):
|
||||
var anim: Node = ClassDB.instantiate("Metin2AnimPlayer")
|
||||
get_root().add_child(anim)
|
||||
anim.set("anim_path", AssetRoot.path().path_join("PC/ymir work/pc/assassin/skill/seomjeon.msa"))
|
||||
await process_frame
|
||||
var found := false
|
||||
for event in anim.call("get_events"):
|
||||
if event is Dictionary and int(event.get("type", -1)) == 9:
|
||||
found = true
|
||||
check(found, "seomjeon.msa exposes MotionEventType 9")
|
||||
anim.free()
|
||||
|
||||
# Allowed path: target - 2.70m along the complete 3D target vector.
|
||||
game._on_local_motion_event_detailed({"type": 9, "sound": "", "pos": Vector3.ZERO})
|
||||
var expected := target.global_position - (target.global_position - Vector3(0.0, 1.0, 0.0)).normalized() * 2.70
|
||||
check(actor.global_position.is_equal_approx(expected),
|
||||
"WARP moves to target minus the 270cm reference distance")
|
||||
check(is_equal_approx(actor.rotation.y, PI * 0.5),
|
||||
"WARP turns toward target on the ground plane")
|
||||
check(net_play.states.size() == 1 and int(net_play.states[0][0]) == NetPlay.FUNC_WAIT,
|
||||
"WARP emits one FUNC_WAIT state through OnWarp")
|
||||
check(world.probes.size() == 1 and world.probes[0].is_equal_approx(Vector2(expected.x, expected.z)),
|
||||
"WARP checks the exact destination against map blocking")
|
||||
|
||||
# Blocked path: orientation and OnWarp still happen, but SetPixelPosition does not.
|
||||
world.blocked = true
|
||||
actor.global_position = Vector3.ZERO
|
||||
actor.rotation.y = 0.0
|
||||
net_play.states.clear()
|
||||
game._on_local_motion_event_detailed({"type": 9, "sound": "", "pos": Vector3.ZERO})
|
||||
check(actor.global_position == Vector3.ZERO, "blocked WARP keeps the actor position")
|
||||
check(is_equal_approx(actor.rotation.y, PI * 0.5), "blocked WARP still turns toward target")
|
||||
check(net_play.states.size() == 1, "blocked WARP still reports OnWarp once")
|
||||
|
||||
# No target: the reference returns without moving or reporting OnWarp.
|
||||
game._motion_effect_target = null
|
||||
net_play.states.clear()
|
||||
actor.global_position = Vector3.ZERO
|
||||
game._on_local_motion_event_detailed({"type": 9, "sound": "", "pos": Vector3.ZERO})
|
||||
check(actor.global_position == Vector3.ZERO and net_play.states.is_empty(),
|
||||
"WARP without a valid fly target is a no-op")
|
||||
|
||||
game.free()
|
||||
world.free()
|
||||
net_play.free()
|
||||
actor.free()
|
||||
target.free()
|
||||
await process_frame
|
||||
if failures == 0:
|
||||
print("PASS: motion_event_warp_test")
|
||||
quit(1 if failures else 0)
|
||||
Reference in New Issue
Block a user