using System; using Data; using Entity; using Newtonsoft.Json; using UnityEngine; // 假设您使用 Unity 的 Debug.LogError namespace HediffComps { // 假设 IAttributesOffsetProvider 接口已定义 /// /// 健康状态组件:动态监控实体属性,并根据属性差值、当前值、占比等, /// 施加额外的累积或非累积的属性偏移。 /// public class HediffComp_DynamicAttributeOffset : HediffComp, IAttributesOffsetProvider { /// /// 可监控的属性类型。 /// public enum MonitoredAttributeType { None, Health, MoveSpeed, Attack, Defense, AttackSpeed, AttackRange, AttackTargetCount } /// /// 属性监控模式。 /// public enum MonitorMode { None, /// 监控当前值与基准值的绝对差值 (Base - Current)。 Difference, /// 监控当前属性的绝对值。 CurrentValue, /// 监控当前值占基准值的剩余百分比 (Current / Base)。 RemainingRatio, /// 监控相对于基准值减少的百分比 ((Base - Current) / Base)。 DecreasedRatio, /// 监控相对于基准值增加的百分比 ((Current - Base) / Base)。 IncreasedRatio } private PropertiesConfig config; private AttributesOffsetDef currentDynamicOffset = new(); // 当前组件贡献的动态偏移量 private int lastAppliedStackCount; // 上次应用时的效果堆叠次数 private float lastMonitoredValue; // 上次监控时的属性值 (实际值或计算后的值) private float timeSinceLastTick; /// /// 获取此提供者当前的属性偏移量定义。 /// /// 此提供者所带来的属性偏移量。 public AttributesOffsetDef GetAttributesOffset() { // 直接返回由 MonitorAndApplyOffset 方法维护的最新动态偏移量 return currentDynamicOffset; } /// /// 辅助方法:从 Attributes 对象中获取指定属性的值。 /// private float GetAttributeValue(Attributes attributes, MonitoredAttributeType attributeType) { if (attributes == null) return 0f; switch (attributeType) { case MonitoredAttributeType.Health: return attributes.health; case MonitoredAttributeType.MoveSpeed: return attributes.moveSpeed; case MonitoredAttributeType.Attack: return attributes.attack; case MonitoredAttributeType.Defense: return attributes.defense; case MonitoredAttributeType.AttackSpeed: return attributes.attackSpeed; case MonitoredAttributeType.AttackRange: return attributes.attackRange; case MonitoredAttributeType.AttackTargetCount: return attributes.attackTargetCount; default: // 对于未知的或 MonitoredAttributeType.None 类型,记录警告并返回0 Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): 尝试获取不支持的监控属性类型值: {attributeType}。返回0。"); return 0f; } } /// /// 辅助方法:获取当前监控属性的现在值和基础值。 /// private void GetCurrentMonitoredValues(out float currentValue, out float baseValue) { currentValue = 0f; baseValue = 0f; if (parentHediff?.entity == null) { Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): 属性监控期间父实体为空。"); return; } if (parentHediff.entity.AttributesNow == null) { Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): 实体当前属性为空。"); return; } if (parentHediff.entity.BaseAttributes == null) { Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): 实体基础属性为空。"); return; } currentValue = GetAttributeValue(parentHediff.entity.AttributesNow, config.MonitoredAttribute); baseValue = GetAttributeValue(parentHediff.entity.BaseAttributes, config.MonitoredAttribute); } /// /// 组件初始化时调用。解析配置并进行校验。 /// public override void Initialize(Hediff parentHediff, HediffCompDef def) { base.Initialize(parentHediff, def); if (string.IsNullOrEmpty(def.properties)) throw new ArgumentNullException(nameof(def.properties), $"错误:HediffComp_DynamicAttributeOffset ({def.defName}) 需要在 HediffCompDef.properties 中配置。"); try { config = JsonConvert.DeserializeObject(def.properties); } catch (JsonException ex) { throw new Exception( $"错误:HediffComp_DynamicAttributeOffset ({def.defName}) 从 JSON 反序列化属性失败:'{def.properties}'。错误信息:{ex.Message}", ex); } if (config == null) throw new InvalidOperationException( $"错误:HediffComp_DynamicAttributeOffset ({def.defName}) 的 PropertiesConfig 反序列化结果为空。输入 JSON:'{def.properties}'。"); // --- 配置校验 --- if (config.MonitoredAttribute == MonitoredAttributeType.None) throw new InvalidOperationException( $"错误:HediffComp_DynamicAttributeOffset ({def.defName}): 'monitoredAttribute' 必须指定且不能为 {MonitoredAttributeType.None}。"); if (config.MonitorMode == MonitorMode.None) throw new InvalidOperationException( $"错误:HediffComp_DynamicAttributeOffset ({def.defName}): 'monitorMode' 必须指定且不能为 {MonitorMode.None}。"); if (config.Threshold <= 0f && (config.MonitorMode == MonitorMode.Difference || config.MonitorMode == MonitorMode.DecreasedRatio || config.MonitorMode == MonitorMode.IncreasedRatio)) // 对于这些模式,阈值应为正值,表示变化量。 // 如果是RemainingRatio,Threshold可以是0到1的百分比。 Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): 'MonitorMode' ({config.MonitorMode}) 通常期望正阈值,但当前阈值为零或负值。这可能导致意外行为。"); if (config.OffsetRule == null) throw new InvalidOperationException( $"错误:HediffComp_DynamicAttributeOffset ({def.defName}): 属性中必须提供 'offsetRule'。"); if (config.TickInterval <= 0f) { Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): 'tickInterval' 为零或负值。已设置为默认值 0.5 秒。"); config.TickInterval = 0.5f; } currentDynamicOffset = new AttributesOffsetDef(); // 初始化为空偏移量 timeSinceLastTick = 0f; lastAppliedStackCount = 0; // 获取初始监控值,用于后续差异计算 GetCurrentMonitoredValues(out lastMonitoredValue, out _); } /// /// 当其父 Hediff 被添加到实体时调用。 /// public override void OnAdded() { base.OnAdded(); if (parentHediff?.entity == null) return; // 安全检查 MonitorAndApplyOffset(); // 首次评估并应用 parentHediff.entity.SetAttribsDirty(); // 通知实体属性可能已更改 } /// /// 当其父 Hediff 从实体移除时调用。 /// public override void OnRemoved() { base.OnRemoved(); if (parentHediff?.entity == null) return; // 安全检查 // 在移除时,强制清零偏移量,并通知实体 var changed = lastAppliedStackCount != 0; // 检查是否有应用的效果 if (changed) { // 创建一个全新的 AttributesOffsetDef 作为零值,避免修改currentDynamicOffset原有实例 currentDynamicOffset = new AttributesOffsetDef(); lastAppliedStackCount = 0; parentHediff.entity.SetAttribsDirty(); } } /// /// 每帧更新时调用,根据 TickInterval 周期性地监控和应用偏移。 /// public override void Tick(float deltaTime) { base.Tick(deltaTime); if (parentHediff?.entity == null) return; // 安全检查 timeSinceLastTick += deltaTime; if (timeSinceLastTick >= config.TickInterval) { MonitorAndApplyOffset(); timeSinceLastTick -= config.TickInterval; // 减去而不是直接清零,保持时间累积的精确性 } } /// /// 核心逻辑:监控属性变化,计算并应用动态偏移。 /// private void MonitorAndApplyOffset() { if (parentHediff?.entity == null || parentHediff.entity.AttributesNow == null || parentHediff.entity.BaseAttributes == null) { // 如果实体或其属性无效,且当前有效果,则取消效果 if (lastAppliedStackCount != 0) { currentDynamicOffset = new AttributesOffsetDef(); lastAppliedStackCount = 0; parentHediff?.entity?.SetAttribsDirty(); } return; } GetCurrentMonitoredValues(out var currentValue, out var baseValue); var calculatedValue = 0f; // 特殊处理 baseValue 为 0f 的情况,避免除以零导致 NaN var canCalculateRatio = baseValue != 0f; if (!canCalculateRatio && (config.MonitorMode == MonitorMode.RemainingRatio || config.MonitorMode == MonitorMode.DecreasedRatio || config.MonitorMode == MonitorMode.IncreasedRatio)) { Debug.LogWarning( $"警告:HediffComp_DynamicAttributeOffset ({def.defName}): {config.MonitoredAttribute} 的基础值为0。无法为模式 {config.MonitorMode} 计算比率。未应用偏移。"); calculatedValue = 0f; // 无法计算,视为无意义或无变化 } else { switch (config.MonitorMode) { case MonitorMode.Difference: calculatedValue = baseValue - currentValue; break; case MonitorMode.CurrentValue: calculatedValue = currentValue; break; case MonitorMode.RemainingRatio: calculatedValue = currentValue / baseValue; break; case MonitorMode.DecreasedRatio: calculatedValue = (baseValue - currentValue) / baseValue; break; case MonitorMode.IncreasedRatio: calculatedValue = (currentValue - baseValue) / baseValue; break; default: Debug.LogError( $"错误:HediffComp_DynamicAttributeOffset ({def.defName}): 未知或不支持的监控模式: {config.MonitorMode}。"); return; } } var numStacks = 0; // 对于 DecreasedRatio, IncreasedRatio 等,Threshold 是一个变化量 (0.01 表示 1%) // 对于 RemainingRatio,Threshold 是一个目标剩余比例 (0.5 表示 50%) if (config.Threshold > 0f) // 避免除以零 { if (config.Accumulate) { numStacks = (int)(calculatedValue / config.Threshold); } else { // 非累积模式:只要达到阈值就触发1层,否则0层 if (config.MonitorMode == MonitorMode.RemainingRatio) { // 剩余比例模式下,如果当前比例 <= 阈值,则视为满足条件触发1层 if (calculatedValue <= config.Threshold) numStacks = 1; } else // 其他模式,如果计算值 >= 阈值,则视为满足条件触发1层 { if (calculatedValue >= config.Threshold) numStacks = 1; } } } // 确保堆叠数不为负 (例如属性增加时,DecreasedRatio 算出来是负的) numStacks = Math.Max(0, numStacks); if (numStacks != lastAppliedStackCount) { // 只有当效果堆叠数发生变化时才更新和通知 var newOffset = new AttributesOffsetDef(); // 应用偏移规则 (OffsetRule) 乘以堆叠数 if (config.OffsetRule != null) { // 绝对值偏移 newOffset.healthOffset = config.OffsetRule.healthOffset * numStacks; newOffset.moveSpeedOffset = config.OffsetRule.moveSpeedOffset * numStacks; newOffset.attackOffset = config.OffsetRule.attackOffset * numStacks; newOffset.defenseOffset = config.OffsetRule.defenseOffset * numStacks; newOffset.attackSpeedOffset = config.OffsetRule.attackSpeedOffset * numStacks; newOffset.attackRangeOffset = config.OffsetRule.attackRangeOffset * numStacks; newOffset.attackTargetCountOffset = config.OffsetRule.attackTargetCountOffset * numStacks; newOffset.damageTakenOffset = config.OffsetRule.damageTakenOffset * numStacks; newOffset.effectiveDamageOffset = config.OffsetRule.effectiveDamageOffset * numStacks; // 百分比偏移 newOffset.healthPercentOffset = config.OffsetRule.healthPercentOffset * numStacks; newOffset.moveSpeedPercentOffset = config.OffsetRule.moveSpeedPercentOffset * numStacks; newOffset.attackPercentOffset = config.OffsetRule.attackPercentOffset * numStacks; newOffset.defensePercentOffset = config.OffsetRule.defensePercentOffset * numStacks; newOffset.attackSpeedPercentOffset = config.OffsetRule.attackSpeedPercentOffset * numStacks; newOffset.attackRangePercentOffset = config.OffsetRule.attackRangePercentOffset * numStacks; newOffset.attackTargetCountPercentOffset = config.OffsetRule.attackTargetCountPercentOffset * numStacks; newOffset.damageTakenPercentOffset = config.OffsetRule.damageTakenPercentOffset * numStacks; newOffset.effectiveDamagePercentOffset = config.OffsetRule.effectiveDamagePercentOffset * numStacks; } currentDynamicOffset = newOffset; // 更新组件当前的动态偏移 lastAppliedStackCount = numStacks; // 更新已应用的堆叠数 // 通知实体其属性可能已更改 parentHediff.entity.SetAttribsDirty(); } } /// /// 用于反序列化 HediffCompDef.properties JSON 字符串的配置类。 /// private class PropertiesConfig { [JsonProperty("monitoredAttribute")] public MonitoredAttributeType MonitoredAttribute { get; set; } = MonitoredAttributeType.None; [JsonProperty("monitorMode")] public MonitorMode MonitorMode { get; set; } = MonitorMode.None; /// /// 触发规则的阈值。根据 MonitorMode 有不同含义。 /// 例如,MonitorMode=DecreasedRatio 且 Threshold=0.01f 表示每减少1%。 /// [JsonProperty("threshold")] public float Threshold { get; set; } /// /// 每次满足阈值时施加的单个属性偏移定义。 /// [JsonProperty("offsetRule")] public AttributesOffsetDef OffsetRule { get; set; } /// /// 是否累积效果。如果为 true,每满足阈值一次就施加一次 OffsetRule 的效果; /// 如果为 false,只在达到 Threshold 时施加一次 OffsetRule 的效果。 /// [JsonProperty("accumulate")] public bool Accumulate { get; set; } = true; /// /// 监控属性的间隔时间(秒),避免每帧都计算,优化性能。 /// [JsonProperty("tickInterval")] public float TickInterval { get; set; } = 0.5f; // 默认0.5秒更新一次 } } }