多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -8,7 +8,8 @@
"versionDefines": [],
"rootNamespace": "BaseGames.Combat.StatusEffects",
"references": [
"BaseGames.Combat"
"BaseGames.Combat",
"BaseGames.Core.Events"
],
"autoReferenced": true,
"overrideReferences": false,

View File

@@ -0,0 +1,45 @@
namespace BaseGames.Combat.StatusEffects
{
/// <summary>
/// 燃烧效果(架构 06_CombatModule §11
/// 规则不可叠加MaxStacks = 1重复施加刷新持续时间每 0.5 秒造成 1 点 True 伤害。
/// </summary>
public class FireEffect : StatusEffect
{
private const float BaseDuration = 3.0f; // 持续 3 秒
private const float DotInterval = 0.5f; // 每 0.5 秒一次
public override StatusEffectType EffectType => StatusEffectType.Fire;
public override int MaxStacks => 1;
public FireEffect()
{
TickInterval = DotInterval;
}
public override void OnApply(StatusEffectManager owner)
{
base.OnApply(owner);
owner.SetShaderParam("_FireGlow", 1f);
}
/// <summary>每次 DoT Tick 造成 1 点 True 伤害(绕过护盾,无敌帧不免疫)。</summary>
public override void OnTick()
{
var info = new DamageInfo.Builder()
.SetRaw(1)
.SetType(DamageType.True)
.SetFlags(DamageFlags.IgnoreIFrame)
.Build();
Owner?.ApplyDirectDamage(info);
}
public override void OnExpire()
{
Owner?.SetShaderParam("_FireGlow", 0f);
}
protected override float GetBaseDuration() => BaseDuration;
public override string GetDisplayName() => "燃烧";
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1dfc988231a6ac14a9aa035ba1719ab0
guid: 2d692ae17737ac54bb0940c3487a59be
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,54 @@
namespace BaseGames.Combat.StatusEffects
{
/// <summary>
/// 中毒效果(架构 06_CombatModule §11
/// 规则:最多叠加 3 层;每层叠加伤害 +1每 1 秒造成 StackCount 点 True 伤害。
/// </summary>
public class PoisonEffect : StatusEffect
{
private const float BaseDuration = 5.0f; // 持续 5 秒
private const float DotInterval = 1.0f; // 每 1 秒一次
public override StatusEffectType EffectType => StatusEffectType.Poison;
public override int MaxStacks => 3;
public PoisonEffect()
{
TickInterval = DotInterval;
}
public override void OnApply(StatusEffectManager owner)
{
base.OnApply(owner);
UpdateShader();
}
public override void OnStack()
{
base.OnStack();
UpdateShader();
}
public override void OnTick()
{
var info = new DamageInfo.Builder()
.SetRaw(StackCount) // 叠层越多伤害越高
.SetType(DamageType.True)
.SetFlags(DamageFlags.IgnoreIFrame)
.Build();
Owner?.ApplyDirectDamage(info);
}
public override void OnExpire()
{
StackCount = 0;
Owner?.SetShaderParam("_PoisonGlow", 0f);
}
private void UpdateShader()
=> Owner?.SetShaderParam("_PoisonGlow", StackCount / (float)MaxStacks);
protected override float GetBaseDuration() => BaseDuration;
public override string GetDisplayName() => $"中毒 ×{StackCount}";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97257dbfc13b78441a89c652f51310cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
namespace BaseGames.Combat.StatusEffects
{
/// <summary>
/// 硬直效果(架构 06_CombatModule §11
/// 规则不可叠加MaxStacks = 1施加期间宿主无法执行动作。
/// 外部查询entity.GetComponent&lt;StatusEffectManager&gt;()?.HasEffect(StatusEffectType.Stagger)
/// </summary>
public class StaggerEffect : StatusEffect
{
private const float BaseDuration = 0.5f; // 默认硬直持续时间(秒)
private readonly float _overrideDuration;
/// <param name="duration">自定义持续时间(&lt;= 0 使用默认值)。</param>
public StaggerEffect(float duration = 0f)
{
_overrideDuration = duration > 0f ? duration : BaseDuration;
TickInterval = 0f; // 无 DoT Tick
}
public override StatusEffectType EffectType => StatusEffectType.Stagger;
public override int MaxStacks => 1;
public override void OnApply(StatusEffectManager owner)
{
Owner = owner;
Duration = _overrideDuration;
}
protected override float GetBaseDuration() => _overrideDuration;
public override string GetDisplayName() => "硬直";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23c67d6324b88a1468bd20baa4f109c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,91 @@
using UnityEngine;
namespace BaseGames.Combat.StatusEffects
{
/// <summary>
/// 状态效果抽象基类(架构 06_CombatModule §11
/// ⚠️ 类名为 StatusEffect非 StatusEffectBase
///
/// 生命周期:
/// OnApply(owner) → Update(delta) × N [内部调用 OnTick()] → OnExpire()
///
/// 叠加规则:同类型再次施加时调用 OnStack()Manager 保证每种类型只有一个实例。
/// </summary>
public abstract class StatusEffect
{
/// <summary>效果类型标识(用作 Dictionary key。</summary>
public abstract StatusEffectType EffectType { get; }
/// <summary>最大叠加层数1 = 不可叠加,重复施加只刷新持续时间)。</summary>
public abstract int MaxStacks { get; }
/// <summary>当前叠加层数。</summary>
public int StackCount { get; protected set; } = 1;
/// <summary>当前剩余持续时间(秒)。</summary>
public float Duration { get; protected set; }
/// <summary>每次 Tick 的间隔(秒)。</summary>
public float TickInterval { get; protected set; }
/// <summary>是否已过期(由 Manager 每帧检查)。</summary>
public virtual bool IsExpired => Duration <= 0f;
private float _tickTimer;
/// <summary>宿主 ManagerOnApply 时注入OnTick/OnExpire 中可访问)。</summary>
public StatusEffectManager Owner { get; protected set; }
// ── 生命周期回调(可重写)─────────────────────────────────────────
/// <summary>
/// 效果施加时调用Owner 在此注入)。
/// ⚠️ 参数为 StatusEffectManager非 IDamageable架构 06 §11。
/// </summary>
public virtual void OnApply(StatusEffectManager owner)
{
Owner = owner;
Duration = GetBaseDuration();
}
/// <summary>
/// 同类型效果再次施加时调用(叠层 / 刷新持续时间)。
/// 默认行为:刷新持续时间并叠加层数(若未达上限)。
/// </summary>
public virtual void OnStack()
{
Duration = GetBaseDuration();
StackCount = Mathf.Min(StackCount + 1, MaxStacks);
}
/// <summary>每个 TickInterval 秒调用一次DoT 等周期效果)。</summary>
public virtual void OnTick() { }
/// <summary>效果到期 / 被净化时调用。⚠️ 名称 OnExpire非 OnRemove。</summary>
public virtual void OnExpire() { }
// ── 框架驱动(由 Manager.Update 调用,每帧执行)──────────────────
/// <summary>递减持续时间并在到达 Tick 间隔时触发 OnTick。</summary>
public void Update(float delta)
{
Duration -= delta;
if (TickInterval <= 0f) return;
_tickTimer += delta;
if (_tickTimer >= TickInterval)
{
_tickTimer -= TickInterval;
OnTick();
}
}
// ── 子类必须实现 ──────────────────────────────────────────────────
/// <summary>返回本类型效果的基础持续时间(秒)。</summary>
protected abstract float GetBaseDuration();
/// <summary>返回本效果的本地化显示名称。</summary>
public abstract string GetDisplayName();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4490046c0c848054e9a6846dc272f9f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using UnityEngine;
using BaseGames.Core.Events;
namespace BaseGames.Combat.StatusEffects
{
/// <summary>状态效果事件(应用 / 到期时广播,可用于 UI 更新)。</summary>
public struct StatusEffectEvent
{
/// <summary>效果类型。</summary>
public StatusEffectType EffectType;
/// <summary>当前叠加层数(到期时为 0。</summary>
public int StackCount;
/// <summary>剩余持续时间(到期时为 0。</summary>
public float RemainingDuration;
}
[CreateAssetMenu(menuName = "Events/StatusEffect")]
public class StatusEffectEventChannelSO : BaseEventChannelSO<StatusEffectEvent> { }
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2aa7161466b28442acab31ae092166b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,20 +1,176 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using BaseGames.Combat;
namespace BaseGames.Combat.StatusEffects
{
/// <summary>
/// 状态效果管理器Phase 1 桩)
/// 实现 IStatusEffectable 接口,由 HurtBox 通过接口调用,避免程序集循环依赖。
/// Phase 2 实现完整的效果叠加、持续时间、DoT 伤害计算。
/// 状态效果管理器。
///
/// 架构 06_CombatModule §11
/// - 双结构ListUpdate 遍历)+ DictionaryO(1) 类型查找)
/// - 实现 IStatusEffectable接受来自 HurtBox 的 DamageType 并映射到具体效果
/// - DealDotDamageStatusEffect 子类通过 Owner 调用,绕过无敌帧造成 DoT
/// - CleanseEffect净化指定类型效果道具/技能使用)
/// </summary>
[RequireComponent(typeof(SpriteRenderer))]
public class StatusEffectManager : MonoBehaviour, IStatusEffectable
{
// Phase 1空实现
public void ApplyStatusEffect(DamageType type) { }
}
[Header("事件频道(可选)")]
[SerializeField] private StatusEffectEventChannelSO _onStatusEffectApplied;
[SerializeField] private StatusEffectEventChannelSO _onStatusEffectExpired;
// ── Phase 1 占位效果类型 ──────────────────────────────────────────────────
public class FireEffect { }
public class PoisonEffect { }
// ── 双结构 ─────────────────────────────────────────────────────────
private readonly List<StatusEffect> _activeList = new();
private readonly Dictionary<StatusEffectType, StatusEffect> _activeIndex = new();
// ── Shader 渲染MaterialPropertyBlock不修改共享材质─────────
private SpriteRenderer _renderer;
private MaterialPropertyBlock _propBlock;
// ── DoT 伤害代理(由 StatusEffect.OnTick 通过 Owner 调用)──────────
private IDamageable _damageable;
// ── 效果工厂字典(可在 Awake 后动态注册)─────────────────────
private readonly Dictionary<DamageType, Func<StatusEffect>> _effectFactories = new();
private void Awake()
{
_renderer = GetComponent<SpriteRenderer>();
_propBlock = new MaterialPropertyBlock();
_damageable = GetComponentInParent<IDamageable>();
// 默认标准效果注册(子类或外部模块可调用 RegisterEffectFactory 覆盖或扩展)
RegisterEffectFactory(DamageType.Fire, () => new FireEffect());
RegisterEffectFactory(DamageType.Poison, () => new PoisonEffect());
}
private void Update()
{
float delta = Time.deltaTime;
// 逆序遍历,避免移除时索引错位
for (int i = _activeList.Count - 1; i >= 0; i--)
{
StatusEffect effect = _activeList[i];
effect.Update(delta);
if (effect.IsExpired)
RemoveAt(i, effect);
}
}
// ── IStatusEffectable 实现 ─────────────────────────────────────────
/// <summary>
/// HurtBox 调用入口:将 DamageType 映射为具体 StatusEffect 实例并施加。
/// </summary>
public void ApplyStatusEffect(DamageType type)
{
StatusEffect effect = CreateEffect(type);
if (effect != null)
ApplyEffect(effect);
}
/// <summary>
/// 注册或覆盖一个 DamageType 对应的效果工厂。
/// Boss 或特殊游玩法式可在运行时注册自定义效果。
/// </summary>
public void RegisterEffectFactory(DamageType type, Func<StatusEffect> factory)
=> _effectFactories[type] = factory;
// ── 公开 API ───────────────────────────────────────────────────────
/// <summary>直接施加一个具体效果(供技能/Boss 使用)。</summary>
public void ApplyEffect(StatusEffect effect)
{
if (_activeIndex.TryGetValue(effect.EffectType, out StatusEffect existing))
{
existing.OnStack();
BroadcastApplied(existing);
}
else
{
effect.OnApply(this);
_activeList.Add(effect);
_activeIndex[effect.EffectType] = effect;
BroadcastApplied(effect);
}
}
/// <summary>净化指定类型效果(净化道具/技能调用)。</summary>
public void CleanseEffect(StatusEffectType type)
{
if (!_activeIndex.TryGetValue(type, out StatusEffect effect)) return;
effect.OnExpire();
_activeIndex.Remove(type);
_activeList.Remove(effect);
BroadcastExpired(effect);
}
/// <summary>查询是否存在指定类型效果(供状态机/UI 轮询)。</summary>
public bool HasEffect(StatusEffectType type) => _activeIndex.ContainsKey(type);
/// <summary>获取指定类型效果(可为 null。</summary>
public StatusEffect GetEffect(StatusEffectType type)
=> _activeIndex.TryGetValue(type, out var e) ? e : null;
/// <summary>
/// DoT 伤害代理(架构 06 §10。StatusEffect.OnTick() 调用此方法,传入已构建好的 DamageInfo。
/// </summary>
public void ApplyDirectDamage(DamageInfo info)
{
_damageable?.TakeDamage(info);
}
/// <summary>设置 Sprite Shader 参数MaterialPropertyBlock不修改共享材质。</summary>
public void SetShaderParam(string param, float value)
{
if (_renderer == null) return;
_renderer.GetPropertyBlock(_propBlock);
_propBlock.SetFloat(param, value);
_renderer.SetPropertyBlock(_propBlock);
}
/// <summary>净化所有状态效果(存档点激活 / 返回城镇等调用)。</summary>
public void CleanseAll()
{
foreach (var e in _activeList) e.OnExpire();
_activeList.Clear();
_activeIndex.Clear();
}
// ── 私有辅助 ───────────────────────────────────────────────────────
private StatusEffect CreateEffect(DamageType type)
=> _effectFactories.TryGetValue(type, out var factory) ? factory() : null;
private void RemoveAt(int index, StatusEffect effect)
{
effect.OnExpire();
_activeList.RemoveAt(index);
_activeIndex.Remove(effect.EffectType);
BroadcastExpired(effect);
}
private void BroadcastApplied(StatusEffect effect)
{
_onStatusEffectApplied?.Raise(new StatusEffectEvent
{
EffectType = effect.EffectType,
StackCount = effect.StackCount,
RemainingDuration = effect.Duration,
});
}
private void BroadcastExpired(StatusEffect effect)
{
_onStatusEffectExpired?.Raise(new StatusEffectEvent
{
EffectType = effect.EffectType,
StackCount = 0,
RemainingDuration = 0f,
});
}
}
}

View File

@@ -0,0 +1,15 @@
namespace BaseGames.Combat.StatusEffects
{
/// <summary>
/// 状态效果类型枚举(架构 06_CombatModule §11
/// 用于状态机索引Dictionary key和事件载荷与 DamageType 相互独立。
/// </summary>
public enum StatusEffectType
{
Fire, // 燃烧DoT不可叠加重复施加刷新持续时间
Poison, // 中毒DoT最多 3 层叠加)
Stagger, // 硬直(无法行动 N 秒)
Freeze, // 冻结(减速 / 固化)
Stun, // 眩晕(无法行动)
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a5502c061dc69b4b82113ed5b282740
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,3 +0,0 @@
// Placeholder to prevent asmdef-no-scripts warning.
namespace BaseGames.Combat.StatusEffects { }