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 @@
|
||||
# intro_create_phase.gd —— 40250 3D 角色塑形创建系统 1:1
|
||||
# 严格对照:
|
||||
# Client/Eternexus/root/introcreate.py (CreateCharacterWindow)
|
||||
# ClientVS22/source/UserInterface/InstanceBaseTransform.cpp
|
||||
# metin2/src/server/game/src/player.cpp
|
||||
extends RefCounted
|
||||
|
||||
const JOB_WARRIOR := 0
|
||||
const JOB_ASSASSIN := 1
|
||||
const JOB_SURA := 2
|
||||
const JOB_SHAMAN := 3
|
||||
|
||||
const GENDER_MALE := 0 # MAN
|
||||
const GENDER_FEMALE := 1 # WOMAN
|
||||
|
||||
const SHAPE_0 := 0 # 经典外观 1 (SHAPE0)
|
||||
const SHAPE_1 := 1 # 经典外观 2 (SHAPE1)
|
||||
|
||||
# 40250 官方职业初始四维属性分配表 (CON, INT, STR, DEX)
|
||||
# 对应 introcreate.py: START_STAT
|
||||
const START_STAT_TABLE := {
|
||||
JOB_WARRIOR: { "con": 4, "int": 3, "str": 6, "dex": 3 }, # 战士
|
||||
JOB_ASSASSIN: { "con": 3, "int": 3, "str": 4, "dex": 6 }, # 刺客
|
||||
JOB_SURA: { "con": 3, "int": 5, "str": 5, "dex": 3 }, # 修罗
|
||||
JOB_SHAMAN: { "con": 4, "int": 6, "str": 3, "dex": 3 }, # 萨满
|
||||
}
|
||||
|
||||
# 违禁词简易过滤器
|
||||
const BANNED_NAME_PATTERNS := ["[GM]", "admin", "null", "fuck", "shit"]
|
||||
|
||||
# 当前创建器状态
|
||||
var selected_job: int = JOB_WARRIOR
|
||||
var selected_gender: int = GENDER_MALE
|
||||
var selected_shape: int = SHAPE_0
|
||||
var turntable_rotation_deg: float = 0.0 # 360° 转盘旋转角度
|
||||
|
||||
func _init(p_job: int = JOB_WARRIOR, p_gender: int = GENDER_MALE) -> void:
|
||||
selected_job = p_job
|
||||
selected_gender = p_gender
|
||||
selected_shape = SHAPE_0
|
||||
turntable_rotation_deg = 0.0
|
||||
|
||||
# 1. 切换职业与属性联动
|
||||
func select_job(job_id: int) -> bool:
|
||||
if not START_STAT_TABLE.has(job_id):
|
||||
return false
|
||||
selected_job = job_id
|
||||
return true
|
||||
|
||||
# 2. 切换性别
|
||||
func select_gender(gender_id: int) -> bool:
|
||||
if gender_id != GENDER_MALE and gender_id != GENDER_FEMALE:
|
||||
return false
|
||||
selected_gender = gender_id
|
||||
return true
|
||||
|
||||
# 3. 切换外观 (Shape 0 / Shape 1)
|
||||
func select_shape(shape_id: int) -> bool:
|
||||
if shape_id != SHAPE_0 and shape_id != SHAPE_1:
|
||||
return false
|
||||
selected_shape = shape_id
|
||||
return true
|
||||
|
||||
# 4. 旋转展示台 (360 度循环)
|
||||
func rotate_turntable(delta_deg: float) -> float:
|
||||
turntable_rotation_deg = fposmod(turntable_rotation_deg + delta_deg, 360.0)
|
||||
return turntable_rotation_deg
|
||||
|
||||
# 5. 获取当前职业的基础四维属性
|
||||
func get_current_stats() -> Dictionary:
|
||||
return START_STAT_TABLE.get(selected_job, { "con": 4, "int": 3, "str": 6, "dex": 3 })
|
||||
|
||||
# 6. 角色名称合规性校验
|
||||
func validate_character_name(name_str: String) -> Dictionary:
|
||||
var res := { "valid": false, "error_code": "OK", "message": "" }
|
||||
|
||||
if name_str.is_empty():
|
||||
res["error_code"] = "EMPTY_NAME"
|
||||
res["message"] = "角色名称不能为空。"
|
||||
return res
|
||||
|
||||
var length := name_str.length()
|
||||
if length < 2 or length > 12:
|
||||
res["error_code"] = "INVALID_NAME_LENGTH"
|
||||
res["message"] = "角色名称长度需在 2 到 12 字符之间。"
|
||||
return res
|
||||
|
||||
if name_str.contains(" ") or name_str.contains("\t"):
|
||||
res["error_code"] = "NAME_CONTAINS_SPACE"
|
||||
res["message"] = "角色名称中不能包含空格。"
|
||||
return res
|
||||
|
||||
var lower_name := name_str.to_lower()
|
||||
for banned in BANNED_NAME_PATTERNS:
|
||||
if lower_name.contains(banned.to_lower()):
|
||||
res["error_code"] = "NAME_CONTAINS_BANNED_WORD"
|
||||
res["message"] = "角色名称中包含非法或保留字符。"
|
||||
return res
|
||||
|
||||
res["valid"] = true
|
||||
return res
|
||||
|
||||
# 7. 确认创建角色并打包角色初始数据
|
||||
func create_character(name_str: String, empire_id: int = 1) -> Dictionary:
|
||||
var val = validate_character_name(name_str)
|
||||
if not val["valid"]:
|
||||
return { "success": false, "error_code": val["error_code"], "message": val["message"] }
|
||||
|
||||
var stats: Dictionary = get_current_stats()
|
||||
var char_data := {
|
||||
"name": name_str,
|
||||
"empire": empire_id,
|
||||
"level": 1,
|
||||
"exp": 0,
|
||||
"job": selected_job,
|
||||
"gender": selected_gender,
|
||||
"shape": selected_shape,
|
||||
"cur_hp": 1000,
|
||||
"max_hp": 1000,
|
||||
"cur_sp": 500,
|
||||
"max_sp": 500,
|
||||
"stats": stats,
|
||||
"horse_level": 0,
|
||||
"playtime_min": 0,
|
||||
"delete_code": "1234567" # 默认 7 位删除安全码
|
||||
}
|
||||
|
||||
return {
|
||||
"success": true,
|
||||
"character": char_data,
|
||||
"message": "角色 %s 创建成功!" % name_str
|
||||
}
|
||||
Reference in New Issue
Block a user