- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
144 lines
5.1 KiB
GDScript
144 lines
5.1 KiB
GDScript
# test_gm_toolkit_parity.gd —— 40250 单机内置 GM 命令解析控制台 1:1 纯净回归测试套件
|
|
extends SceneTree
|
|
|
|
const GmToolkitSystem = preload("res://gm_toolkit_system.gd")
|
|
|
|
var _passed := 0
|
|
var _failed := 0
|
|
|
|
func _assert(cond: bool, msg: String) -> void:
|
|
if cond:
|
|
_passed += 1
|
|
print(" [PASS] %s" % msg)
|
|
else:
|
|
_failed += 1
|
|
printerr(" [FAIL] %s" % msg)
|
|
|
|
func _init() -> void:
|
|
print("\n=== Running test_gm_toolkit_parity (Direction 4: GM Toolkit Console 1:1) ===")
|
|
_test_item_command()
|
|
_test_level_and_gold_commands()
|
|
_test_set_skill_command()
|
|
_test_warp_and_mob_commands()
|
|
_test_help_and_unknown_command()
|
|
_finish()
|
|
|
|
func _test_item_command() -> void:
|
|
print("\n--- Test 1: /item Command Execution ---")
|
|
var gm := GmToolkitSystem.new()
|
|
var inv: Array = [null, null]
|
|
var ctx := {"inventory": inv}
|
|
|
|
# 1. Spawn Battle Sword +9 (169)
|
|
var r1 = gm.execute_command("/item 169 1", ctx)
|
|
_assert(r1["ok"] == true, "/item 169 1 succeeded")
|
|
_assert(inv[0] != null and inv[0]["vnum"] == 169, "Battle Sword spawned in slot 0")
|
|
|
|
# 2. Spawn 5x Soul Stones (50513)
|
|
var r2 = gm.execute_command("/item 50513 5", ctx)
|
|
_assert(r2["ok"] == true, "/item 50513 5 succeeded")
|
|
_assert(inv[1] != null and inv[1]["vnum"] == 50513 and inv[1]["count"] == 5, "5x Soul Stones spawned in slot 1")
|
|
|
|
# 3. Full inventory
|
|
var r3 = gm.execute_command("/item 10 1", ctx)
|
|
_assert(r3["ok"] == false and r3["reason"] == "INVENTORY_FULL", "Blocked when inventory full")
|
|
|
|
# 4. Syntax error
|
|
var r4 = gm.execute_command("/item", ctx)
|
|
_assert(r4["ok"] == false and r4["reason"] == "SYNTAX_ERROR", "Syntax error caught on empty args")
|
|
|
|
func _test_level_and_gold_commands() -> void:
|
|
print("\n--- Test 2: /level and /gold Commands ---")
|
|
var gm := GmToolkitSystem.new()
|
|
var pdata := {"level": 1, "gold": 0}
|
|
var ctx := {"player_data": pdata}
|
|
|
|
# /level 75
|
|
var r_lv = gm.execute_command("/level 75", ctx)
|
|
_assert(r_lv["ok"] == true and pdata["level"] == 75, "Level set to 75")
|
|
|
|
# /level 200 (clamped to 105)
|
|
gm.execute_command("/level 200", ctx)
|
|
_assert(pdata["level"] == 105, "Level clamped to max 105")
|
|
|
|
# /gold 1000000
|
|
var r_gold = gm.execute_command("/gold 1000000", ctx)
|
|
_assert(r_gold["ok"] == true and pdata["gold"] == 1000000, "1,000,000 Yang injected")
|
|
|
|
func _test_set_skill_command() -> void:
|
|
print("\n--- Test 3: /set_skill Command (M1, G1, P grades) ---")
|
|
var gm := GmToolkitSystem.new()
|
|
var skills := {}
|
|
var ctx := {"skills": skills}
|
|
|
|
# Set skill 1 to M1 (Lv 20)
|
|
var r_m1 = gm.execute_command("/set_skill 1 20", ctx)
|
|
_assert(r_m1["ok"] == true and skills[1] == 20, "Skill 1 set to 20")
|
|
_assert(r_m1["grade"].begins_with("M级"), "Grade is M")
|
|
|
|
# Set skill 1 to P (Lv 40)
|
|
var r_p = gm.execute_command("/set_skill 1 40", ctx)
|
|
_assert(r_p["ok"] == true and skills[1] == 40, "Skill 1 set to 40 (P-grade)")
|
|
_assert(r_p["grade"].begins_with("P级"), "Grade is P")
|
|
|
|
func _test_warp_and_mob_commands() -> void:
|
|
print("\n--- Test 4: /warp, /map, and /spawn_mob Commands ---")
|
|
var gm := GmToolkitSystem.new()
|
|
var ctx := {"player_pos": Vector3(10.0, 0.0, 20.0)}
|
|
|
|
var warp_signal := {"fired": false, "pos": Vector3.ZERO, "map": ""}
|
|
gm.player_warped.connect(func(pos: Vector3, mname: String):
|
|
warp_signal["fired"] = true
|
|
warp_signal["pos"] = pos
|
|
warp_signal["map"] = mname
|
|
)
|
|
|
|
# /map sohan
|
|
var r_map = gm.execute_command("/map sohan", ctx)
|
|
_assert(r_map["ok"] == true, "/map sohan succeeded")
|
|
_assert(warp_signal["fired"] == true and warp_signal["map"] == "sohan", "Warped to sohan preset")
|
|
_assert(warp_signal["pos"] == Vector3(400, 0, 600), "Sohan coords match (400, 0, 600)")
|
|
|
|
# /warp 150 250
|
|
var r_warp = gm.execute_command("/warp 150 250", ctx)
|
|
_assert(r_warp["ok"] == true, "/warp 150 250 succeeded")
|
|
_assert(warp_signal["pos"] == Vector3(150, 0, 250), "Custom warp coords match")
|
|
|
|
# /spawn_mob 2493 1 (Beran-Setaou)
|
|
var mob_signal := {"fired": false, "vnum": 0, "count": 0}
|
|
gm.mob_spawned.connect(func(v: int, c: int, _p: Vector3):
|
|
mob_signal["fired"] = true
|
|
mob_signal["vnum"] = v
|
|
mob_signal["count"] = c
|
|
)
|
|
|
|
var r_mob = gm.execute_command("/spawn_mob 2493 1", ctx)
|
|
_assert(r_mob["ok"] == true, "/spawn_mob 2493 1 succeeded")
|
|
_assert(mob_signal["fired"] == true and mob_signal["vnum"] == 2493 and mob_signal["count"] == 1, "mob_spawned signal fired for Beran-Setaou")
|
|
|
|
func _test_help_and_unknown_command() -> void:
|
|
print("\n--- Test 5: /help and Error Handling ---")
|
|
var gm := GmToolkitSystem.new()
|
|
var ctx := {}
|
|
|
|
# /help
|
|
var r_help = gm.execute_command("/help", ctx)
|
|
_assert(r_help["ok"] == true and r_help["commands"].size() >= 5, "/help returned command list")
|
|
|
|
# Not a command
|
|
var r_not = gm.execute_command("hello", ctx)
|
|
_assert(r_not["ok"] == false and r_not["reason"] == "NOT_A_COMMAND", "Plain string rejected as non-command")
|
|
|
|
# Unknown command
|
|
var r_unk = gm.execute_command("/fly", ctx)
|
|
_assert(r_unk["ok"] == false and r_unk["reason"] == "UNKNOWN_COMMAND", "Unknown command rejected")
|
|
|
|
func _finish() -> void:
|
|
print("\n==========================================")
|
|
print("GM Toolkit Parity Test Results: %d Passed, %d Failed" % [_passed, _failed])
|
|
print("==========================================")
|
|
if _failed > 0:
|
|
quit(1)
|
|
else:
|
|
quit(0)
|