Files
mtgodot-poc/project/remote_motion_splash_test.gd
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

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