- 桥梁与静态物体高度采样修复: - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程 - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题 - 新增 test_bridge_height_parity.gd 自动化对拍测试 - 40250 怪物击杀经验动效: - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附 - 40250 客户端全系统功能对齐(Batches 1-31): - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试 - 文档沉淀: - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
73 lines
2.4 KiB
GDScript
73 lines
2.4 KiB
GDScript
extends SceneTree
|
|
|
|
const CharSelectScreen = preload("res://ui/char_select_screen.gd")
|
|
|
|
class FakeClient extends Node:
|
|
func get_slot_count() -> int: return 4
|
|
func get_empire() -> int: return 1
|
|
|
|
var _fail := 0
|
|
func _ck(c: bool, m: String) -> void:
|
|
if not c:
|
|
_fail += 1
|
|
printerr("FAIL: " + m)
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
func _run() -> void:
|
|
var client := FakeClient.new()
|
|
root.add_child(client)
|
|
var assets := AssetRoot.path()
|
|
var screen := CharSelectScreen.new()
|
|
root.add_child(screen)
|
|
|
|
screen.setup(client, assets, [
|
|
{"index": 0, "name": "WarriorTest", "job": 0, "level": 50, "guild_name": "TestGuild",
|
|
"play_minutes": 120, "ht": 40, "iq": 20, "st": 50, "dx": 30, "main_part": 0, "hair_part": 0}
|
|
])
|
|
|
|
for _i in 10:
|
|
await process_frame
|
|
|
|
_ck(screen._pv != null, "PlayerView created on stage")
|
|
_ck(not screen._starting, "Screen not initially starting")
|
|
_ck(screen._pv.anim != null and String(screen._pv.anim.get("anim_path")).ends_with("intro/wait.msa"),
|
|
"Initial animation is wait.msa")
|
|
_ck(bool(screen._pv.anim.get("loop")) == true, "wait.msa is looping")
|
|
|
|
var result := {"slot": -1}
|
|
screen.select_requested.connect(func(slot: int):
|
|
print("Test received select_requested signal with slot = ", slot)
|
|
result.slot = slot
|
|
)
|
|
|
|
# 触发开始流程
|
|
screen._do_start()
|
|
|
|
_ck(screen._starting, "Screen entered _starting state")
|
|
_ck(screen._btn_start.disabled, "Start button disabled during selection")
|
|
_ck(screen._pv.anim != null and String(screen._pv.anim.get("anim_path")).ends_with("intro/selected.msa"),
|
|
"Selected animation changed to intro/selected.msa")
|
|
_ck(bool(screen._pv.anim.get("loop")) == false, "intro/selected.msa is non-looping")
|
|
|
|
# 40250 原生逻辑:选角开始时仅播放角色动作与语音,无脚底光环粒子特效
|
|
var fx_nodes: Array = screen._pivot.find_children("fx_*", "Node3D", true, false)
|
|
_ck(fx_nodes.is_empty(), "40250 parity: no extraneous ground particle halo spawned")
|
|
|
|
# 等待动作完成与 select_requested 信号触发(基于真实时间)
|
|
var t0 := Time.get_ticks_msec()
|
|
var frames := 0
|
|
while result.slot == -1 and (Time.get_ticks_msec() - t0) < 4000:
|
|
await process_frame
|
|
frames += 1
|
|
|
|
_ck(result.slot == 0, "select_requested emitted with slot 0 after animation finishes")
|
|
|
|
if _fail == 0:
|
|
print("PASS: char_select_start_motion_test (selected.msa motion + sound script + 40250 clean parity)")
|
|
quit(0)
|
|
else:
|
|
printerr("%d check(s) failed" % _fail)
|
|
quit(1)
|