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,132 @@
|
||||
# test_intro_create_parity.gd —— 40250 3D 角色塑形创建回归测试
|
||||
extends SceneTree
|
||||
|
||||
const SystemClass = preload("res://intro_create_phase.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_intro_create_parity (Batch 29 Direction 4) ===")
|
||||
_test_class_gender_shape_matrix()
|
||||
_test_canonical_start_stats()
|
||||
_test_turntable_360_rotation()
|
||||
_test_character_name_validation()
|
||||
_test_successful_character_creation()
|
||||
|
||||
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_class_gender_shape_matrix() -> void:
|
||||
print("\n--- Test 1: Class, Gender, and Shape Matrix ---")
|
||||
var cr = SystemClass.new()
|
||||
|
||||
_assert(cr.selected_job == SystemClass.JOB_WARRIOR, "Default job is Warrior")
|
||||
_assert(cr.selected_gender == SystemClass.GENDER_MALE, "Default gender is Male")
|
||||
_assert(cr.selected_shape == SystemClass.SHAPE_0, "Default shape is Shape 0")
|
||||
|
||||
# Switch to Assassin, Female, Shape 1
|
||||
var s_job = cr.select_job(SystemClass.JOB_ASSASSIN)
|
||||
var s_gen = cr.select_gender(SystemClass.GENDER_FEMALE)
|
||||
var s_shp = cr.select_shape(SystemClass.SHAPE_1)
|
||||
|
||||
_assert(s_job == true and cr.selected_job == SystemClass.JOB_ASSASSIN, "Switched to Assassin")
|
||||
_assert(s_gen == true and cr.selected_gender == SystemClass.GENDER_FEMALE, "Switched to Female")
|
||||
_assert(s_shp == true and cr.selected_shape == SystemClass.SHAPE_1, "Switched to Shape 1")
|
||||
|
||||
# Reject invalid selections
|
||||
_assert(cr.select_job(99) == false, "Reject invalid job id 99")
|
||||
_assert(cr.select_gender(5) == false, "Reject invalid gender id 5")
|
||||
_assert(cr.select_shape(3) == false, "Reject invalid shape id 3")
|
||||
|
||||
func _test_canonical_start_stats() -> void:
|
||||
print("\n--- Test 2: 40250 Canonical START_STAT Table (CON, INT, STR, DEX) ---")
|
||||
var cr = SystemClass.new()
|
||||
|
||||
# 1. Warrior: [4, 3, 6, 3]
|
||||
cr.select_job(SystemClass.JOB_WARRIOR)
|
||||
var st_w = cr.get_current_stats()
|
||||
_assert(st_w["con"] == 4 and st_w["int"] == 3 and st_w["str"] == 6 and st_w["dex"] == 3, "Warrior stats: 4/3/6/3")
|
||||
|
||||
# 2. Assassin: [3, 3, 4, 6]
|
||||
cr.select_job(SystemClass.JOB_ASSASSIN)
|
||||
var st_a = cr.get_current_stats()
|
||||
_assert(st_a["con"] == 3 and st_a["int"] == 3 and st_a["str"] == 4 and st_a["dex"] == 6, "Assassin stats: 3/3/4/6")
|
||||
|
||||
# 3. Sura: [3, 5, 5, 3]
|
||||
cr.select_job(SystemClass.JOB_SURA)
|
||||
var st_s = cr.get_current_stats()
|
||||
_assert(st_s["con"] == 3 and st_s["int"] == 5 and st_s["str"] == 5 and st_s["dex"] == 3, "Sura stats: 3/5/5/3")
|
||||
|
||||
# 4. Shaman: [4, 6, 3, 3]
|
||||
cr.select_job(SystemClass.JOB_SHAMAN)
|
||||
var st_m = cr.get_current_stats()
|
||||
_assert(st_m["con"] == 4 and st_m["int"] == 6 and st_m["str"] == 3 and st_m["dex"] == 3, "Shaman stats: 4/6/3/3")
|
||||
|
||||
func _test_turntable_360_rotation() -> void:
|
||||
print("\n--- Test 3: Turntable 360-Degree Rotation ---")
|
||||
var cr = SystemClass.new()
|
||||
|
||||
cr.rotate_turntable(45.0)
|
||||
_assert(cr.turntable_rotation_deg == 45.0, "Rotated by 45 degrees")
|
||||
|
||||
cr.rotate_turntable(340.0) # 45 + 340 = 385 -> wraps to 25
|
||||
_assert(is_equal_approx(cr.turntable_rotation_deg, 25.0), "Rotated beyond 360 wrapped correctly to 25 degrees")
|
||||
|
||||
cr.rotate_turntable(-30.0) # 25 - 30 = -5 -> wraps to 355
|
||||
_assert(is_equal_approx(cr.turntable_rotation_deg, 355.0), "Negative rotation wrapped correctly to 355 degrees")
|
||||
|
||||
func _test_character_name_validation() -> void:
|
||||
print("\n--- Test 4: Character Name Validation Rules ---")
|
||||
var cr = SystemClass.new()
|
||||
|
||||
# Empty
|
||||
var r_empty = cr.validate_character_name("")
|
||||
_assert(r_empty["error_code"] == "EMPTY_NAME", "Reject empty name")
|
||||
|
||||
# Too short (< 2)
|
||||
var r_short = cr.validate_character_name("A")
|
||||
_assert(r_short["error_code"] == "INVALID_NAME_LENGTH", "Reject name length < 2")
|
||||
|
||||
# Too long (> 12)
|
||||
var r_long = cr.validate_character_name("VeryLongHeroNameExceeding12")
|
||||
_assert(r_long["error_code"] == "INVALID_NAME_LENGTH", "Reject name length > 12")
|
||||
|
||||
# Contains space
|
||||
var r_space = cr.validate_character_name("Hello World")
|
||||
_assert(r_space["error_code"] == "NAME_CONTAINS_SPACE", "Reject name containing spaces")
|
||||
|
||||
# Banned word (GM / Admin)
|
||||
var r_gm = cr.validate_character_name("[GM]Tester")
|
||||
_assert(r_gm["error_code"] == "NAME_CONTAINS_BANNED_WORD", "Reject name containing [GM]")
|
||||
|
||||
var r_ok = cr.validate_character_name("DarkKnight")
|
||||
_assert(r_ok["valid"] == true, "Valid name DarkKnight passed")
|
||||
|
||||
func _test_successful_character_creation() -> void:
|
||||
print("\n--- Test 5: Successful Character Creation Payload ---")
|
||||
var cr = SystemClass.new()
|
||||
cr.select_job(SystemClass.JOB_WARRIOR)
|
||||
cr.select_gender(SystemClass.GENDER_MALE)
|
||||
cr.select_shape(SystemClass.SHAPE_0)
|
||||
|
||||
var res = cr.create_character("Conan", 1)
|
||||
_assert(res["success"] == true, "Created character Conan successfully")
|
||||
var ch = res["character"]
|
||||
_assert(ch["name"] == "Conan", "Name matches Conan")
|
||||
_assert(ch["level"] == 1, "Level is 1")
|
||||
_assert(ch["empire"] == 1, "Empire is 1 (Shinsoo)")
|
||||
_assert(ch["stats"]["str"] == 6, "Warrior STR is 6")
|
||||
_assert(ch["delete_code"] == "1234567", "Generated 7-digit delete code")
|
||||
Reference in New Issue
Block a user