Files
Cielonos/Assets/Scripts/MainGame/Characters/Automata/AI/Conditionals/StableRandomProbability.cs

107 lines
4.5 KiB
C#
Raw Normal View History

2026-03-20 12:07:44 -04:00
using Opsive.BehaviorDesigner.Runtime.Tasks;
using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals;
using Opsive.GraphDesigner.Runtime;
using Opsive.GraphDesigner.Runtime.Variables;
using Opsive.Shared.Utility;
using SLSUtilities.General;
using UnityEngine;
namespace Cielonos.MainGame.Characters.AI
{
[Description("使用伪随机分布PRD/稳定概率)进行掷骰判定。连续判定失败会提升下次触发几率,判定成功后重置。用于防脸黑与连爆,制造更为平滑均匀的“伪随机”发生感。")]
[NodeIcon("69bf50f8923f54c4c8bb8e258883a411", "6c5770241610a4c4aae4ac3af0ac8bf8")]
[Category("Cielonos")]
public class StableRandomProbability : Conditional
{
[Tooltip("当前节点期望判定的成功概率(0~1)。")]
public SharedVariable<float> successProbability = 0.25f;
[Tooltip("是否启用跨节点共享概率机制。启用后,多个绑定了同一 SharedStableProbability 变量的 StableRandomProbability 节点将共享同一个 N 计数(但可独立设定各自的目标概率 P。如果不启用则每个节点独立追踪自己的 N 计数。")]
public bool useSharedProbability = false;
[Tooltip("【可选】跨节点共享状态槽。如果需要多个不同的判定节点连续3次不同的普攻共享同一个失败累计次数 N请绑定同一个 SharedStableProbability 变量")]
public SharedVariable<StableProbability> sharedStableProbability;
[Tooltip("冷却时间(秒)。在此期间内如果节点被每帧重复求值(由于条件打断 Lower Priority/Both将直接返回上一次的判定结果不会进行新的掷骰。配置为 0 则不使用冷却。")]
public SharedVariable<float> rollCooldown = 0.5f;
private float _lastRollTime = -999f;
private bool _lastResult = false;
private StableProbability _internalProbability;
public override void OnAwake()
{
// 如果用户没有绑定共享变量,或者绑定了但由于某种原因无法引用
if (!useSharedProbability)
{
// 创建此节点私有的稳定概率对象
_internalProbability = new StableProbability(successProbability.Value);
}
else
{
// 如果绑定了共享变量,但在整棵树第一次唤醒时尚未初始化其值实例,则由此节点代为创建
if (sharedStableProbability.Value == null)
{
sharedStableProbability.Value = new StableProbability(successProbability.Value);
}
}
}
public override TaskStatus OnUpdate()
{
if (rollCooldown != null && rollCooldown.Value > 0f)
{
if (Time.time - _lastRollTime < rollCooldown.Value)
{
return _lastResult ? TaskStatus.Success : TaskStatus.Failure;
}
}
StableProbability prdInstance = null;
if (useSharedProbability && sharedStableProbability.Value != null)
{
prdInstance = sharedStableProbability.Value;
// 实时更新外接共享实例的期望概率 (防止外部在游戏运行中用其他 Action 更改了目标百分比)
prdInstance.Probability = successProbability.Value;
}
else
{
prdInstance = _internalProbability;
if (prdInstance != null)
{
prdInstance.Probability = successProbability.Value;
}
}
if (prdInstance == null)
{
return TaskStatus.Failure;
}
// 执行 Roll 判定并自动增加 N 计数 / 重置 N 计数
Debug.Log($"当前实际概率: {prdInstance.C * prdInstance.N:F2}");
bool isSuccess = prdInstance.Roll();
_lastResult = isSuccess;
_lastRollTime = Time.time;
return isSuccess ? TaskStatus.Success : TaskStatus.Failure;
}
public override void Reset()
{
base.Reset();
successProbability = 0.25f;
rollCooldown = 0.5f;
_lastRollTime = -999f;
_lastResult = false;
useSharedProbability = false;
sharedStableProbability = null;
if (_internalProbability != null)
{
_internalProbability.Reset();
}
}
}
}