Files
mtgodot-poc/project/test_gm_toolkit_parity.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

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)