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秒更新一次
}
}
}