- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
125 lines
4.9 KiB
GDScript
125 lines
4.9 KiB
GDScript
# test_party_management_parity.gd —— 40250 8人组队面板、经验分配与领导力光环系统回归测试
|
|
extends SceneTree
|
|
|
|
const SystemClass = preload("res://party_management_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_party_management_parity (Batch 27 Direction 4) ===")
|
|
_test_party_capacity_and_join()
|
|
_test_member_expel_and_leave()
|
|
_test_leadership_tactical_auras()
|
|
_test_exp_distribution_modes()
|
|
_test_hp_percentage_synchronization()
|
|
|
|
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_party_capacity_and_join() -> void:
|
|
print("\n--- Test 1: Party Creation & 8-Member Limit ---")
|
|
var party = SystemClass.new(1, "LeaderOne", 50)
|
|
_assert(party.members.size() == 1, "Party created with 1 member (Leader)")
|
|
_assert(party.leader_pid == 1, "Leader PID is 1")
|
|
|
|
# Add 7 more members (total 8)
|
|
for i in range(2, 9):
|
|
var r = party.add_member(i, "Member%d" % i, 40 + i)
|
|
_assert(r["success"] == true, "Member %d joined party" % i)
|
|
|
|
_assert(party.members.size() == 8, "Party reached 8 members")
|
|
|
|
# 9th member must be rejected
|
|
var r_overflow = party.add_member(9, "OverflowMember", 50)
|
|
_assert(r_overflow["error_code"] == "PARTY_FULL", "Reject 9th member (max 8)")
|
|
|
|
func _test_member_expel_and_leave() -> void:
|
|
print("\n--- Test 2: Expel & Leave Mechanics ---")
|
|
var party = SystemClass.new(1, "Leader", 60)
|
|
party.add_member(2, "Alice", 55)
|
|
party.add_member(3, "Bob", 50)
|
|
|
|
# Non-leader cannot expel
|
|
var r_fail = party.expel_member(2, 3)
|
|
_assert(r_fail["error_code"] == "NOT_LEADER", "Non-leader cannot expel member")
|
|
|
|
# Leader expels Alice
|
|
var r_expel = party.expel_member(1, 2)
|
|
_assert(r_expel["success"] == true, "Leader successfully expelled Alice")
|
|
_assert(party.members.has(2) == false, "Alice no longer in party")
|
|
|
|
# Leader leaves party -> Bob promoted to new leader
|
|
var r_lead_leave = party.leave_party(1)
|
|
_assert(r_lead_leave["success"] == true, "Leader left party")
|
|
_assert(party.leader_pid == 3, "Bob promoted to new leader")
|
|
_assert(party.members[3].is_leader == true, "Bob marked as leader")
|
|
|
|
# Bob leaves -> Party dissolves
|
|
var r_dissolve = party.leave_party(3)
|
|
_assert(r_dissolve["dissolved"] == true, "Party dissolved when last member left")
|
|
|
|
func _test_leadership_tactical_auras() -> void:
|
|
print("\n--- Test 3: Leader Tactical Auras (Roles & Buffs) ---")
|
|
var party = SystemClass.new(1, "Commander", 75)
|
|
party.add_member(2, "Warrior", 70)
|
|
party.add_member(3, "Sura", 72)
|
|
|
|
# Non-leader cannot assign role
|
|
var r_non_lead = party.set_member_role(2, 2, SystemClass.ROLE_ATTACKER)
|
|
_assert(r_non_lead["error_code"] == "NOT_LEADER", "Non-leader cannot assign role")
|
|
|
|
# Leader assigns Warrior as ATTACKER
|
|
var r1 = party.set_member_role(1, 2, SystemClass.ROLE_ATTACKER)
|
|
_assert(r1["success"] == true, "Assigned Warrior as ATTACKER")
|
|
var buff1 = party.get_member_buff(2)
|
|
_assert(buff1["stat"] == "att_grade" and buff1["val"] == 30, "Warrior receives +30 att_grade buff")
|
|
|
|
# Leader assigns Sura as TANKER
|
|
var r2 = party.set_member_role(1, 3, SystemClass.ROLE_TANKER)
|
|
_assert(r2["success"] == true, "Assigned Sura as TANKER")
|
|
var buff2 = party.get_member_buff(3)
|
|
_assert(buff2["stat"] == "max_hp" and buff2["val"] == 2500, "Sura receives +2500 max_hp buff")
|
|
|
|
# Reject duplicate ATTACKER assignment to Sura
|
|
var r_dup = party.set_member_role(1, 3, SystemClass.ROLE_ATTACKER)
|
|
_assert(r_dup["error_code"] == "ROLE_ALREADY_ASSIGNED", "Reject duplicate role assignment")
|
|
|
|
func _test_exp_distribution_modes() -> void:
|
|
print("\n--- Test 4: Experience Distribution Modes ---")
|
|
var party = SystemClass.new(1, "PlayerA", 20) # Level 20
|
|
party.add_member(2, "PlayerB", 80) # Level 80
|
|
|
|
# 1. PARITY (Equal mode)
|
|
party.set_exp_distribution_mode(1, SystemClass.EXP_MODE_PARITY)
|
|
var exp_parity = party.distribute_exp(1000)
|
|
_assert(exp_parity[1] == 500, "PlayerA gets 500 in PARITY mode")
|
|
_assert(exp_parity[2] == 500, "PlayerB gets 500 in PARITY mode")
|
|
|
|
# 2. LEVEL (Weighted mode: 20% vs 80%)
|
|
party.set_exp_distribution_mode(1, SystemClass.EXP_MODE_LEVEL)
|
|
var exp_level = party.distribute_exp(1000)
|
|
_assert(exp_level[1] == 200, "PlayerA (Lv20) gets 200 (20%) in LEVEL mode")
|
|
_assert(exp_level[2] == 800, "PlayerB (Lv80) gets 800 (80%) in LEVEL mode")
|
|
|
|
func _test_hp_percentage_synchronization() -> void:
|
|
print("\n--- Test 5: HP Gauge Percentage Synchronization ---")
|
|
var party = SystemClass.new(1, "PlayerA", 50)
|
|
party.sync_member_hp(1, 3000, 6000)
|
|
_assert(is_equal_approx(party.members[1].get_hp_percent(), 0.5), "HP percentage is exactly 50%")
|
|
|
|
party.sync_member_hp(1, 6000, 6000)
|
|
_assert(is_equal_approx(party.members[1].get_hp_percent(), 1.0), "HP percentage is 100%")
|