- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
103 lines
5.2 KiB
GDScript
103 lines
5.2 KiB
GDScript
# test_alignment_parity.gd —— 验证方向 2:人物善恶道德与红名系统 40250 1:1 对齐
|
|
extends SceneTree
|
|
|
|
const AlignmentSystem = preload("res://alignment_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_alignment_parity (Direction 2: Karma & Alignment 1:1) ===")
|
|
test_alignment_grades_and_thresholds()
|
|
test_title_colors_and_names()
|
|
test_natural_time_recovery()
|
|
test_kill_monster_karma()
|
|
test_zen_bean_karma_clearing()
|
|
|
|
print("\n=== SUMMARY: %d passed, %d failed ===" % [_passed, _failed])
|
|
if _failed > 0:
|
|
printerr("TEST FAILED!")
|
|
quit(1)
|
|
else:
|
|
print("ALL TESTS PASSED!")
|
|
quit(0)
|
|
|
|
func test_alignment_grades_and_thresholds() -> void:
|
|
print("\n--- Test 1: 9 Alignment Grades & Numerical Thresholds ---")
|
|
_assert(AlignmentSystem.get_alignment_grade(15000) == AlignmentSystem.TITLE_HEROIC, "15000 is Heroic (>= 12000)")
|
|
_assert(AlignmentSystem.get_alignment_grade(12000) == AlignmentSystem.TITLE_HEROIC, "12000 is Heroic (boundary)")
|
|
_assert(AlignmentSystem.get_alignment_grade(9500) == AlignmentSystem.TITLE_NOBLE, "9500 is Noble (8000..11999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(5000) == AlignmentSystem.TITLE_GOOD, "5000 is Good (4000..7999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(2000) == AlignmentSystem.TITLE_FRIENDLY, "2000 is Friendly (1000..3999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(500) == AlignmentSystem.TITLE_NEUTRAL, "500 is Neutral (0..999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(0) == AlignmentSystem.TITLE_NEUTRAL, "0 is Neutral (boundary)")
|
|
_assert(AlignmentSystem.get_alignment_grade(-2000) == AlignmentSystem.TITLE_AGGRESSIVE, "-2000 is Aggressive (-1..-3999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(-5000) == AlignmentSystem.TITLE_FRAUDULENT, "-5000 is Fraudulent (-4000..-7999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(-10000) == AlignmentSystem.TITLE_MALICIOUS, "-10000 is Malicious (-8000..-11999)")
|
|
_assert(AlignmentSystem.get_alignment_grade(-15000) == AlignmentSystem.TITLE_CRUEL, "-15000 is Cruel (<= -12000)")
|
|
|
|
func test_title_colors_and_names() -> void:
|
|
print("\n--- Test 2: Title Names and 40250 RGB Colors ---")
|
|
_assert(AlignmentSystem.get_title_name(AlignmentSystem.TITLE_HEROIC) == "英雄", "Grade 0 is 英雄")
|
|
_assert(AlignmentSystem.get_title_name(AlignmentSystem.TITLE_CRUEL) == "魔头", "Grade 8 is 魔头")
|
|
|
|
var c_heroic := AlignmentSystem.get_title_color(AlignmentSystem.TITLE_HEROIC)
|
|
_assert(c_heroic == Color8(0, 204, 255), "Heroic color matches 40250 Color8(0, 204, 255) cyan")
|
|
|
|
var c_cruel := AlignmentSystem.get_title_color(AlignmentSystem.TITLE_CRUEL)
|
|
_assert(c_cruel == Color8(255, 0, 0), "Cruel color matches 40250 Color8(255, 0, 0) red")
|
|
|
|
_assert(AlignmentSystem.is_red_name(-100), "-100 is red name (evil)")
|
|
_assert(not AlignmentSystem.is_red_name(0), "0 is not red name")
|
|
_assert(AlignmentSystem.is_blue_name(1500), "1500 is blue name (just)")
|
|
|
|
func test_natural_time_recovery() -> void:
|
|
print("\n--- Test 3: 40250 Online Natural Time Recovery (char.cpp:1197) ---")
|
|
# 1. Evil player (-1000) after 10 minutes -> +60 * 10 = +600 -> -400
|
|
var evil_rec := AlignmentSystem.calculate_time_recovery(-1000, 10, false)
|
|
_assert(evil_rec == -400, "Evil karma recovered from -1000 to -400 (+60/min)")
|
|
|
|
# 2. Evil player with fast item (120/min) after 5 minutes -> +120 * 5 = +600 -> -400
|
|
var fast_rec := AlignmentSystem.calculate_time_recovery(-1000, 5, true)
|
|
_assert(fast_rec == -400, "Evil karma with fast item recovered +120/min")
|
|
|
|
# 3. Evil natural recovery clamps at 0 (does not automatically become positive just by AFK)
|
|
var clamp_rec := AlignmentSystem.calculate_time_recovery(-100, 10, false)
|
|
_assert(clamp_rec == 0, "Evil time recovery clamps at 0")
|
|
|
|
# 4. Good player (1000) after 10 minutes -> +5 * 10 = +50 -> 1050
|
|
var good_rec := AlignmentSystem.calculate_time_recovery(1000, 10, false)
|
|
_assert(good_rec == 1050, "Good karma increases +5/min")
|
|
|
|
func test_kill_monster_karma() -> void:
|
|
print("\n--- Test 4: Monster Kill Karma Growth ---")
|
|
var normal_kill := AlignmentSystem.on_kill_monster(500, 30, 20) # 30 - 10 = 20
|
|
_assert(normal_kill == 520, "Monster kill added 20 alignment")
|
|
|
|
# Evil players get double recovery rate when killing monsters
|
|
var evil_kill := AlignmentSystem.on_kill_monster(-1000, 30, 20) # 20 * 2 = 40
|
|
_assert(evil_kill == -960, "Evil player gets 2x karma on monster kill")
|
|
|
|
func test_zen_bean_karma_clearing() -> void:
|
|
print("\n--- Test 5: Zen Bean (70102) Karma Clearing ---")
|
|
# Zen bean on evil player (-800) clears 500 karma -> -300
|
|
var res_bean := AlignmentSystem.use_zen_bean(-800)
|
|
_assert(res_bean.ok and res_bean.new_alignment == -300, "Zen bean reduced evil karma from -800 to -300")
|
|
|
|
# Zen bean on slightly evil player (-200) clamps at 0
|
|
var res_bean_clamp := AlignmentSystem.use_zen_bean(-200)
|
|
_assert(res_bean_clamp.ok and res_bean_clamp.new_alignment == 0, "Zen bean clamps at 0")
|
|
|
|
# Zen bean on neutral or good player is rejected
|
|
var res_bean_good := AlignmentSystem.use_zen_bean(500)
|
|
_assert(not res_bean_good.ok and res_bean_good.code == "CANNOT_USE_NOT_EVIL",
|
|
"Zen bean rejected when player is not evil")
|