using System;
using Data;
using Entity;
using Newtonsoft.Json;
using Parsing;
namespace HediffComps
{
///
/// 健康状态组件:在特定条件下为实体提供属性偏移量。
/// 当条件满足时,该组件会提供预设的 ;否则提供一个所有值为零的空偏移量。
///
public class HediffComp_ApplyAttributesOffset : HediffComp, IAttributesOffsetProvider
{
// 不活跃时返回的空属性偏移量 (所有字段为0)。这是一个只读字段,在构造时已初始化,因此永远不需要空检查。
private readonly AttributesOffsetDef emptyOffset = new();
private AttributesOffsetDef cachedActiveOffset; // 当活跃时返回的实际属性偏移量
private Func condition; // 评估条件的委托
private PropertiesConfig config; // 从 JSON 反序列化得到的配置
private bool isActive; // 当前属性偏移是否活跃(即条件是否满足)
///
/// 获取此提供者当前的属性偏移量定义。
/// 实现 接口。
///
/// 此提供者所带来的属性偏移量。
public AttributesOffsetDef GetAttributesOffset()
{
// 如果组件活跃,则返回实际的属性偏移量;否则返回一个空的(所有值为零)的偏移量。
// cachedActiveOffset 在 Initialize 时已保证非 null,emptyOffset 是 readonly 字段并已初始化,因此不需空检查。
return isActive ? cachedActiveOffset : emptyOffset;
}
///
/// 组件初始化时调用,在构造函数之后。
/// 负责解析 中的 JSON 配置,并创建条件委托。
///
/// 父级 Hediff 实例。
/// 组件定义。
/// 当 为空或 null 时抛出。
/// 当 JSON 反序列化失败时抛出。
/// 当配置对象、条件表达式或偏移定义为 null 时抛出。
/// 当条件委托创建失败时抛出(包装原始异常)。
public override void Initialize(Hediff parentHediff, HediffCompDef def)
{
base.Initialize(parentHediff, def);
// 检查配置 JSON 字符串是否为空或 null
if (string.IsNullOrEmpty(def.properties))
throw new ArgumentNullException(nameof(def.properties),
$"HediffComp_ApplyAttributesOffset ({def.defName}) requires configuration in HediffCompDef.properties.");
// 反序列化配置 JSON 字符串到 PropertiesConfig 对象
try
{
config = JsonConvert.DeserializeObject(def.properties);
}
catch (JsonException ex)
{
// JsonException 是一种特定类型的异常,直接抛出,而不是包装在泛型 Exception 中。
throw new JsonException($"Failed to deserialize HediffComp_ApplyAttributesOffset ({def.defName}) properties " +
$"JSON: '{def.properties}'. Error: {ex.Message}", ex);
}
// 检查反序列化后的配置对象是否为 null
if (config == null)
throw new InvalidOperationException(
$"HediffComp_ApplyAttributesOffset ({def.defName}) PropertiesConfig deserialization resulted in null. " +
$"Input JSON: '{def.properties}'.");
// 检查条件表达式是否为空或 null
if (string.IsNullOrEmpty(config.ConditionExpression))
throw new InvalidOperationException(
$"HediffComp_ApplyAttributesOffset ({def.defName}) requires a 'condition' expression in its properties.");
// 创建条件委托
try
{
condition = ConditionDelegateFactory.CreateConditionDelegate(
config.ConditionExpression,
typeof(Entity.Entity),
typeof(ConditionFunctions)
);
}
catch (Exception ex)
{
// 包装原始异常,提供更多上下文信息。
throw new Exception($"Failed to create condition delegate for HediffComp_ApplyAttributesOffset ({def.defName}) " +
$"expression: '{config.ConditionExpression}'. Error: {ex.Message}", ex);
}
// 检查属性偏移定义是否为 null
if (config.OffsetDef == null)
throw new InvalidOperationException(
$"HediffComp_ApplyAttributesOffset ({def.defName}) requires 'offsetDef' in its properties, but it was null.");
// 缓存实际的偏移量,当组件活跃时使用。此时已保证 config.OffsetDef 非空。
cachedActiveOffset = config.OffsetDef;
}
///
/// 当其父 被添加到实体时调用。
/// 在此进行初始条件评估,并通知实体属性可能已更改。
///
public override void OnAdded()
{
base.OnAdded();
// 在被添加到实体时进行首次条件评估,设置初始 isActive 状态
EvaluateConditionAndSetState();
// 通知实体属性可能已更改,以便其重新计算属性
parentHediff?.entity?.SetAttribsDirty();
}
///
/// 当其父 从实体移除时调用。
/// 确保状态被重置为不活跃,并通知实体属性可能已更改。
///
public override void OnRemoved()
{
base.OnRemoved();
// 确保移除时,如果组件当前活跃,则将状态设置为不活跃,并通知实体属性更新。
// 这样可以消除此 HediffComp 组件移除时带来的属性影响。
if (isActive)
{
isActive = false;
parentHediff?.entity?.SetAttribsDirty();
}
// 重置 cachedActiveOffset 和 condition 等引用,有助于 GC 并在理论上防止组件被缓存后引用旧数据。
cachedActiveOffset = null;
condition = null;
config = null; // 释放对配置的引用
}
///
/// 每帧更新时调用。
/// 在此周期性地评估条件以更新 状态。
///
/// 自上次更新以来的时间(秒)。
public override void Tick(float deltaTime)
{
base.Tick(deltaTime);
EvaluateConditionAndSetState();
}
///
/// 评估当前条件并更新 状态。
/// 如果 状态发生变化,则通知实体属性更新。
///
private void EvaluateConditionAndSetState()
{
// 如果父 Hediff 的实体为空或条件委托未初始化,则无法评估条件。
// 此时应确保偏移量处于非激活状态,并通知属性变化(如果状态有变)。
// condition 已经通过 Initialize 方法保证非 null,除非父 Hediff.entity 为 null,
// 否则这里可以简化。但为了更强的健壮性,保留 condition == null 检查。
if (parentHediff?.entity == null || condition == null)
{
if (isActive) // 如果当前是活跃状态,但现在条件不可评估或实体不存在,需要重置
{
isActive = false;
parentHediff?.entity?.SetAttribsDirty(); // 通知属性脏,以便移除此组件的影响
}
return;
}
// 评估条件委托
var newIsActive = condition(parentHediff.entity);
// 如果 isActive 状态发生变化,则更新状态并通知实体。
if (newIsActive != isActive)
{
isActive = newIsActive;
parentHediff.entity.SetAttribsDirty();
}
}
///
/// 用于反序列化 JSON 字符串的配置类。
///
private class PropertiesConfig
{
///
/// 条件表达式字符串,用于通过 创建条件委托。
/// 例如:"Entity.IsHuman && Entity.HealthRatio < 0.5"
///
[JsonProperty("condition")]
public string ConditionExpression { get; set; }
///
/// 当条件满足时应用的属性偏移定义。
///
[JsonProperty("offsetDef")]
public AttributesOffsetDef OffsetDef { get; set; }
}
}
}