- 连击类型由 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
130 lines
3.8 KiB
GDScript
130 lines
3.8 KiB
GDScript
# PhysicsPush —— CLIENT-GAP §3.7:受击击退,逐行移植 GameLib/PhysicsObject.cpp `CPhysicsObject`
|
||
# + GameLib/MapUtil.cpp `CEaseOutInterpolation`。单位 = Metin2 cm(平面 x / y)。
|
||
# __PushCircle -> set_direction(normalize(victim - attacker))
|
||
# IncreaseExternalForce(base, fExternalForce) -> increase_external_force(force[, collides])
|
||
# PhysicsProcess:update(dt) 后 AddMovement(get_movement())
|
||
extends RefCounted
|
||
|
||
const FRAME_TIME := 0.02 # c_fFrameTime
|
||
const EPSILON := 0.001
|
||
const LOOP_VALUE := 100
|
||
const FLT_EPSILON := 1.192092896e-07
|
||
|
||
class EaseOut:
|
||
var remaining := 0.0
|
||
var value := 0.0
|
||
var speed := 0.0
|
||
var acceleration := 0.0
|
||
var start_value := 0.0
|
||
var last_value := 0.0
|
||
|
||
func initialize() -> void:
|
||
remaining = 0.0
|
||
value = 0.0
|
||
speed = 0.0
|
||
acceleration = 0.0
|
||
start_value = 0.0
|
||
last_value = 0.0
|
||
|
||
func setup(f_start: float, f_end: float, f_time: float) -> void:
|
||
if absf(f_time) < FLT_EPSILON:
|
||
f_time = 0.01
|
||
value = f_start
|
||
start_value = f_start
|
||
last_value = f_start
|
||
speed = (2.0 * (f_end - f_start)) / f_time
|
||
acceleration = 2.0 * (f_end - f_start) / (f_time * f_time) - 2.0 * speed / f_time
|
||
remaining = f_time
|
||
|
||
func interpolate(dt: float) -> void:
|
||
last_value = value
|
||
remaining -= dt
|
||
speed += acceleration * dt
|
||
value += speed * dt
|
||
if not is_playing():
|
||
value = 0.0
|
||
last_value = 0.0
|
||
|
||
func is_playing() -> bool:
|
||
return remaining > 0.0
|
||
|
||
func changing_value() -> float:
|
||
return value - last_value
|
||
|
||
var mass := 1.0
|
||
var friction := 0.3
|
||
var direction := Vector3.ZERO
|
||
var acceleration := Vector3.ZERO
|
||
var velocity := Vector3.ZERO
|
||
var last_position := Vector3.ZERO
|
||
var _x := EaseOut.new()
|
||
var _y := EaseOut.new()
|
||
|
||
func initialize() -> void:
|
||
mass = 1.0
|
||
friction = 0.3
|
||
direction = Vector3.ZERO
|
||
acceleration = Vector3.ZERO
|
||
velocity = Vector3.ZERO
|
||
last_position = Vector3.ZERO
|
||
_x.initialize()
|
||
_y.initialize()
|
||
|
||
func set_direction(dir: Vector2) -> void:
|
||
direction = Vector3(dir.x, dir.y, 0.0)
|
||
|
||
func update(dt: float) -> void:
|
||
if _x.is_playing():
|
||
_x.interpolate(dt)
|
||
if _y.is_playing():
|
||
_y.interpolate(dt)
|
||
|
||
func _accumulate(pos: Vector3) -> Vector3:
|
||
var force := 0.0
|
||
if absf(velocity.x) < EPSILON or absf(velocity.y) < EPSILON or absf(velocity.z) < EPSILON:
|
||
force -= mass * friction
|
||
acceleration = direction * (force / mass)
|
||
velocity += acceleration
|
||
if velocity.x * direction.x < EPSILON:
|
||
velocity.x = 0.0
|
||
direction.x = 0.0
|
||
if velocity.y * direction.y < EPSILON:
|
||
velocity.y = 0.0
|
||
direction.y = 0.0
|
||
if velocity.z * direction.z < EPSILON:
|
||
velocity.z = 0.0
|
||
direction.z = 0.0
|
||
return pos + velocity
|
||
|
||
# collides(movement: Vector2) -> bool 对应 IPhysicsWorld::isPhysicalCollision(base + movement);
|
||
# 空 Callable = 没有物理世界(参考端 pWorld == NULL 分支)。
|
||
func increase_external_force(force: float, collides := Callable()) -> void:
|
||
acceleration = direction * (force / mass)
|
||
velocity = acceleration
|
||
var movement := Vector3.ZERO
|
||
for i in LOOP_VALUE:
|
||
movement = _accumulate(movement)
|
||
if collides.is_valid() and bool(collides.call(Vector2(movement.x, movement.y))):
|
||
initialize()
|
||
return
|
||
if absf(velocity.x) < EPSILON and absf(velocity.y) < EPSILON and absf(velocity.z) < EPSILON:
|
||
break
|
||
set_last_position(Vector2(movement.x, movement.y), float(LOOP_VALUE) * FRAME_TIME)
|
||
|
||
func set_last_position(p: Vector2, blending_time: float) -> void:
|
||
last_position = Vector3(p.x, p.y, 0.0)
|
||
_x.setup(0.0, p.x, blending_time)
|
||
_y.setup(0.0, p.y, blending_time)
|
||
|
||
func get_last_position() -> Vector2:
|
||
return Vector2(last_position.x, last_position.y)
|
||
|
||
# GetXMovement / GetYMovement:本帧 ease-out 增量。
|
||
func get_movement() -> Vector2:
|
||
return Vector2(_x.changing_value(), _y.changing_value())
|
||
|
||
func is_blending() -> bool:
|
||
if velocity.length() != 0.0:
|
||
return true
|
||
return _x.is_playing() or _y.is_playing()
|