using Cielonos.MainGame.Buffs.Character;
using Opsive.BehaviorDesigner.Runtime;
using SLSUtilities.General;
using UnityEngine;
using UnityEngine.AI;
namespace Cielonos.MainGame.Characters
{
public partial class AutomataLandMovementSubcontroller : LandMovementSubcontroller
{
public NavMeshAgent navMeshAgent => (owner as Automata)!.behaviorSc.navMeshAgent;
public LerpFloat animatorMoveSpeedX;
public LerpFloat animatorMoveSpeedZ;
public override void Initialize()
{
base.Initialize();
navMeshAgent.isStopped = true;
animatorMoveSpeedX = new LerpFloat(0f, 5f);
animatorMoveSpeedZ = new LerpFloat(0f, 5f);
}
private Vector3 lastDirection;
protected override void Update()
{
base.Update();
HandleNavMeshState();
}
protected override void OnAnimatorMove()
{
if (owner.selfTimeSm.DeltaTime == 0 || owner.statusSm.isDead)
{
return;
}
if (navMeshAgent != null && navMeshAgent.enabled)
{
UpdateNavigationAnimParams();
}
base.OnAnimatorMove();
InitiativeMove();
lastDirection = transform.forward;
}
protected override void InitiativeMove()
{
if (navMeshAgent.enabled)
{
navMeshAgent.Move(finalMovementVelocity);
navMeshAgent.baseOffset += finalMovementVelocity.y;
navMeshAgent.baseOffset = Mathf.Max(navMeshAgent.baseOffset, 0f);
}
else
{
if (owner.collisionSc.useCharacterController)
{
owner.collisionSc.characterController.Move(finalMovementVelocity);
}
else
{
Vector3 startPosition = owner.collisionSc.mainRigidbody.position;
owner.collisionSc.mainRigidbody.MovePosition(startPosition + finalMovementVelocity);
}
}
}
// ────────────────────────────────────────────────────────────────────
// Navigation Animation
// ────────────────────────────────────────────────────────────────────
private void UpdateNavigationAnimParams()
{
if (!is8WayMovement)
{
animatorMoveSpeedZ.targetValue = navMeshAgent.isStopped
? 0f
: navMeshAgent.velocity.magnitude
+ Vector3.Angle(lastDirection, transform.forward) / owner.selfTimeSm.DeltaTime * 0.02f;
animatorMoveSpeedZ.Update(0.2f, 1);
animator.SetFloat("MoveSpeedZ", animatorMoveSpeedZ.currentValue);
}
else
{
if (!navMeshAgent.isStopped)
{
Vector3 velocity = navMeshAgent.velocity;
animatorMoveSpeedX.targetValue = Vector3.Dot(velocity, transform.right);
animatorMoveSpeedZ.targetValue = Vector3.Dot(velocity, transform.forward);
}
else
{
animatorMoveSpeedX.targetValue = 0f;
animatorMoveSpeedZ.targetValue = 0f;
}
animatorMoveSpeedX.Update(0.2f, 1);
animatorMoveSpeedZ.Update(0.2f, 1);
animator.SetFloat("MoveSpeedX", animatorMoveSpeedX.currentValue);
animator.SetFloat("MoveSpeedZ", animatorMoveSpeedZ.currentValue);
}
}
}
// ════════════════════════════════════════════════════════════════════════
// Hit Impact & Stun
// ════════════════════════════════════════════════════════════════════════
public partial class AutomataLandMovementSubcontroller
{
private const float DEFAULT_STUN_DURATION = 0.25f;
public bool stagnantFirstHalf;
public bool stagnantLastHalf;
/// 当前是否处于击飞引发的眩晕状态(由落地检测解除)。
private bool isLaunchStunned;
private Automata Automata => owner as Automata;
///
/// 处理受击位移(击退 / 击飞)。
///
/// 力的矢量(XZ = 击退方向和强度,Y = 击飞强度)。
/// 是否为"击飞"(无视抗性,自动眩晕)。
/// Y 轴是否使用抛物线物理。仅 isLaunch 时有意义。
/// 脉冲持续时间(秒),0 使用默认值。
/// 速度衰减曲线,null 使用默认 EaseOut。
/// (仅击退)是否附加眩晕状态。击飞始终附加眩晕。
/// (仅击退)眩晕持续时间(秒),0 跟随脉冲持续时间。
/// 重力缩放倍率。0 = 浮空,1 = 正常(默认),大于 1 = 加速坠落。
/// 重力修正持续时间(秒)。0 = 不施加重力修正。
public void ApplyHitImpact(Vector3 force, bool isLaunch, bool isParabolic,
float duration = 0f, AnimationCurve curve = null,
bool applyStun = false, float stunDuration = 0f,
float gravityMultiplier = 1f, float gravityDuration = 0f)
{
if (isLaunch && navMeshAgent.enabled) navMeshAgent.enabled = false;
if (!stagnantFirstHalf && !stagnantLastHalf)
{
stagnantFirstHalf = true;
}
impulseSm.ApplyImpulse(force, isLaunch, duration, curve, isParabolic);
if (isLaunch)
{
ApplyStun();
isLaunchStunned = true;
}
else if (applyStun)
{
float effectiveDuration = stunDuration > 0f
? stunDuration
: (duration > 0f ? duration : DEFAULT_STUN_DURATION);
ApplyStun(effectiveDuration);
}
ApplyGravityModifier(gravityMultiplier, gravityDuration);
}
// ────────────────────────────────────────────────────────────────────
// NavMesh State Machine
// ────────────────────────────────────────────────────────────────────
private void HandleNavMeshState()
{
if (navMeshAgent == null) return;
if (stagnantFirstHalf && !isOnGround && finalMovementVelocity.y < 0)
{
stagnantFirstHalf = false;
stagnantLastHalf = true;
}
if (stagnantLastHalf && isOnGround)
{
impulseSm.ClearLaunch();
TryRemoveLaunchStun();
if (!navMeshAgent.enabled)
{
navMeshAgent.enabled = true;
navMeshAgent.Warp(transform.position);
}
stagnantFirstHalf = false;
stagnantLastHalf = false;
}
}
// ────────────────────────────────────────────────────────────────────
// Gravity Modifier
// ────────────────────────────────────────────────────────────────────
///
/// 通过 VerticalMoveModification Buff 施加重力修正。
/// multiplier 大于 1 时先清除已有浮空 Buff,确保 Finisher 坠落生效。
///
private void ApplyGravityModifier(float multiplier, float duration)
{
if (duration <= 0f) return;
if (multiplier > 1f)
{
if (owner.buffSm.TryGetBuff(out var existingBuff))
{
existingBuff.Remove();
}
}
new VerticalMoveModification(duration, multiplier).Apply(owner);
}
// ────────────────────────────────────────────────────────────────────
// Stun Helpers
// ────────────────────────────────────────────────────────────────────
///
/// 施加眩晕。无参数版为永久眩晕(击飞用),由落地检测解除;
/// 带 duration 版为定时眩晕(击退用),到期自动解除。
///
private void ApplyStun(float duration = -1f)
{
Automata.statusSm.AddStatus(StatusType.Stun);
Automata.behaviorSc.mainBehaviorTree.StopBehavior(true);
if (duration > 0f)
{
owner.selfTimeSm.AddLocalTimer(duration, () => TryResumeFromStun());
}
}
///
/// 尝试解除击飞眩晕(落地时调用)。
///
private void TryRemoveLaunchStun()
{
if (!isLaunchStunned) return;
isLaunchStunned = false;
TryResumeFromStun();
}
///
/// 移除一层 Stun,若所有 Stun 层归零则恢复行为树。
///
private void TryResumeFromStun()
{
Automata.statusSm.RemoveStatus(StatusType.Stun);
if (!Automata.statusSm.HasStatus(StatusType.Stun))
{
Automata.behaviorSc.mainBehaviorTree.StartBehavior();
}
}
}
}