using System.Collections.Generic;
using UnityEngine;
using BaseGames.Core;
using BaseGames.Core.Events;
namespace BaseGames.Combat
{
///
/// 拼刀系统单例服务(架构 06_CombatModule §15)。
/// 常驻 Persistent 场景,由 GameManager 持有。
/// 当玩家与敌人的近战 HitBox 同时激活并相互重叠时触发拼刀:双方均不扣血,各自弹开。
///
[DefaultExecutionOrder(-500)]
public class ClashResolver : MonoBehaviour, IClashService
{
[SerializeField] private VoidEventChannelSO _onNailClash; // EVT_NailClash
[SerializeField] private ClashConfigSO _config;
// 防止同一碰撞在同帧被双方 HitBox 各触发一次(去重)
// Key = (min(idA,idB), max(idA,idB)),顺序无关且无 XOR 哈希碰撞风险
private readonly HashSet<(int, int)> _processedThisFrame = new();
private void Awake()
{
if (ServiceLocator.GetOrDefault() != null)
{
Destroy(gameObject);
return;
}
ServiceLocator.Register(this);
Debug.Assert(_config != null, "[ClashResolver] _config 未赋值,请在 Inspector 中指定 ClashConfigSO。", this);
}
private void LateUpdate() => _processedThisFrame.Clear();
///
/// 由 HitBox.OnTriggerEnter2D 调用。
/// 对同一对 HitBox 每帧只处理一次(HashSet 去重)。
///
public void ResolveClash(HitBox hitBoxA, HitBox hitBoxB)
{
int idA = hitBoxA.GetInstanceID();
int idB = hitBoxB.GetInstanceID();
(int, int) key = (System.Math.Min(idA, idB), System.Math.Max(idA, idB));
if (!_processedThisFrame.Add(key)) return;
// 1. 拼刀冻帧(比普通命中的 2 帧更短,强化拼刀手感)
ServiceLocator.GetOrDefault()?.FreezeFrames(_config.ClashFreezeFrames);
// 2. 双方弹开
ApplyClashKnockback(hitBoxA.OwnerRigidbody, hitBoxB.transform.position);
ApplyClashKnockback(hitBoxB.OwnerRigidbody, hitBoxA.transform.position);
// 3. 广播事件(VFX / Audio / CameraImpulse 订阅)
_onNailClash?.Raise();
}
private void ApplyClashKnockback(Rigidbody2D rb, Vector2 oppositePos)
{
if (rb == null) return;
Vector2 dir = ((Vector2)rb.transform.position - oppositePos).normalized;
rb.AddForce(dir * _config.ClashKnockbackForce, ForceMode2D.Impulse);
}
private void OnDestroy()
{
ServiceLocator.Unregister(this);
}
}
}