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:
+60
-11
@@ -15,7 +15,8 @@ class FakeClient extends Node:
|
||||
for i in 11: equip.append({"vnum": 0, "count": 0, "wear": i})
|
||||
func get_equipment() -> Array: return equip
|
||||
func get_main_vid() -> int: return 1000
|
||||
func get_entity(_v) -> Dictionary: return {"race": 0, "parts": main_parts}
|
||||
var mount_vnum := 0
|
||||
func get_entity(_v) -> Dictionary: return {"race": 0, "parts": main_parts, "mount_vnum": mount_vnum}
|
||||
func set_wear(idx, vnum):
|
||||
equip[idx] = {"vnum": vnum, "count": 1, "wear": idx}
|
||||
inventory_changed.emit(2, 90 + idx) # window EQUIPMENT
|
||||
@@ -36,6 +37,14 @@ class StubModel extends Node3D:
|
||||
"hair_skin": hair_skin = val; return true
|
||||
return false
|
||||
|
||||
# item_proto 桩:只给武器 subtype(WEAPON_DAGGER=1 BOW=2 FAN=5,item_length.h EWeaponSubTypes)
|
||||
class FakeProto extends Node:
|
||||
const SUB := {19: 0, 2000: 2, 4000: 1, 7000: 5}
|
||||
func item(vnum: int) -> Dictionary:
|
||||
if SUB.has(vnum):
|
||||
return {"type": 1, "sub_type": SUB[vnum]}
|
||||
return {}
|
||||
|
||||
var _fail := 0
|
||||
func _ck(c: bool, m: String) -> void:
|
||||
if not c:
|
||||
@@ -43,7 +52,7 @@ func _ck(c: bool, m: String) -> void:
|
||||
printerr("FAIL: " + m)
|
||||
|
||||
func _init() -> void:
|
||||
_run()
|
||||
await _run()
|
||||
if _fail == 0:
|
||||
print("PASS: equip_model_test (item_list + weapon swap)")
|
||||
quit(0)
|
||||
@@ -97,15 +106,50 @@ func _run() -> void:
|
||||
await process_frame
|
||||
_ck(model.gr2_path == "d:/ymir work/pc/warrior/warrior.gr2", "armor_model_map -> gr2_path set")
|
||||
|
||||
# 盾(WEAR_SHIELD=10)-> shield_gr2 = item_list.model 解析
|
||||
# 用一个真存在的武器 vnum 当盾(资产里 00010.gr2 存在)
|
||||
fc.set_wear(10, 19)
|
||||
# 左右手:ActorInstanceAttach.cpp AttachWeapon
|
||||
# __IsRightHandWeapon: DAGGER / (FAN && 骑马) -> 右;BOW -> 否;其余 -> 右
|
||||
# __IsLeftHandWeapon : DAGGER / (FAN && 骑马) / BOW -> 左(GetSubModelThing = 同一 gr2)
|
||||
# shield_gr2 槽 = PART_WEAPON_LEFT;参考端不渲染 WEAR_SHIELD(盾在 item_list 没模型)。
|
||||
var fch := FakeClient.new()
|
||||
var mh := StubModel.new()
|
||||
var fp := FakeProto.new()
|
||||
get_root().add_child(fch)
|
||||
get_root().add_child(mh)
|
||||
get_root().add_child(fp)
|
||||
var emh: Node = EquipModel.new()
|
||||
get_root().add_child(emh)
|
||||
emh.setup(fch, il, func() -> Node: return mh, assets)
|
||||
emh.main_getter = func() -> int: return 1000
|
||||
emh.proto = fp
|
||||
var sword_p: String = emh._resolve_weapon(19)
|
||||
var bow_p: String = emh._resolve_weapon(2000)
|
||||
var dagger_p: String = emh._resolve_weapon(4000)
|
||||
var fan_p: String = emh._resolve_weapon(7000)
|
||||
_ck(sword_p != "" and bow_p != "" and dagger_p != "" and fan_p != "", "weapon gr2 resolvable (19/2000/4000/7000)")
|
||||
fch.set_wear(4, 19)
|
||||
await process_frame
|
||||
print(" shield_gr2 -> '", model.shield_gr2, "'")
|
||||
_ck(model.shield_gr2 == "" or model.shield_gr2.ends_with("00010.gr2"), "WEAR_SHIELD -> shield_gr2")
|
||||
fc.set_wear(10, 0)
|
||||
_ck(mh.weapon_gr2 == sword_p and mh.shield_gr2 == "", "sword -> right hand only (%s | %s)" % [mh.weapon_gr2, mh.shield_gr2])
|
||||
fch.set_wear(4, 4000)
|
||||
await process_frame
|
||||
_ck(model.shield_gr2 == "", "unequip shield -> shield_gr2 cleared")
|
||||
_ck(mh.weapon_gr2 == dagger_p and mh.shield_gr2 == dagger_p, "dagger -> both hands (%s | %s)" % [mh.weapon_gr2, mh.shield_gr2])
|
||||
fch.set_wear(4, 2000)
|
||||
await process_frame
|
||||
_ck(mh.weapon_gr2 == "" and mh.shield_gr2 == bow_p, "bow -> left hand only (%s | %s)" % [mh.weapon_gr2, mh.shield_gr2])
|
||||
fch.set_wear(4, 7000)
|
||||
await process_frame
|
||||
_ck(mh.weapon_gr2 == fan_p and mh.shield_gr2 == "", "fan on foot -> right hand only (%s | %s)" % [mh.weapon_gr2, mh.shield_gr2])
|
||||
fch.mount_vnum = 20030
|
||||
fch.entity_info.emit(1000, {})
|
||||
await process_frame
|
||||
_ck(mh.weapon_gr2 == fan_p and mh.shield_gr2 == fan_p, "fan mounted -> both hands (%s | %s)" % [mh.weapon_gr2, mh.shield_gr2])
|
||||
fch.mount_vnum = 0
|
||||
fch.set_wear(4, 19)
|
||||
fch.set_wear(10, 19) # WEAR_SHIELD 不渲染
|
||||
await process_frame
|
||||
_ck(mh.shield_gr2 == "", "WEAR_SHIELD is not rendered (%s)" % mh.shield_gr2)
|
||||
fch.set_wear(4, 0)
|
||||
await process_frame
|
||||
_ck(mh.weapon_gr2 == "" and mh.shield_gr2 == "", "unequip -> both hands cleared")
|
||||
|
||||
# 头盔(WEAR_HEAD=1)有模型 -> 覆盖 hair_gr2;拆下回角色发型(parts[3]=5)
|
||||
var em2: Node = EquipModel.new()
|
||||
@@ -139,6 +183,11 @@ func _run() -> void:
|
||||
# 11000 "Wolf Armour" -> values[3] == 0 -> 回退 =vnum
|
||||
_ck(em3._armor_shape_default(11000) == 11000, "armor 11000 -> values[3]=0 回退 =vnum")
|
||||
_ck(em3._armor_specular(11209) == 100, "armor 11209(+9) -> specular 100")
|
||||
# FakeProto 的 subtype 与真 item_proto 一致
|
||||
for v in FakeProto.SUB:
|
||||
var it: Dictionary = pr.item(v)
|
||||
_ck(int(it.get("type", -1)) == 1 and int(it.get("sub_type", -1)) == FakeProto.SUB[v],
|
||||
"item_proto %d -> WEAPON sub_type %d (got %s/%s)" % [v, FakeProto.SUB[v], it.get("type"), it.get("sub_type")])
|
||||
|
||||
# 时装 / parts[] 优先:服务器把 costume vnum 写进主角 parts[ARMOR],压过装备槽
|
||||
var seen := {"body": -1, "hair": ""}
|
||||
@@ -174,7 +223,7 @@ func _run() -> void:
|
||||
fc.equip[10] = {"vnum": 19, "count": 1, "wear": 10} # 有盾
|
||||
fc.main_parts = [88888, 0, 0, 5] # parts[ARMOR] -> shape 101
|
||||
model.weapon_gr2 = "x"; model.shield_gr2 = "x"; model.hair_gr2 = "x"
|
||||
em5._last_weapon_vnum = -1; em5._last_shield_vnum = -1
|
||||
em5._last_weapon_vnum = -1
|
||||
em5._last_body_vnum = -1; em5._last_head_vnum = -2
|
||||
em5.refresh()
|
||||
_ck(model.weapon_gr2 == "", "动物时装 -> weapon_gr2 清空 (%s)" % model.weapon_gr2)
|
||||
@@ -182,7 +231,7 @@ func _run() -> void:
|
||||
_ck(model.hair_gr2 == "", "动物时装 -> hair_gr2 清空 (%s)" % model.hair_gr2)
|
||||
# 脱下动物时装 -> 武器恢复解析(资产里 00010.gr2 可能不存在,至少不再是遮挡态)
|
||||
fc.main_parts = [0, 0, 0, 5]
|
||||
em5._last_weapon_vnum = -999; em5._last_shield_vnum = -999
|
||||
em5._last_weapon_vnum = -999
|
||||
em5._last_body_vnum = -1; em5._last_head_vnum = -2
|
||||
em5.refresh()
|
||||
_ck(model.weapon_gr2 == "" or model.weapon_gr2.ends_with("00010.gr2"),
|
||||
|
||||
Reference in New Issue
Block a user