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

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