using UnityEngine; using BaseGames.Core; using BaseGames.Core.Events; namespace BaseGames.Enemies { /// /// 敌人运行时数值组件(架构 07_EnemyModule §2)。 /// 由 EnemyBase.Awake() 通过 Initialize(EnemyStatsSO) 注入配置。 /// 同时订阅难度变更事件,支持游戏进行中切换难度(架构 19 §5)。 /// public class EnemyStats : MonoBehaviour { private EnemyStatsSO _config; [SerializeField] private DifficultyChangedEventChannel _onDifficultyChanged; public int MaxHP { get; private set; } public int CurrentHP { get; private set; } public int Defense { get; private set; } public float AttackCooldownTimer { get; private set; } // ── 移动速度(透传 SO,供 BD 任务运行时读取)──────────────────────── public float WalkSpeed => _config?.WalkSpeed ?? 2f; public float RunSpeed => _config?.RunSpeed ?? 4f; // ── AI 追击配置(透传 SO)────────────────────────────────────────── public float MaxChaseDistance => _config?.MaxChaseDistance ?? 15f; public float LoseLinkTimeout => _config?.LoseLinkTimeout ?? 2f; public float AlertDuration => _config?.AlertDuration ?? 0.6f; public float InvestigateDuration => _config?.InvestigateDuration ?? 3f; public float HomeRadius => _config?.HomeRadius ?? 0.5f; /// /// 每帧由 EnemyBase 更新(sqrMagnitude,避免 sqrt 开销)。 /// 使用方请与 range*range 比较,而非直接与 range 比较。 /// public float SqrDistanceToPlayer { get; set; } private readonly CompositeDisposable _subs = new(); private void OnEnable() { _onDifficultyChanged?.Subscribe(HandleDifficultyChanged).AddTo(_subs); } private void OnDisable() { _subs.Clear(); } public void Initialize(EnemyStatsSO so) { _config = so; Defense = so.Defense; // Defense 不随难度缩放(架构 19 §5) ApplyHPScaler(); CurrentHP = MaxHP; } /// 难度变更时重算 HP(保持 HP 比例,架构 19 §5)。 private void HandleDifficultyChanged(DifficultyLevel _) { if (_config == null) return; float hpRatio = MaxHP > 0 ? (float)CurrentHP / MaxHP : 1f; ApplyHPScaler(); CurrentHP = Mathf.Clamp(Mathf.RoundToInt(MaxHP * hpRatio), 1, MaxHP); } private void ApplyHPScaler() { if (_config == null) return; var scaler = ServiceLocator.GetOrDefault()?.CurrentScaler; int raw = scaler != null ? Mathf.RoundToInt(_config.MaxHP * scaler.EnemyHPMultiplier) : _config.MaxHP; MaxHP = Mathf.Max(1, raw); // 防止 MaxHP=0 导致 HP 比例计算 NaN } public void TakeDamage(int amount) { CurrentHP = Mathf.Max(0, CurrentHP - amount); } public void TickAttackCooldown(float dt) { if (AttackCooldownTimer > 0f) AttackCooldownTimer -= dt; } public void ResetAttackCooldown() { AttackCooldownTimer = _config.AttackCooldown; } } }