fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# motion_event_fly_test —— 40250 local MotionEventType=6 parity。
|
||||
# `.msa` FLY 帧必须按 FlyFileName 创建本地弹道;CG_SHOOT 仍由 GameScene
|
||||
# 单独发送,服务器 GC_CREATE_FLY 不应被拿来替代这条本地视觉路径。
|
||||
extends SceneTree
|
||||
|
||||
const AssetRoot = preload("res://asset_root.gd")
|
||||
const FlyObject = preload("res://fly_object.gd")
|
||||
const NetWorld = preload("res://net_world.gd")
|
||||
const GameScene = preload("res://game_scene.gd")
|
||||
|
||||
class Client extends Node:
|
||||
var shots: Array[int] = []
|
||||
func get_main_vid() -> int:
|
||||
return 1
|
||||
func shoot(skill: int) -> bool:
|
||||
shots.append(skill)
|
||||
return true
|
||||
|
||||
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:
|
||||
await process_frame
|
||||
var mount := Node3D.new()
|
||||
get_root().add_child(mount)
|
||||
var world := NetWorld.new()
|
||||
get_root().add_child(world)
|
||||
world.parent = mount
|
||||
world._assets_root = AssetRoot.path()
|
||||
var manager := FlyObject.new()
|
||||
manager.manual_step = true
|
||||
get_root().add_child(manager)
|
||||
manager.setup(mount, null, AssetRoot.path())
|
||||
world._fly_mgr = manager
|
||||
|
||||
var actor := Node3D.new()
|
||||
actor.position = Vector3(0, 1, 0)
|
||||
get_root().add_child(actor)
|
||||
var target := Node3D.new()
|
||||
target.position = Vector3(0, 1, -8)
|
||||
get_root().add_child(target)
|
||||
var event := {
|
||||
"type": 6,
|
||||
"fly_file": "d:/ymir work/pc/assassin/effect/arrow_01_fast.msf",
|
||||
"fly_pos": Vector3.ZERO,
|
||||
"fly_bone": "",
|
||||
"fly_attaching": false,
|
||||
}
|
||||
check(world.spawn_motion_fly(actor, event, target, 7),
|
||||
"valid FlyFileName creates the local motion projectile")
|
||||
check(manager.active_count() == 1, "local motion projectile enters FlyManager once")
|
||||
if manager.active_count() == 1:
|
||||
var before: Vector3 = manager._instances[0].pos
|
||||
manager.step(0.01)
|
||||
check(manager._instances[0].pos != before, "local motion projectile advances")
|
||||
var missing := event.duplicate()
|
||||
missing.fly_file = "d:/ymir work/pc/assassin/effect/missing.msf"
|
||||
check(not world.spawn_motion_fly(actor, missing, target),
|
||||
"missing FlyFileName follows reference create failure")
|
||||
check(manager.active_count() == 1, "missing fly resource does not add a duplicate instance")
|
||||
|
||||
# GameScene keeps the two 40250 side effects separate: one local visual
|
||||
# instance plus one queued CG_SHOOT at the same motion frame.
|
||||
manager.clear_for_map_change()
|
||||
var game := GameScene.new()
|
||||
var client := Client.new()
|
||||
get_root().add_child(game)
|
||||
get_root().add_child(client)
|
||||
game.client = client
|
||||
game.net_world = world
|
||||
game.player = actor
|
||||
game._mount = mount
|
||||
game._motion_effect_target = weakref(target)
|
||||
game._queue_shot(7)
|
||||
game._on_local_motion_event_detailed(event)
|
||||
check(client.shots == [7], "FLY event still sends exactly one queued CG_SHOOT")
|
||||
check(manager.active_count() == 1, "FLY event creates exactly one local projectile")
|
||||
game.free()
|
||||
client.free()
|
||||
|
||||
manager.clear_for_map_change()
|
||||
manager.free()
|
||||
actor.free()
|
||||
target.free()
|
||||
world.free()
|
||||
mount.free()
|
||||
await process_frame
|
||||
if failures == 0:
|
||||
print("PASS: motion_event_fly_test")
|
||||
quit(1 if failures else 0)
|
||||
Reference in New Issue
Block a user