Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/Entity/Hediff.cs
2025-09-02 11:08:15 +08:00

277 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using Data;
using UnityEngine;
namespace Entity
{
/// <summary>
/// 定义一个接口,用于提供属性偏移量。
/// 任何实现此接口的组件都可以贡献实体的属性修正。
/// </summary>
public interface IAttributesOffsetProvider
{
/// <summary>
/// 获取此提供者当前的属性偏移量定义。
/// </summary>
/// <returns>此提供者所带来的属性偏移量。</returns>
AttributesOffsetDef GetAttributesOffset();
}
/// <summary>
/// 表示一个实体上运行时存在的健康状态Hediff
/// Hediff 可以是疾病、增益、减益、伤口等,它们会影响实体的属性、行为或状态。
/// </summary>
public class Hediff
{
/// <summary>
/// 此 Hediff 的定义数据。
/// </summary>
public HediffDef def { get; private set; }
/// <summary>
/// 此 Hediff 附加到的活体实体。
/// </summary>
public LivingEntity parent { get; internal set; }
/// <summary>
/// 此 Hediff 存在的当前年龄(以秒为单位)。
/// </summary>
public float Age { get; private set; } = 0f;
/// <summary>
/// 当前激活的 Hediff 阶段的索引。
/// </summary>
private int _currentStageIndex = -1;
/// <summary>
/// 附加到此 Hediff 上的所有组件列表。
/// </summary>
public List<HediffComp> Comps { get; private set; } = new List<HediffComp>();
/// <summary>
/// 标志,指示此 Hediff 是否应该被父实体移除。
/// </summary>
public bool ShouldRemove { get; private set; } = false;
/// <summary>
/// 内部缓存,用于存储计算出的总属性偏移量。
/// </summary>
private AttributesOffsetDef _cachedTotalAttributesOffset;
/// <summary>
/// 标志,指示 Hediff 的属性偏移量是否需要重新计算。
/// </summary>
private bool _attribsDirty = true;
/// <summary>
/// 构造函数,创建一个新的运行时 Hediff 实例。
/// </summary>
/// <param name="definition">此 Hediff 的定义。</param>
/// <exception cref="ArgumentNullException">如果传入的定义为空。</exception>
public Hediff(HediffDef definition)
{
if (definition == null)
{
throw new ArgumentNullException(nameof(definition), "Hediff 定义不能为空。");
}
this.def = definition;
this._attribsDirty = true; // 构造时标记需要计算属性
// 确保阶段列表按开始时间排序,以便正确判断当前阶段。
if (this.def.stages == null) this.def.stages = new List<HediffStageDef>();
this.def.stages = this.def.stages.OrderBy(s => s.start).ToList();
// 实例化所有定义的组件。
if (def.comps != null)
{
foreach (var compDef in def.comps)
{
if (compDef.compClass != null && typeof(HediffComp).IsAssignableFrom(compDef.compClass))
{
try
{
// 使用 Activator.CreateInstance 动态创建组件实例,并传入构造函数参数。
// HediffComp 的构造函数需要接受 Hediff parentHediff 和 HediffCompDef def。
var comp = (HediffComp)Activator.CreateInstance(compDef.compClass, this, compDef);
Comps.Add(comp);
comp.Initialize(); // 初始化组件
}
catch (Exception ex)
{
Debug.LogError(
$"实例化健康状态组件 '{compDef.compClass?.Name ?? ""}' 失败,所属健康状态 '{def.defName ?? def.GetType().Name}'{ex.Message}");
}
}
else
{
Debug.LogWarning(
$"警告:健康状态组件定义 '{compDef.compClass?.Name ?? ""}' 无效或未继承自 HediffComp所属健康状态 '{def.defName ?? def.GetType().Name}'。");
}
}
}
// 初始化时确定第一个阶段,这会触发 SetDirty()。
UpdateStageIndex();
}
/// <summary>
/// 获取此 Hediff 当前激活的阶段定义。
/// 如果没有激活的阶段或阶段列表为空,则返回 null。
/// </summary>
public HediffStageDef CurrentStage
{
get
{
if (_currentStageIndex >= 0 && _currentStageIndex < def.stages.Count)
{
return def.stages[_currentStageIndex];
}
return null;
}
}
/// <summary>
/// 获取此 Hediff 当前累计的总属性偏移量。
/// 只有当属性被标记为“脏”时才重新计算,否则返回缓存值。
/// </summary>
public AttributesOffsetDef CurrentTotalAttributesOffset
{
get
{
// 只有当属性需要更新时才重新计算。
if (_attribsDirty || _cachedTotalAttributesOffset == null)
{
_cachedTotalAttributesOffset = CalculateTotalAttributesOffset();
_attribsDirty = false; // 计算完成后重置标志。
}
return _cachedTotalAttributesOffset;
}
}
/// <summary>
/// 内部方法,用于实际计算 Hediff 的所有总属性偏移量。
/// 这包括当前阶段的偏移量和所有组件提供的偏移量。
/// </summary>
/// <returns>此 Hediff 当前的总属性偏移量。</returns>
private AttributesOffsetDef CalculateTotalAttributesOffset()
{
var totalOffset = new AttributesOffsetDef();
if (CurrentStage != null)
{
totalOffset += CurrentStage.attributesOffset;
}
// 累加来自组件的属性偏移。
foreach (var comp in Comps)
{
if (comp is IAttributesOffsetProvider attributesOffsetProvider)
{
totalOffset += attributesOffsetProvider.GetAttributesOffset();
}
}
return totalOffset;
}
/// <summary>
/// 标记 Hediff 的属性偏移量需要重新计算。
/// 当 Hediff 的内部状态改变并可能影响属性时调用。
/// 同时会通知其父 LivingEntity使其也更新属性缓存。
/// </summary>
internal void SetDirty()
{
_attribsDirty = true;
// 如果此 Hediff 附加到了一个 LivingEntity 上,也通知 LivingEntity 属性可能改变。
if (parent)
{
parent.SetAttribsDirty();
}
}
/// <summary>
/// 更新 Hediff 的年龄,检查阶段变化并更新所有组件。
/// </summary>
/// <param name="deltaTime">自上次更新以来的时间(秒)。</param>
public void Tick(float deltaTime)
{
if (ShouldRemove)
return; // 已经标记为移除,无需继续更新。
Age += deltaTime; // 增加 Hediff 的年龄。
UpdateStageIndex(); // 检查是否有阶段变化(此方法内部会调用 SetDirty
// 更新所有组件。
foreach (var comp in Comps)
{
comp.Tick(deltaTime);
}
// 检查 Hediff 是否到期(如果 def.time > 0 表示有时限,否则为永久)。
if (def.time > 0 && Age >= def.time)
{
ShouldRemove = true;
SetDirty(); // Hediff 将被移除,其贡献的属性将不再有效,标记为脏。
}
}
/// <summary>
/// 根据当前年龄更新阶段索引。
/// 如果阶段发生变化,会调用 SetDirty()。
/// </summary>
private void UpdateStageIndex()
{
var originalStageIndex = _currentStageIndex; // 获取当前阶段索引。
var newStageIndex = _currentStageIndex;
// 从后往前遍历阶段,找到当前年龄所属的最高阶段。
for (var i = def.stages.Count - 1; i >= 0; i--)
{
if (def.stages[i].start <= Age)
{
newStageIndex = i;
break;
}
}
// 如果阶段发生变化,则标记为脏。
if (newStageIndex != originalStageIndex)
{
_currentStageIndex = newStageIndex;
// 阶段发生变化,属性偏移可能改变,标记为脏。
SetDirty();
}
}
/// <summary>
/// 当此 Hediff 被添加到实体时调用。
/// 进行初始化设置,如存储父实体引用,并标记属性为脏以强制重新计算。
/// </summary>
/// <param name="entity">此 Hediff 附加到的 LivingEntity 实例。</param>
internal void OnAdded(LivingEntity entity)
{
this.parent = entity; // 存储父实体引用。
SetDirty(); // 刚添加也需要标记为脏以确保属性在下次计算时被纳入。
foreach (var comp in Comps)
{
comp.OnAdded();
}
}
/// <summary>
/// 当此 Hediff 从实体移除时调用。
/// 进行清理操作,如清除父实体引用,并标记属性为脏以强制重新计算。
/// </summary>
/// <param name="entity">此 Hediff 从中移除的 LivingEntity 实例。</param>
internal void OnRemoved(LivingEntity entity)
{
foreach (var comp in Comps)
{
comp.OnRemoved();
}
this.parent = null; // 清除对父实体的引用。
SetDirty(); // 移除后也需要标记为脏确保属性计算考虑去除此Hediff。
}
}
}