# HitReaction —— CLIENT-GAP §3.7 受击方视图(PlayerView / MobView)共用的受击状态机。 # __HitGood / __HitGreate / __HitStone(ActorInstanceBattle.cpp)的分支判定、 # __Shake + ShakeProcess(世界矩阵平移 ±rand()%10 cm)、CGraphicThingInstance::InsertDelay、 # InterceptOnceMotion + PushOnceMotion + PushLoopMotion 的动作链。 # 视图只提供「按状态名绑定一段一次性动作」的 Callable(bind(step) -> bool,资源缺失 = false, # 等同 GetMotionKey 失败时 InterceptOnceMotion / PushOnceMotion 返回 FALSE)。 extends RefCounted const SHAKE_TIME := 0.1 # __Shake(100):m_dwShakeTime = now + 100 ms const SHAKE_RAND_CM := 10 # ShakeProcess:rand() % 10 const LOOP_STATES := ["wait", "walk", "run"] const DAMAGE_STATES := ["damage", "damage_back"] # IsDamage 类 const KNOCKDOWN_STATES := ["knockdown", "knockdown_back"] # IsKnockDown:DAMAGE_FLYING(_BACK) const STANDUP_STATES := ["standup", "standup_back"] # __IsStandUpMotion:STAND_UP(_BACK) # CActorInstance::isLock:NORMAL_ATTACK / COMBO_ATTACK_* / SPECIAL_* / 钓鱼 / 表情跳舞等 const LOCK_STATES := ["attack", "combo", "skill", "__motion", "emotion", "dig", "fishing", "fishing_react", "fishing_catch", "fishing_fail"] const SKILL_STATES := ["skill"] # IsUsingSkill:SKILL 段 / SPECIAL_1..6 var delay_left := 0.0 var speed := 1.0 # 当前动作的 fSpeedRatio(InsertDelay 结束后恢复) var shake_left := 0.0 var queue: Array = [] # 剩余的 PushOnceMotion 步 var resume := "wait" # 链尾 PushLoopMotion 的循环动作 var active := false # 受击动作链正在播 var real_dead := false # m_isRealDead:晕眩击倒后不起身 var _shaking := false var _shake_base := Vector3.ZERO static func is_reaction(state: String) -> bool: return state in DAMAGE_STATES or state in KNOCKDOWN_STATES or state in STANDUP_STATES # __HitGood:返回候选动作链(按顺序尝试,首步绑定成功的那条生效)。 func good(state: String, scalar: float, stunned: bool) -> Array: if state in KNOCKDOWN_STATES: return [] if stunned: return [] # IsStun() -> Die():死亡动作由服务器驱动(seam) shake() if state in LOCK_STATES: return [] if scalar < 0.0: return [["damage"]] return [["damage_back"], ["damage"]] # __HitGreate func greate(state: String, scalar: float, stunned: bool) -> Array: if state in KNOCKDOWN_STATES or state in STANDUP_STATES: return [] shake() if state in SKILL_STATES: return [] if stunned: if scalar < 0.0: return [["knockdown"]] return [["knockdown_back"], ["knockdown"]] if scalar < 0.0: return [["knockdown", "standup"]] return [["knockdown_back", "standup_back"], ["knockdown", "standup"]] # __HitStone func stone(stunned: bool) -> void: if not stunned: shake() func shake() -> void: shake_left = SHAKE_TIME func insert_delay(d: float, anim: Object) -> void: if d <= 0.0: return delay_left = d if anim: anim.set("time_scale", 0.0) # 绑定新动作时的 time_scale:InsertDelay 期间保持冻结。 func scale_for(ratio: float) -> float: speed = ratio return 0.0 if delay_left > 0.0 else ratio # 首步绑定成功 -> 记下链尾要回的循环动作,剩余步入队。 func start(chains: Array, current: String, bind: Callable, dead_after := false) -> bool: for chain: Array in chains: if chain.is_empty() or not bool(bind.call(String(chain[0]))): continue if not is_reaction(current): resume = current if current in LOOP_STATES else "wait" queue = chain.slice(1) active = true real_dead = dead_after return true return false # 一段受击动作播完:绑下一步(缺资源的步跳过);返回 false = 链结束。 func advance(bind: Callable) -> bool: while not queue.is_empty(): if bool(bind.call(String(queue.pop_front()))): return true active = false return false func cancel() -> void: active = false queue.clear() real_dead = false func tick(dt: float, anim: Object, model: Node3D) -> void: if delay_left > 0.0: delay_left -= dt if delay_left <= 0.0 and anim: anim.set("time_scale", speed) if shake_left > 0.0: shake_left -= dt if model == null: return if shake_left > 0.0: if not _shaking: _shaking = true _shake_base = model.position model.position = _shake_base + Vector3(_rand_cm(), _rand_cm(), _rand_cm()) / 100.0 elif _shaking: _shaking = false model.position = _shake_base static func _rand_cm() -> float: var v := float(randi() % SHAKE_RAND_CM) return v if randi() % 2 == 0 else -v