Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/HediffComps/HediffComp_ExecuteEvent.cs

205 lines
8.4 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 Data;
using Entity;
using Managers;
using Newtonsoft.Json;
using Parsing;
namespace HediffComps
{
/// <summary>
/// 健康状态组件:在冷却时间结束后或满足特定条件时触发一个预定义的事件。
/// </summary>
public class HediffComp_ExecuteEvent : HediffComp
{
// 从 JSON 反序列化得到的配置
private PropertiesConfig config;
// 评估条件是否满足的委托 (如果定义了)
private Func<Entity.Entity, bool> condition;
// 距离下次事件触发或条件评估还剩多少秒
private float remainingCooldown;
// 如果配置为 TriggerOnce记录是否已触发过一次
private bool hasTriggered;
/// <summary>
/// 组件初始化时调用,在构造函数之后。
/// 负责解析 <see cref="HediffCompDef.properties" /> 中的 JSON 配置,并创建条件委托。
/// </summary>
/// <param name="parentHediff">父级 Hediff 实例。</param>
/// <param name="def">组件定义。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="def.properties" /> 为空或 null 时抛出。</exception>
/// <exception cref="JsonException">当 JSON 反序列化失败时抛出。</exception>
/// <exception cref="InvalidOperationException">当配置对象、事件名称为空时抛出。</exception>
/// <exception cref="Exception">当条件委托创建失败时抛出(包装原始异常)。</exception>
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<PropertiesConfig>(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; // 初始状态为未触发
}
/// <summary>
/// 当其父 <see cref="Hediff" /> 被添加到实体时调用。
/// 重置冷却时间和触发状态。
/// </summary>
public override void OnAdded()
{
base.OnAdded();
// 每次添加到实体时,重置冷却时间和触发状态,确保组件可以重新开始工作
remainingCooldown = Math.Max(0f, config.CooldownSeconds);
hasTriggered = false;
}
/// <summary>
/// 当其父 <see cref="Hediff" /> 从实体移除时调用。
/// 清空引用以辅助垃圾回收。
/// </summary>
public override void OnRemoved()
{
base.OnRemoved();
// 释放对配置和委托的引用,以辅助垃圾回收
config = null;
condition = null;
}
/// <summary>
/// 每帧更新时调用。
/// 在此周期性地检查冷却时间和条件,以触发事件。
/// </summary>
/// <param name="deltaTime">自上次更新以来的时间(秒)。</param>
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);
}
}
}
}
/// <summary>
/// 用于反序列化 <see cref="HediffCompDef.properties" /> JSON 字符串的配置类。
/// </summary>
private class PropertiesConfig
{
/// <summary>
/// 要触发的事件名称。
/// </summary>
[JsonProperty("eventName")]
public string EventName { get; set; }
/// <summary>
/// 可选的条件表达式字符串。如果存在,只在条件满足时触发。
/// </summary>
[JsonProperty("condition")]
public string ConditionExpression { get; set; }
/// <summary>
/// 可选的冷却时间。事件触发后需要等待此时间才能再次触发。默认为0。
/// JSON 中未定义时默认为0。
/// </summary>
[JsonProperty("cooldownSeconds")]
public float CooldownSeconds { get; set; } = 0f;
/// <summary>
/// 可选的布尔值。如果为 true事件只触发一次。默认为 false。
/// JSON 中未定义时默认为false。
/// </summary>
[JsonProperty("triggerOnce")]
public bool TriggerOnce { get; set; } = false;
}
}
}