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 客户端对拍缺陷发现与修复工程指南
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# test_chat_hyperlink_dice_parity.gd —— 40250 聊天物品超链接与 /dice 掷骰系统回归测试
|
||||
extends SceneTree
|
||||
|
||||
const SystemClass = preload("res://chat_hyperlink_dice_system.gd")
|
||||
|
||||
var _passed: int = 0
|
||||
var _failed: int = 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_chat_hyperlink_dice_parity (Batch 27 Direction 3) ===")
|
||||
_test_item_hyperlink_encode_decode()
|
||||
_test_tooltip_generation()
|
||||
_test_broken_hyperlink_handling()
|
||||
_test_dice_command_variations()
|
||||
|
||||
print("\n=== Result: %d Passed, %d Failed ===" % [_passed, _failed])
|
||||
if _failed == 0:
|
||||
print("ALL TESTS PASSED!\n")
|
||||
else:
|
||||
printerr("SOME TESTS FAILED!\n")
|
||||
quit(0 if _failed == 0 else 1)
|
||||
|
||||
func _test_item_hyperlink_encode_decode() -> void:
|
||||
print("\n--- Test 1: Item Hyperlink Encode & Decode Round-trip ---")
|
||||
var original_item = {
|
||||
"vnum": 189,
|
||||
"name": "Poison Sword+9",
|
||||
"flags": 1,
|
||||
"sockets": [28430, 28431, 28432],
|
||||
"attributes": [
|
||||
{ "type": 72, "value": 45 }, # 平均伤害 45%
|
||||
{ "type": 71, "value": -12 }, # 技能伤害 -12%
|
||||
{ "type": 17, "value": 10 } # 半人加成 10%
|
||||
]
|
||||
}
|
||||
|
||||
# 1. Encode
|
||||
var link_str = SystemClass.encode_item_hyperlink(original_item)
|
||||
_assert(link_str.begins_with("|cffffc700|Hitem:"), "Link starts with official color and header")
|
||||
_assert(link_str.ends_with("[Poison Sword+9]|h|r"), "Link ends with brackets and reset tag")
|
||||
|
||||
# 2. Decode
|
||||
var decoded = SystemClass.decode_item_hyperlink(link_str)
|
||||
_assert(decoded["valid"] == true, "Decoded successfully")
|
||||
_assert(decoded["vnum"] == 189, "Vnum matches 189")
|
||||
_assert(decoded["name"] == "Poison Sword+9", "Name matches Poison Sword+9")
|
||||
_assert(decoded["sockets"][0] == 28430, "Socket 0 matches 28430")
|
||||
_assert(decoded["sockets"][1] == 28431, "Socket 1 matches 28431")
|
||||
_assert(decoded["sockets"][2] == 28432, "Socket 2 matches 28432")
|
||||
_assert(decoded["attributes"].size() == 3, "Decoded 3 attributes")
|
||||
_assert(decoded["attributes"][0]["type"] == 72 and decoded["attributes"][0]["value"] == 45, "Attr 0 is Type 72 Val 45")
|
||||
|
||||
func _test_tooltip_generation() -> void:
|
||||
print("\n--- Test 2: Tooltip Generation on Link Hover ---")
|
||||
var decoded = {
|
||||
"valid": true,
|
||||
"vnum": 11299,
|
||||
"name": "Black Steel Armor+9",
|
||||
"sockets": [28440, 28441, 0],
|
||||
"attributes": [
|
||||
{ "type": 1, "value": 2000 } # 最大生命值 +2000
|
||||
]
|
||||
}
|
||||
|
||||
var tooltip = SystemClass.build_tooltip_text(decoded)
|
||||
_assert(tooltip.contains("Black Steel Armor+9"), "Tooltip contains item name")
|
||||
_assert(tooltip.contains("Vnum: 11299"), "Tooltip contains item vnum")
|
||||
_assert(tooltip.contains("镶嵌宝石 [Vnum 28440]"), "Tooltip contains socket 1 gem")
|
||||
_assert(tooltip.contains("附加属性: 类型 1 +2000"), "Tooltip contains max HP bonus")
|
||||
|
||||
func _test_broken_hyperlink_handling() -> void:
|
||||
print("\n--- Test 3: Broken Hyperlink Text Safety ---")
|
||||
var r1 = SystemClass.decode_item_hyperlink("Just normal chat text")
|
||||
_assert(r1["valid"] == false, "Normal text produces invalid hyperlink result")
|
||||
|
||||
var r2 = SystemClass.decode_item_hyperlink("|cffffc700|Hitem:corrupted_data|h")
|
||||
_assert(r2["valid"] == false, "Corrupted hyperlink handled safely without crash")
|
||||
|
||||
func _test_dice_command_variations() -> void:
|
||||
print("\n--- Test 4: 40250 /dice Command Logic (1:1 do_dice) ---")
|
||||
# 1. Default /dice (1..100)
|
||||
var d1 = SystemClass.execute_dice("Player1", [], 77)
|
||||
_assert(d1["roll"] == 77, "Default dice rolled 77")
|
||||
_assert(d1["start"] == 1 and d1["end"] == 100, "Default range is 1-100")
|
||||
_assert(d1["message"].contains("77 (1-100)"), "Message correctly formatted")
|
||||
|
||||
# 2. Single arg /dice 20 (1..20)
|
||||
var d2 = SystemClass.execute_dice("Player2", ["20"], 15)
|
||||
_assert(d2["roll"] == 15, "Dice rolled 15 in range 1-20")
|
||||
_assert(d2["start"] == 1 and d2["end"] == 20, "Single arg range is 1-20")
|
||||
|
||||
# 3. Two args reversed: /dice 80 20 -> auto min/max to (20..80)
|
||||
var d3 = SystemClass.execute_dice("Player3", ["80", "20"], 50)
|
||||
_assert(d3["start"] == 20 and d3["end"] == 80, "Auto swaps reversed range to 20-80")
|
||||
_assert(d3["roll"] == 50, "Rolled 50 within normalized range")
|
||||
Reference in New Issue
Block a user