mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 00:47:12 +08:00
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/60
400 lines
18 KiB
C#
400 lines
18 KiB
C#
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))
|
||
// 对于这些模式,阈值应为正值,表示变化量。
|
||
// 如果是RemainingRatio,Threshold可以是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%)
|
||
// 对于 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();
|
||
}
|
||
}
|
||
|
||
/// <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秒更新一次
|
||
}
|
||
}
|
||
} |