Files
mtgodot-poc/project/net_world_push_test.gd
T
shenleiandClaude Opus 5 1933a5ceda 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
2026-09-12 13:15:28 +09:00

126 lines
4.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# net_world_push_test —— CLIENT-GAP §3.7 受击击退的场景侧:
# __PushCircle + IncreaseExternalForce -> net_world.push_victim
# CActorInstance::PhysicsProcess(每帧 AddMovement(GetX/YMovement)-> 节点偏移
# GetBlendingPositionblending 中 = 当前位置 + LastPosition-> OnHit 的 CG_SYNC_POSITION 坐标
# godot --headless --path project --script net_world_push_test.gd
extends SceneTree
const NetWorld = preload("res://net_world.gd")
const PhysicsPush = preload("res://physics_push.gd")
class FakeClient extends Node:
signal entity_spawned(entity: Dictionary)
signal entity_despawned(vid: int)
signal entity_moved(vid: int)
signal entity_main_set(vid: int)
signal entity_info(vid: int, entity: Dictionary)
signal chat(type: int, vid: int, text: String)
signal vitals_changed(vid: int)
signal entity_dead(vid: int)
signal damage(vid: int, amount: int, flag: int)
var ents := {}
func get_entity(vid: int) -> Dictionary: return ents.get(vid, {})
func get_entities() -> Array: return ents.values()
func get_pvp_relations() -> Array: return []
func get_duel() -> Dictionary: return {}
func add(vid: int, cm: Vector2, extra := {}) -> Dictionary:
var d := {"vid": vid, "name": "e%d" % vid, "is_main": false,
"func": 0, "moving": false, "angle_deg": 0.0,
"hp": 100, "max_hp": 100, "dead": false, "race": 0,
"affect_flags": 0, "parts": [0, 0, 0, 0]}
d.merge(extra, true)
ents[vid] = d
place(vid, cm)
return d
func place(vid: int, cm: Vector2) -> void:
ents[vid]["pos"] = Vector3(cm.x * 0.01, 0.0, -cm.y * 0.01)
ents[vid]["pos_cm"] = Vector3(cm.x, cm.y, 0.0)
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
var _done := false
func _init() -> void:
await _run()
_ck(_done, "test body ran to completion (a SCRIPT ERROR aborted _run)")
if _fail == 0:
print("PASS: net_world_push_test (push_victim / PhysicsProcess offset / GetBlendingPosition)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _off(v: Vector2) -> Vector3:
return Vector3(v.x / 100.0, 0.0, -v.y / 100.0)
func _run() -> void:
var mount := Node3D.new()
get_root().add_child(mount)
var fc := FakeClient.new()
get_root().add_child(fc)
var nw: Node = NetWorld.new()
get_root().add_child(nw)
nw.setup(fc, mount)
fc.add(1, Vector2.ZERO, {"is_main": true})
fc.entity_main_set.emit(1)
fc.entity_spawned.emit(fc.add(2, Vector2(300, 0)))
await process_frame
nw._process(0.02)
var n: Node3D = nw.node_for(2)
_ck(n != null, "victim node spawned")
if n == null:
return
_ck(not nw.is_pushing(2), "no push -> not pushing")
_ck(nw.blending_position(2).is_equal_approx(n.global_position), "not blending -> GetBlendingPosition = current position")
# 攻击者在西边:victim - attacker = actor-world (+1, 0)
nw.push_victim(2, Vector2(1, 0), 3.0)
var ref := PhysicsPush.new()
ref.set_direction(Vector2(1, 0))
ref.increase_external_force(3.0)
var last := ref.get_last_position()
_ck(last.x > 0.0, "reference push has a positive displacement")
_ck(nw.is_pushing(2), "push_victim -> is_pushing")
_ck(nw.blending_position(2).is_equal_approx(n.global_position + _off(last)),
"blending -> GetBlendingPosition = current + LastPosition, got %s" % nw.blending_position(2))
var acc := Vector2.ZERO
for i in 60:
nw._process(0.02)
ref.update(0.02)
acc += ref.get_movement()
var want: Vector3 = nw._grounded(fc.ents[2]["pos"]) + _off(acc)
_ck(acc.x > 0.0 and n.position.is_equal_approx(want),
"PhysicsProcess: node follows the accumulated ease-out movement, got %s want %s" % [n.position, want])
# blending 中服务器位置变化:偏移叠在新位置上(位移还在进行)
fc.place(2, Vector2(300, 50))
nw._process(0.02)
ref.update(0.02)
acc += ref.get_movement()
want = nw._grounded(fc.ents[2]["pos"]) + _off(acc)
_ck(n.position.is_equal_approx(want), "server move while blending -> offset kept on top, got %s want %s" % [n.position, want])
for i in 100:
nw._process(0.02)
ref.update(0.02)
acc += ref.get_movement()
_ck(not nw.is_pushing(2), "blend over -> not pushing")
want = nw._grounded(fc.ents[2]["pos"]) + _off(acc)
_ck(n.position.is_equal_approx(want), "blend over -> actor stays where the push left it, got %s want %s" % [n.position, want])
# blend 结束后服务器位置再变:以服务器位置为准(丢弃本地击退偏移)
fc.place(2, Vector2(320, 50))
nw._process(0.02)
_ck(n.position.is_equal_approx(nw._grounded(fc.ents[2]["pos"])), "server position after the blend supersedes the push offset")
nw.push_victim(2, Vector2(0, 1), 3.0)
fc.entity_despawned.emit(2)
_ck(not nw.is_pushing(2) and not nw._push.has(2), "despawn drops the push state")
_done = true