fix(combat): 战斗逐项对齐 40250,修复移动 / 技能释放与武器挂点

- 连击类型由 combo_changed 驱动(SetComboSkillFlag,技能 122 等级)
- 命中判定改为 .msa 刀尖 Z 圆柱 vs .msm 防御球(hit_collision.gd 逐行移植)
- _register_hit 改为 map::insert 语义,修正命中上限
- 攻速同时缩放 GetAttackingElapsedTime 与攻击动作速率;按武器动作模式目录绑定攻击段
- 去掉本地连击超时,motion_bound 清命中表 / 连击段号
- __ProcessDataAttackSuccess:InsertDelay 硬直、physics_push.gd 击退 + GetBlendingPosition 同步、
  命中特效、__HitGood / __HitGreate / __HitStone 受击动作链与抖动(ui/hit_reaction.gd)
- ClassicSession::send_use_skill 不再夹带 CG_FLY_TARGETING
- mac 前进 / 后退方向与技能释放修复;武器按职业骨骼挂到手上
- 新增 hit_collision / physics_push / hit_view / net_world_push / weapon_attach 测试;
  gpu_pose_bounds / race_motion_assembly 适配 damage 随机变体
- 文档:CLIENT-GAP.md、CLIENT-GAP-FIX.md §3.5 / §3.7 与 C.5 增量 130

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ft3kXpNdLbKEfg5uDLfqVF
This commit is contained in:
shenlei
2026-09-12 13:15:28 +09:00
co-authored by Claude Opus 5
parent 96c29d2bb7
commit 1933a5ceda
30 changed files with 2353 additions and 209 deletions
+61 -2
View File
@@ -162,6 +162,60 @@ func load_file(path: String) -> bool:
count = _by_id.size()
return count > 0
# RegisterSkillTablePythonSkill.cpp:90):SkillTable.txt 按 TAB 切分,列数不等于
# TABLE_TOKEN_TYPE_MAX_NUM 的行、skilldesc 里没有的 vnum 都跳过。参考端 RegisterSkill(.msk)
# 没有调用点,TSkillData 构造时 dwTargetRange = 0,只由这里非空的 TARGET_RANGE 列覆盖;
# 所以表生效后不再沿用 .msk 的 Range。第 2 列是 CP949 技能名,按字节切分避免解码告警。
const TABLE_TOKEN_TYPE_VNUM := 0
const TABLE_TOKEN_TYPE_TARGET_RANGE := 25
const TABLE_TOKEN_TYPE_MAX_NUM := 27
func load_table(path: String) -> bool:
if _by_id.is_empty() or not FileAccess.file_exists(path):
return false
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
return false
var bytes := file.get_buffer(file.get_length())
var ranges := {}
var start := 0
while start < bytes.size():
var stop := bytes.find(10, start)
if stop < 0:
stop = bytes.size()
var fields := _tab_fields(bytes.slice(start, stop))
start = stop + 1
if fields.size() != TABLE_TOKEN_TYPE_MAX_NUM:
continue
var vnum := int(_ascii_token(fields[TABLE_TOKEN_TYPE_VNUM]))
if not _by_id.has(vnum):
continue
ranges[vnum] = _ascii_token(fields[TABLE_TOKEN_TYPE_TARGET_RANGE])
if ranges.is_empty():
return false
for id in _by_id:
_by_id[id]["target_range"] = 0
for vnum in ranges:
if String(ranges[vnum]) != "":
_by_id[vnum]["target_range"] = int(ranges[vnum])
return true
static func _tab_fields(line: PackedByteArray) -> Array:
var fields := []
var begin := 0
for i in line.size() + 1:
if i == line.size() or line[i] == 9:
fields.append(line.slice(begin, i))
begin = i + 1
return fields
static func _ascii_token(field: PackedByteArray) -> String:
var out := PackedByteArray()
for b in field:
if b >= 32 and b <= 126:
out.append(b)
return out.get_string_from_ascii().strip_edges()
func has(id: int) -> bool:
return _by_id.has(id)
@@ -270,9 +324,14 @@ func target_count(id: int, level: int) -> int:
return int(floor(float(value)))
# __GetSkillTargetRangePythonPlayerSkill.cpp:391= rkSkillData.GetTargetRange()
# + GetStatus(POINT_BOW_DISTANCE)*100。这里只给 .msk 的基础射程(cm);主角弓距加成
# 由 net_play(有玩家状态)叠加。
# + GetStatus(POINT_BOW_DISTANCE)*100。这里是 GetTargetRangePythonSkill.cpp:1086):
# MELEE_ATTACK / CHARGE_ATTACK 固定 MELEE_SKILL_TARGET_RANGE,否则 dwTargetRange
# load_table 之后取 SkillTable.txt TARGET_RANGE);主角弓距加成由 player_skill 叠加。
const MELEE_SKILL_TARGET_RANGE := 170
func target_range(id: int) -> int:
if is_melee(id) or is_charge_skill(id):
return MELEE_SKILL_TARGET_RANGE
return int(_by_id.get(id, {}).get("target_range", 0))
# skilldesc / .msk 的 FAN_RANGE / CIRCLE_RANGE → NetPlay.FlyShapeSINGLE/FAN/CIRCLE)。