- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
142 lines
3.9 KiB
GDScript
142 lines
3.9 KiB
GDScript
# remote_motion_splash_test —— 40250 ActorInstanceMotionEvent::SPECIAL_ATTACKING
|
|
# / InstanceBase::AttackProcess 的远端 actor 回归。
|
|
#
|
|
# 远端攻击事件必须建立同样的 SplashArea 并执行碰撞、命中上限和去重;
|
|
# 但远端事件不能伪造本地主角的 CG_ATTACK。
|
|
extends SceneTree
|
|
|
|
const NetPlay = preload("res://net_play.gd")
|
|
|
|
class HitActor extends Node3D:
|
|
var hits := 0
|
|
var delays: Array = []
|
|
var shakes := 0
|
|
var spheres := [{"radius": 60.0, "pos": Vector3.ZERO, "bone": ""}]
|
|
|
|
func get_defending_spheres() -> Array:
|
|
return spheres
|
|
|
|
func insert_delay(value: float) -> void:
|
|
delays.append(value)
|
|
|
|
func hit_good(_scalar: float, _stunned: bool) -> void:
|
|
hits += 1
|
|
|
|
func shake() -> void:
|
|
shakes += 1
|
|
|
|
class ClientStub extends Node:
|
|
var entities: Dictionary = {}
|
|
var attacks: Array = []
|
|
|
|
func is_in_game() -> bool:
|
|
return true
|
|
|
|
func get_entity(vid: int) -> Dictionary:
|
|
return entities.get(vid, {})
|
|
|
|
func get_entities() -> Array:
|
|
return entities.values()
|
|
|
|
func attack(skill: int, vid: int) -> bool:
|
|
attacks.append([skill, vid])
|
|
return true
|
|
|
|
class PCStub extends Node:
|
|
var player: Node3D
|
|
|
|
class WorldStub extends Node:
|
|
var nodes: Dictionary = {}
|
|
|
|
func node_for(vid: int) -> Node3D:
|
|
return nodes.get(vid, null)
|
|
|
|
var _fail := 0
|
|
|
|
func _ck(ok: bool, message: String) -> void:
|
|
if not ok:
|
|
_fail += 1
|
|
printerr("FAIL: " + message)
|
|
|
|
func _init() -> void:
|
|
await _run()
|
|
if _fail == 0:
|
|
print("PASS: remote_motion_splash_test (remote SPECIAL_ATTACKING)")
|
|
quit(0)
|
|
else:
|
|
printerr("%d check(s) failed" % _fail)
|
|
quit(1)
|
|
|
|
func _run() -> void:
|
|
var client := ClientStub.new()
|
|
var pc := PCStub.new()
|
|
var world := WorldStub.new()
|
|
var local := HitActor.new()
|
|
var remote := HitActor.new()
|
|
local.position = Vector3(-1.0, 0.0, 0.0)
|
|
remote.position = Vector3.ZERO
|
|
pc.player = local
|
|
for n in [client, pc, world, local, remote]:
|
|
get_root().add_child(n)
|
|
|
|
client.entities[1] = {"vid": 1, "kind": 0, "race": 0, "dead": false,
|
|
"in_safe": false, "pk_mode": 0}
|
|
client.entities[2] = {"vid": 2, "kind": 2, "race": 101, "dead": false,
|
|
"in_safe": false, "owner_vid": 0}
|
|
world.nodes[1] = local
|
|
world.nodes[2] = remote
|
|
|
|
var np: Node = NetPlay.new()
|
|
get_root().add_child(np)
|
|
np.client = client
|
|
np.pc = pc
|
|
np.net_world = world
|
|
np._main_vid = 1
|
|
np._local_time = 1.0
|
|
|
|
var event := {
|
|
"type": 4,
|
|
"skill_index": 51,
|
|
"duration_time": 0.5,
|
|
"enable_hit_process": true,
|
|
"attack_type": 0,
|
|
"hitting_type": 2,
|
|
"stiffen_time": 0.1,
|
|
"invisible_time": 0.2,
|
|
"external_force": 0.0,
|
|
"hit_limit_count": 1,
|
|
"collision_spheres": [{"radius": 60.0, "pos": Vector3(100, 0, 0)}],
|
|
}
|
|
np.start_remote_motion_splash(2, event)
|
|
_ck(np._remote_motion_splashes.has(2),
|
|
"remote SPECIAL_ATTACKING creates an actor-owned SplashArea")
|
|
|
|
np._remote_splash_attack_process()
|
|
_ck(local.hits == 1, "remote splash applies the normal hit reaction to the local actor")
|
|
_ck(local.delays.size() == 1 and is_equal_approx(float(local.delays[0]), 0.1),
|
|
"remote splash applies the server motion stiffen time")
|
|
_ck(remote.delays.size() == 1 and is_equal_approx(float(remote.delays[0]), 0.1),
|
|
"remote splash inserts delay on the attacking actor, not the local input actor")
|
|
_ck(client.attacks.is_empty(),
|
|
"remote splash does not synthesize a local CG_ATTACK packet")
|
|
|
|
# Clearing the victim's temporary invisible window must still not produce a
|
|
# second hit: CActorInstance::m_HitDataMap deduplicates the same VID.
|
|
np._victim_invisible_until.clear()
|
|
np._remote_splash_attack_process()
|
|
_ck(local.hits == 1, "remote splash deduplicates a victim for the area lifetime")
|
|
_ck(np._remote_motion_splashes.has(2), "active remote splash remains until DuringTime expires")
|
|
|
|
np._local_time = 1.5
|
|
np._remote_splash_attack_process()
|
|
_ck(not np._remote_motion_splashes.has(2),
|
|
"remote splash is cleaned up at DuringTime")
|
|
|
|
np.free()
|
|
remote.free()
|
|
local.free()
|
|
world.free()
|
|
pc.free()
|
|
client.free()
|
|
await process_frame
|