using System; using Data; using Entity; using Managers; using Newtonsoft.Json; using Parsing; namespace HediffComps { /// /// 健康状态组件:在冷却时间结束后或满足特定条件时触发一个预定义的事件。 /// public class HediffComp_ExecuteEvent : HediffComp { // 从 JSON 反序列化得到的配置 private PropertiesConfig config; // 评估条件是否满足的委托 (如果定义了) private Func condition; // 距离下次事件触发或条件评估还剩多少秒 private float remainingCooldown; // 如果配置为 TriggerOnce,记录是否已触发过一次 private bool hasTriggered; /// /// 组件初始化时调用,在构造函数之后。 /// 负责解析 中的 JSON 配置,并创建条件委托。 /// /// 父级 Hediff 实例。 /// 组件定义。 /// 为空或 null 时抛出。 /// 当 JSON 反序列化失败时抛出。 /// 当配置对象、事件名称为空时抛出。 /// 当条件委托创建失败时抛出(包装原始异常)。 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_ExecuteEvent ({def.defName}) requires configuration in HediffCompDef.properties."); // 反序列化配置 JSON 字符串到 PropertiesConfig 对象 try { config = JsonConvert.DeserializeObject(def.properties); } catch (JsonException ex) { throw new JsonException($"Failed to deserialize HediffComp_ExecuteEvent ({def.defName}) properties " + $"JSON: '{def.properties}'. Error: {ex.Message}", ex); } // 检查反序列化后的配置对象是否为 null if (config == null) throw new InvalidOperationException( $"HediffComp_ExecuteEvent ({def.defName}) PropertiesConfig deserialization resulted in null. " + $"Input JSON: '{def.properties}'."); // 检查事件名称是否为空或 null if (string.IsNullOrEmpty(config.EventName)) throw new InvalidOperationException( $"HediffComp_ExecuteEvent ({def.defName}) requires an 'eventName' in its properties."); // 如果定义了条件表达式,则创建条件委托 if (!string.IsNullOrEmpty(config.ConditionExpression)) { try { condition = ConditionDelegateFactory.CreateConditionDelegate( config.ConditionExpression, typeof(Entity.Entity), typeof(ConditionFunctions) ); } catch (Exception ex) { throw new Exception($"Failed to create condition delegate for HediffComp_ExecuteEvent ({def.defName}) " + $"expression: '{config.ConditionExpression}'. Error: {ex.Message}", ex); } } // 初始化冷却时间。如果 CooldownSeconds 小于0,则视为0。 remainingCooldown = Math.Max(0f, config.CooldownSeconds); hasTriggered = false; // 初始状态为未触发 } /// /// 当其父 被添加到实体时调用。 /// 重置冷却时间和触发状态。 /// public override void OnAdded() { base.OnAdded(); // 每次添加到实体时,重置冷却时间和触发状态,确保组件可以重新开始工作 remainingCooldown = Math.Max(0f, config.CooldownSeconds); hasTriggered = false; } /// /// 当其父 从实体移除时调用。 /// 清空引用以辅助垃圾回收。 /// public override void OnRemoved() { base.OnRemoved(); // 释放对配置和委托的引用,以辅助垃圾回收 config = null; condition = null; } /// /// 每帧更新时调用。 /// 在此周期性地检查冷却时间和条件,以触发事件。 /// /// 自上次更新以来的时间(秒)。 public override void Tick(float deltaTime) { base.Tick(deltaTime); // 如果组件设置为只触发一次且已触发,则不再进行任何操作。 if (config.TriggerOnce && hasTriggered) { return; } // 如果实体不存在,则无法触发事件或评估条件 if (parentHediff?.entity == null) { return; } // 递减冷却时间 remainingCooldown -= deltaTime; // 冷却时间结束后,检查是否可以触发事件 if (remainingCooldown <= 0) { // 默认条件满足,除非有条件委托且评估失败 bool conditionMet = true; // 如果存在条件委托,则评估条件 if (condition != null) { conditionMet = condition(parentHediff.entity); } if (conditionMet) { // 触发事件 EventManager.Instance.Action(config.EventName, parentHediff.entity); hasTriggered = true; // 标记已触发 // 如果不是只触发一次,则重置冷却时间以便下次触发 if (!config.TriggerOnce) { remainingCooldown = Math.Max(0f, config.CooldownSeconds); } } else { // 如果冷却结束但条件不满足,并且是非一次性触发,重置冷却时间以便下次尝试 // 仅当 CooldownSeconds > 0 时才重置,避免0冷却无限Tick评估条件导致性能问题 if (!config.TriggerOnce && config.CooldownSeconds > 0) { remainingCooldown = Math.Max(0f, config.CooldownSeconds); } } } } /// /// 用于反序列化 JSON 字符串的配置类。 /// private class PropertiesConfig { /// /// 要触发的事件名称。 /// [JsonProperty("eventName")] public string EventName { get; set; } /// /// 可选的条件表达式字符串。如果存在,只在条件满足时触发。 /// [JsonProperty("condition")] public string ConditionExpression { get; set; } /// /// 可选的冷却时间(秒)。事件触发后需要等待此时间才能再次触发。默认为0。 /// JSON 中未定义时默认为0。 /// [JsonProperty("cooldownSeconds")] public float CooldownSeconds { get; set; } = 0f; /// /// 可选的布尔值。如果为 true,事件只触发一次。默认为 false。 /// JSON 中未定义时默认为false。 /// [JsonProperty("triggerOnce")] public bool TriggerOnce { get; set; } = false; } } }